Fixes broken shift sticky translation (regression) (NB#164592)
[fremantle-hildon-desktop:hildon-input-method-framework.git] / src / hildon-im-context.c
1 /**
2    @file: hildon-im-context.c
3
4  */
5 /*
6  * This file is part of hildon-input-method-framework
7  *
8  * Copyright (C) 2005-2007 Nokia Corporation.
9  *
10  * Contact: Mohammad Anwari <Mohammad.Anwari@nokia.com>
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public License
14  * version 2.1 as published by the Free Software Foundation.
15  *
16  * This library is distributed in the hope that it will be useful, but
17  * WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
24  * 02110-1301 USA
25  *
26  */
27
28 #include <string.h>
29 #include <libintl.h>
30 #include <gtk/gtk.h>
31 #include <gdk/gdkkeysyms.h>
32 #include <gdk/gdkx.h>
33 #include <pango/pango.h>
34 #include <cairo/cairo.h>
35 #include <X11/Xatom.h>
36 #include <X11/Xlib.h>
37 #include <X11/extensions/XTest.h>
38 #include "hildon-im-context.h"
39 #include "hildon-im-gtk.h"
40 #include "hildon-im-common.h"
41
42 #define HILDON_IM_DEFAULT_LAUNCH_DELAY 70
43
44 #define COMPOSE_KEY GDK_Multi_key
45 #define LEVEL_KEY GDK_ISO_Level3_Shift
46 #define LEVEL_KEY_MOD_MASK GDK_MOD5_MASK
47
48 #define BASE_LEVEL     0
49 #define NUMERIC_LEVEL  2
50 #define LOCKABLE_LEVEL 4
51
52 /* if we don't use gtk_im_context_get_surrounding or a text buffer to get the
53  * surrounding, this is the fixed size that will be used */
54 #define SURROUNDING_CHARS_BEFORE_CURSOR 48
55 #define SURROUNDING_CHARS_AFTER_CURSOR  16
56
57 /* Maximum distance that can be dragged in order to show the IM */
58 #define SHOW_CONTEXT_MAX_DISTANCE 25
59
60 /* Special cased widgets */
61 #define HILDON_IM_INTERNAL_TEXTVIEW "him-textview"
62 #define HILDON_ENTRY_COMPLETION_POPUP "hildon-completion-window"
63 #define MAEMO_BROWSER_URL_ENTRY "maemo-browser-url-entry"
64
65 /* Long-press feature */
66 #define DEFAULT_LONG_PRESS_TIMEOUT 600
67
68 static GtkIMContextClass *parent_class;
69 static GType im_context_type = 0;
70
71 static gulong grab_focus_hook_id = 0;
72 static gulong unmap_hook_id = 0;
73 static guint launch_delay_timeout_id = 0;
74 static guint launch_delay = HILDON_IM_DEFAULT_LAUNCH_DELAY;
75 static HildonIMTrigger trigger = HILDON_IM_TRIGGER_UNKNOWN;
76 static gboolean internal_reset = FALSE;
77 static gboolean enter_on_focus_pending = FALSE;
78
79 typedef struct _HildonIMContext HildonIMContext;
80
81 struct _HildonIMContext
82 {
83   GtkIMContext context;
84
85   HildonIMInternalModifierMask mask;
86   HildonIMOptionMask options;
87
88   GdkWindow *client_gdk_window;
89   GtkWidget *client_gtk_widget;
90   gboolean is_internal_widget;
91   
92   Window im_window;
93
94   HildonIMCommitMode commit_mode;
95   HildonIMCommitMode previous_commit_mode;
96
97   GString *preedit_buffer;
98   /* we need the incoming preedit buffer because the message might be split */
99   GString *incoming_preedit_buffer;
100   /* keep the preedit's position on GtkTextView or GtkEditable */
101   GtkTextMark *text_view_preedit_mark;
102   gint editable_preedit_position;
103   /* in case we want to hide the preedit buffer without canceling it */
104   gboolean show_preedit;
105   /* append a space after the text is committed */
106   gboolean space_after_commit;
107
108   /* IDs of handlers attached to client widget */
109   gint client_changed_signal_handler;
110   gint client_hide_signal_handler;
111   gint client_copy_clipboard_signal_handler;
112
113   /* State */
114   gboolean last_internal_change;
115   gint changed_count;
116   GdkEventKey *last_key_event;
117   guint32 combining_char;
118   gboolean auto_upper;
119   gboolean auto_upper_enabled;
120   gboolean has_focus;
121   gboolean is_url_entry;
122   gboolean committed_preedit;
123
124   /* Keep track on cursor position to prevent unnecessary calls */
125   gint prev_cursor_x;
126   gint prev_cursor_y;
127
128   gchar *surrounding;
129   guint  prev_surrounding_hash;
130   guint  prev_surrounding_cursor_pos;
131
132   gdouble button_press_x;
133   gdouble button_press_y;
134
135   /* used to implement long-press feature */
136   gboolean enable_long_press;
137   gboolean char_key_is_down;
138   guint long_press_timeout_src_id;
139   guint long_press_timeout;
140   GdkEventKey *long_press_last_key_event;
141
142   gboolean last_was_shift_backspace;
143 };
144
145 /* Initialisation/finalisation functions */
146 static void       hildon_im_context_init                (HildonIMContext*
147                                                          self);
148
149 static void       hildon_im_context_class_init          (HildonIMContextClass*
150                                                          im_context_class);
151 static void       hildon_im_context_class_finalize      (HildonIMContextClass*
152                                                          im_context_class);
153
154 /* Virtual functions */
155 static void       hildon_im_context_focus_in            (GtkIMContext*
156                                                          context);
157 static void       hildon_im_context_focus_out           (GtkIMContext*
158                                                          context);
159 static void       hildon_im_context_hide                (GtkIMContext *context);
160 #ifdef MAEMO_CHANGES
161 static void       hildon_im_context_show                (GtkIMContext *context);
162 #endif
163 static gboolean   hildon_im_context_filter_event        (GtkIMContext *context,
164                                                          GdkEvent     *event);
165
166 static void       hildon_im_context_reset               (GtkIMContext *context);
167
168 static gboolean   hildon_im_context_get_surrounding     (GtkIMContext *context,
169                                                          gchar **text,
170                                                          gint *cursor_index);
171
172 static void       hildon_im_context_set_client_window   (GtkIMContext *context,
173                                                          GdkWindow *window);
174
175 static gboolean   hildon_im_context_filter_keypress     (GtkIMContext *context,
176                                                          GdkEventKey *event);
177
178
179 static void       hildon_im_context_set_cursor_location (GtkIMContext *context,
180                                                          GdkRectangle *area);
181
182 /* Private functions */
183 static void       hildon_im_context_show_real           (GtkIMContext *context);
184 static void       hildon_im_context_reset_real          (GtkIMContext *context);
185 static void       hildon_im_context_insert_utf8         (HildonIMContext *self,
186                                                          gint flag,
187                                                          const char *text);
188 static void       hildon_im_context_send_input_mode     (HildonIMContext *self);
189 static void       hildon_im_context_send_command        (HildonIMContext *self,
190                                                          HildonIMCommand cmd);
191 static void       hildon_im_context_check_sentence_start(HildonIMContext*
192                                                          self);
193 static void       hildon_im_context_send_surrounding    (HildonIMContext*
194                                                          self,
195                                                          gboolean
196                                                          send_full_line);
197 static void       hildon_im_context_send_committed_preedit(HildonIMContext *self,
198                                                            gchar* committed_preedit);
199 static void       hildon_im_context_send_key_event      (HildonIMContext *self,
200                                                          GdkEventType type,
201                                                          guint state,
202                                                          guint keyval,
203                                                          guint16 hardware_keycode);
204 static void       hildon_im_context_commit_surrounding  (HildonIMContext*
205                                                          self);
206 static void       hildon_im_context_get_preedit_string  (GtkIMContext *context,
207                                                          gchar **str,
208                                                          PangoAttrList **attrs,
209                                                          gint *cursor_pos);
210 static void       hildon_im_context_set_use_preedit     (GtkIMContext *context,
211                                                          gboolean use_preedit);
212
213 /* Useful functions */
214 static Window          get_window_id                    (Atom window_atom);
215
216 static GdkFilterReturn client_message_filter            (GdkXEvent *xevent,
217                                                          GdkEvent *event,
218                                                          HildonIMContext *self);
219 /* this takes care of notifying changes in the buffer */
220 static void         set_preedit_buffer                  (HildonIMContext *self,
221                                                          const gchar* s);
222 /* commits text */
223 static gboolean     commit_text                         (HildonIMContext *self,
224                                                          const gchar* s);
225
226
227 static void hildon_im_context_set_mask_state (HildonIMContext *self,
228                                               HildonIMInternalModifierMask *mask,
229                                               HildonIMInternalModifierMask lock_mask,
230                                               HildonIMInternalModifierMask sticky_mask,
231                                               gboolean was_press_and_release);
232
233 static void hildon_im_context_send_event(HildonIMContext *self, XEvent *event);
234
235 static gboolean key_pressed (HildonIMContext *context, GdkEventKey *event);
236
237 static void hildon_im_context_abort_long_press (HildonIMContext *context);
238
239 static void
240 hildon_im_context_send_fake_key (guint key_val, gboolean is_press)
241 {
242   GdkKeymapKey *keys=NULL;
243   gint n_keys=0;
244
245   if(gdk_keymap_get_entries_for_keyval (NULL, key_val, &keys, &n_keys))
246   {
247     XTestFakeKeyEvent(GDK_DISPLAY(),keys->keycode, is_press, 0);
248   }
249   else
250   {
251     g_warning("Keycode not found for keyval %x", key_val);
252   }
253
254   g_free(keys);
255 }
256
257 void
258 hildon_im_context_register_type (GTypeModule *module )
259 {
260   if (!im_context_type)
261   {
262     static const GTypeInfo im_context_info =
263     {
264       sizeof(HildonIMContextClass),
265       NULL, /* base_init */
266       NULL, /* base_finalize */
267       (GClassInitFunc) hildon_im_context_class_init,
268       (GClassInitFunc) hildon_im_context_class_finalize,
269       NULL, /* class_data */
270       sizeof ( HildonIMContext ),
271       0,    /* n_preallocs */
272       (GInstanceInitFunc) hildon_im_context_init,
273     };
274     im_context_type = g_type_module_register_type(module, GTK_TYPE_IM_CONTEXT,
275                                                   "HildonIMContext",
276                                                   &im_context_info, 0);
277   }
278 }
279
280 static void
281 hildon_im_context_finalize(GObject *obj)
282 {
283   HildonIMContext *imc = HILDON_IM_CONTEXT(obj);
284
285   hildon_im_context_set_client_window(GTK_IM_CONTEXT(imc), NULL);
286   g_free(imc->surrounding);
287
288   if (imc->last_key_event)
289   {
290     gdk_event_free((GdkEvent*)imc->last_key_event);
291   }
292
293   if (launch_delay_timeout_id != 0)
294   {
295     g_source_remove(launch_delay_timeout_id);
296     launch_delay_timeout_id = 0;
297   }
298
299   g_string_free (imc->preedit_buffer, TRUE);
300   g_string_free (imc->incoming_preedit_buffer, TRUE);
301
302   if (imc->long_press_last_key_event != NULL)
303   {
304     gdk_event_free ((GdkEvent *) imc->long_press_last_key_event);
305     imc->long_press_last_key_event = NULL;
306   }
307
308   G_OBJECT_CLASS(parent_class)->finalize(obj);
309 }
310
311 static GtkTextBuffer *
312 get_buffer(GtkWidget *widget)
313 {
314   if (GTK_IS_TEXT_VIEW (widget))
315   {
316     return gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
317   }
318
319   return NULL;
320 }
321
322 static gboolean
323 hildon_im_hook_grab_focus_handler(GSignalInvocationHint *ihint,
324                                   guint n_param_values,
325                                   const GValue *param_values,
326                                   gpointer data)
327 {
328   GtkIMContext *context = NULL;
329   GtkWidget *focus_widget, *old_focus_widget, *toplevel;
330
331   g_return_val_if_fail(n_param_values > 0, TRUE);
332
333   /* Widgets created by the IM have valid contexts, but focusing them
334      should not cause any action 
335   if (is_internal_widget)
336     return TRUE;
337      */
338
339   focus_widget = g_value_get_object(&param_values[0]);
340
341   if (!GTK_WIDGET_VISIBLE (focus_widget))
342     return TRUE;
343
344   toplevel = gtk_widget_get_toplevel (focus_widget);
345   if (!GTK_WIDGET_TOPLEVEL (toplevel) ||
346       !GTK_IS_WINDOW(toplevel))
347   {
348     /* No parent toplevel */
349     return TRUE;
350   }
351
352   old_focus_widget = gtk_window_get_focus(GTK_WINDOW(toplevel));
353   if (old_focus_widget == focus_widget)
354   {
355     /* Already focused there */
356     return TRUE;
357   }
358
359   if (old_focus_widget == NULL ||
360       !GTK_WIDGET_HAS_FOCUS(old_focus_widget))
361   {
362     return TRUE;
363   }
364
365   if (GTK_IS_ENTRY (old_focus_widget))
366     context = GTK_ENTRY (old_focus_widget)->im_context;
367   else if (GTK_IS_TEXT_VIEW (old_focus_widget))
368     context = GTK_TEXT_VIEW (old_focus_widget)->im_context;
369
370   if (focus_widget)
371   {
372     gboolean is_combo_box_entry, is_inside_toolbar;
373     gboolean is_editable_entry, is_editable_text_view;
374     gboolean is_inside_completion_popup;
375     gboolean allow_deselect;
376     GtkWidget *parent;
377
378     parent = gtk_widget_get_parent (focus_widget);
379     is_combo_box_entry = GTK_IS_COMBO_BOX_ENTRY(focus_widget);
380     is_inside_toolbar =
381       gtk_widget_get_ancestor (focus_widget, GTK_TYPE_TOOLBAR) != NULL;
382     is_editable_entry = GTK_IS_ENTRY (focus_widget) &&
383       gtk_editable_get_editable (GTK_EDITABLE(focus_widget));
384     is_editable_text_view = GTK_IS_TEXT_VIEW (focus_widget) &&
385       gtk_text_view_get_editable (GTK_TEXT_VIEW (focus_widget));
386     is_inside_completion_popup =
387       g_strcmp0(gtk_widget_get_name(toplevel), HILDON_ENTRY_COMPLETION_POPUP) == 0;
388
389     if (focus_widget == NULL ||
390         (!is_editable_entry &&
391          !is_editable_text_view &&
392          !GTK_IS_SCROLLBAR (focus_widget) &&
393          !GTK_IS_MENU_ITEM (focus_widget) &&
394          !GTK_IS_MENU (focus_widget) &&
395          !is_inside_toolbar &&
396          !is_combo_box_entry &&
397          !is_inside_completion_popup))
398     {
399       if (context != NULL && HILDON_IS_IM_CONTEXT (context))
400       {
401         hildon_im_context_hide(context);
402       }
403     }
404
405     if (is_inside_toolbar && context)
406     {
407       internal_reset = TRUE;
408       gtk_im_context_reset(context);
409     }
410
411     /* Remove text highlight (selection) unless focus is moved
412        inside a toolbar, scrollbar or combo */
413     allow_deselect = (!is_inside_toolbar &&
414                       !is_combo_box_entry &&
415                       !GTK_IS_SCROLLBAR (focus_widget));
416
417     if (allow_deselect)
418     {
419       GtkWidget *selection_toplevel = gtk_widget_get_toplevel(old_focus_widget);
420
421       if (!GTK_WIDGET_TOPLEVEL(selection_toplevel) ||
422           selection_toplevel != toplevel)
423         return TRUE;
424
425       if (GTK_IS_ENTRY (old_focus_widget) &&
426           gtk_editable_get_selection_bounds (GTK_EDITABLE (old_focus_widget), NULL, NULL))
427       {
428         gint pos;
429         pos = gtk_editable_get_position (GTK_EDITABLE (old_focus_widget));
430         gtk_editable_set_position (GTK_EDITABLE (old_focus_widget), pos);
431       }
432       else if (GTK_IS_TEXT_VIEW (old_focus_widget))
433       {
434         GtkTextBuffer *text_buff = get_buffer (old_focus_widget);
435
436         if (gtk_text_buffer_get_selection_bounds (text_buff, NULL, NULL))
437         {
438           GtkTextIter insert;
439           gtk_text_buffer_get_iter_at_mark (text_buff, &insert,
440             gtk_text_buffer_get_insert (text_buff));
441           gtk_text_buffer_place_cursor (text_buff, &insert);
442         }
443       }
444     }
445   }
446
447   return TRUE;
448 }
449
450 static gboolean
451 hildon_im_hook_unmap_handler(GSignalInvocationHint *ihint,
452                              guint n_param_values,
453                              const GValue *param_values,
454                              gpointer data)
455 {
456   GtkWidget *widget;
457
458   g_return_val_if_fail(n_param_values > 0, TRUE);
459
460   widget = g_value_get_object(&param_values[0]);
461
462   /* If the IM is opened for this widget, hide the IM */
463   if (GTK_WIDGET_HAS_FOCUS(widget))
464   {
465     GtkIMContext *context = NULL;
466
467     if (GTK_IS_ENTRY (widget))
468       context = GTK_ENTRY (widget)->im_context;
469     else if (GTK_IS_TEXT_VIEW (widget))
470       context = GTK_TEXT_VIEW (widget)->im_context;
471     
472     if (HILDON_IS_IM_CONTEXT (context))
473     {
474       hildon_im_context_hide(context);
475     }
476     else if (GTK_IS_IM_CONTEXT (context))
477     {
478       gtk_im_context_hide (context);
479     }
480   }
481
482   return TRUE;
483 }
484
485 static void
486 hildon_im_context_class_init (HildonIMContextClass *im_context_class)
487 {
488   gint signal_id;
489
490   GObjectClass *object_class = G_OBJECT_CLASS(im_context_class);
491   GtkIMContextClass *gtk_im_context_class = GTK_IM_CONTEXT_CLASS(im_context_class);
492
493   parent_class = g_type_class_peek_parent( im_context_class );
494
495   object_class->finalize = hildon_im_context_finalize;
496
497   /* Virtual functions */
498   gtk_im_context_class->focus_in = hildon_im_context_focus_in;
499   gtk_im_context_class->focus_out = hildon_im_context_focus_out;
500 #ifdef MAEMO_CHANGES
501   gtk_im_context_class->show = hildon_im_context_show;
502   gtk_im_context_class->hide = hildon_im_context_hide;
503   gtk_im_context_class->filter_event = hildon_im_context_filter_event;
504 #endif
505   gtk_im_context_class->set_client_window = hildon_im_context_set_client_window;
506   gtk_im_context_class->filter_keypress = hildon_im_context_filter_keypress;
507   gtk_im_context_class->set_cursor_location = hildon_im_context_set_cursor_location;
508   gtk_im_context_class->reset = hildon_im_context_reset;
509   gtk_im_context_class->get_surrounding = hildon_im_context_get_surrounding;
510   gtk_im_context_class->get_preedit_string = hildon_im_context_get_preedit_string;
511   gtk_im_context_class->set_use_preedit = hildon_im_context_set_use_preedit;
512
513   signal_id = g_signal_lookup("grab-focus", GTK_TYPE_WIDGET);
514   grab_focus_hook_id =
515     g_signal_add_emission_hook(signal_id, 0, hildon_im_hook_grab_focus_handler,
516                                NULL, NULL);
517
518   signal_id = g_signal_lookup("unmap-event", GTK_TYPE_WIDGET);
519   unmap_hook_id =
520     g_signal_add_emission_hook(signal_id, 0, hildon_im_hook_unmap_handler,
521                                NULL, NULL);
522 }
523
524 static void
525 hildon_im_context_class_finalize(HildonIMContextClass *im_context_class)
526 {
527   gint signal_id;
528
529   signal_id = g_signal_lookup("grab-focus", GTK_TYPE_WIDGET);
530   g_signal_remove_emission_hook(signal_id, grab_focus_hook_id);
531
532   signal_id = g_signal_lookup("unmap-event", GTK_TYPE_WIDGET);
533   g_signal_remove_emission_hook(signal_id, unmap_hook_id);
534 }
535
536 GtkIMContext *
537 hildon_im_context_new(void)
538 {
539   return g_object_new(im_context_type, NULL);
540 }
541
542 /* Retrieves the X window id of the IM */
543 static Window
544 get_window_id(Atom window_atom)
545 {
546   Window result = None;
547   unsigned long n=0;
548   unsigned long extra=0;
549   gint format=0;
550   gint status=0;
551
552   Atom realType;
553   union
554   {
555     Window *win;
556     unsigned char *val;
557   } value;
558
559   gdk_error_trap_push();
560   status = XGetWindowProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
561                               window_atom, 0L, 4L, 0,
562                               XA_WINDOW, &realType, &format,
563                               &n, &extra, (unsigned char **) &value.val);
564
565   if (gdk_error_trap_pop() == 0 &&
566       (status == Success && realType == XA_WINDOW
567       && format == HILDON_IM_WINDOW_ID_FORMAT && n == 1 && value.win != None))
568   {
569     result = value.win[0];
570     XFree(value.val);
571   }
572   else
573   {
574     g_warning("Unable to get the window id\n");
575   }
576
577   return result;
578 }
579
580 #ifdef MAEMO_CHANGES
581 static void
582 hildon_im_context_input_mode_changed(GObject *object, GParamSpec *pspec)
583 {
584   HildonIMContext *self = HILDON_IM_CONTEXT(object);
585   gint input_mode = 0;
586   gint default_input_mode = 0;
587
588   g_object_get (self, "hildon-input-mode", &input_mode, NULL);
589   g_object_get (self, "hildon-input-default", &default_input_mode, NULL);
590
591   self->auto_upper_enabled =
592     ( (self->options & HILDON_IM_AUTOCASE) != 0 &&
593       (input_mode & HILDON_GTK_INPUT_MODE_AUTOCAP) != 0);
594
595   if (self->client_gtk_widget != NULL && GTK_WIDGET_HAS_FOCUS (self->client_gtk_widget))
596   {
597     /* Notify IM of any input mode changes in cases where the UI is
598        already visible. */
599     hildon_im_context_send_input_mode(self);
600   }
601 }
602 #endif
603
604 #ifndef MAEMO_CHANGES
605 static gboolean
606 button_press_release (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
607 {
608   HildonIMContext *self = HILDON_IM_CONTEXT(user_data);
609
610   if ((GTK_IS_ENTRY(widget) || GTK_IS_TEXT_VIEW(widget)) &&
611       (!self->is_internal_widget ||  self->is_url_entry))
612   {
613     return hildon_im_context_filter_event (GTK_IM_CONTEXT(self), (GdkEvent *)event);
614   }
615   return FALSE;
616 }
617 #endif
618
619 static void
620 hildon_im_context_init(HildonIMContext *self)
621 {
622   self->prev_cursor_y = None;
623   self->prev_cursor_x = None;
624   self->has_focus = FALSE;
625   self->surrounding = g_strdup("");
626   self->preedit_buffer = g_string_new ("");
627   self->show_preedit = FALSE;
628   self->space_after_commit = FALSE;
629   self->is_internal_widget = FALSE;
630   self->im_window = get_window_id(hildon_im_protocol_get_atom(HILDON_IM_WINDOW));
631   self->commit_mode = HILDON_IM_COMMIT_REDIRECT;
632   self->previous_commit_mode = self->commit_mode;
633   self->incoming_preedit_buffer = g_string_new ("");
634
635   self->auto_upper_enabled = FALSE;
636   self->auto_upper = FALSE;
637
638   self->button_press_x = -1.0;
639   self->button_press_y = -1.0;
640
641   self->enable_long_press = TRUE;
642   self->char_key_is_down = FALSE;
643   self->long_press_timeout_src_id = 0;
644   self->long_press_timeout = DEFAULT_LONG_PRESS_TIMEOUT;
645   self->long_press_last_key_event = NULL;
646
647   self->last_was_shift_backspace = FALSE;
648
649 #ifdef MAEMO_CHANGES
650   g_signal_connect(self, "notify::hildon-input-mode",
651     G_CALLBACK(hildon_im_context_input_mode_changed), NULL);
652   g_signal_connect(self, "notify::hildon-input-default",
653       G_CALLBACK(hildon_im_context_input_mode_changed), NULL);
654 #endif
655 }
656
657 /* gets the character with relation to the cursor
658  * offset : -1 for previous char, 0 for char at cursor 
659  * returns 0x0 if there was an error */
660 static gunichar
661 get_unichar_by_offset_from_cursor (HildonIMContext *self,
662                                    gint desired_offset)
663 {
664   gchar *surrounding = NULL;
665   gchar *character = NULL;
666   gunichar unicharacter = 0x0;
667   gint offset = 0;
668   
669   hildon_im_context_get_surrounding (GTK_IM_CONTEXT(self), &surrounding, &offset);
670   
671   if (surrounding != NULL)
672   {
673     character = g_utf8_offset_to_pointer (surrounding, (glong)(offset+desired_offset));
674     
675     if (character != NULL)
676     {
677       unicharacter = g_utf8_get_char_validated(character, -1);
678     }
679   }
680   
681   g_free(surrounding);
682   
683   if (unicharacter == (gunichar)-1 || unicharacter == (gunichar)-2)
684     unicharacter = 0x0;
685   
686   return unicharacter;
687 }
688
689 static gboolean
690 commit_text (HildonIMContext *self, const gchar* s)
691 {
692   g_return_val_if_fail(HILDON_IS_IM_CONTEXT(self), FALSE);
693   if (self->client_gtk_widget == NULL
694       || s == NULL)
695     return FALSE;
696
697   g_signal_emit_by_name(self, "commit", s);
698
699   return TRUE;
700 }
701
702 static void
703 set_preedit_buffer (HildonIMContext *self, const gchar* s)
704 {
705 #ifdef MAEMO_CHANGES
706   HildonGtkInputMode input_mode;
707   g_object_get (self, "hildon-input-mode", &input_mode, NULL);
708
709   if ((input_mode & HILDON_GTK_INPUT_MODE_DICTIONARY) == 0 ||
710       (input_mode & HILDON_GTK_INPUT_MODE_INVISIBLE) != 0)
711   {
712     return;
713   }
714 #endif
715   if (self->client_gtk_widget == NULL
716       || !GTK_WIDGET_REALIZED(self->client_gtk_widget))
717     return;
718
719   if (s != NULL)
720   {
721     GtkTextIter cursor;
722     GtkTextBuffer *buffer;
723     gchar *up_string = NULL;
724     gchar *string;
725     
726     if (self->mask & HILDON_IM_SHIFT_LOCK_MASK)
727     {
728       up_string = g_utf8_strup(s, -1);
729       string = up_string;
730     }
731     else
732     {
733       string = (gchar*)s;
734     }
735     
736     if (self->preedit_buffer == NULL)
737     {
738       self->preedit_buffer = g_string_new (string);
739     }
740     else
741     {
742       g_string_append(self->preedit_buffer, string);
743     }
744
745     if (GTK_IS_TEXT_VIEW (self->client_gtk_widget))
746     {
747       buffer = get_buffer(self->client_gtk_widget);
748       gtk_text_buffer_get_iter_at_mark(buffer, &cursor,
749                                        gtk_text_buffer_get_insert(buffer));
750       gtk_text_buffer_move_mark (buffer, self->text_view_preedit_mark, &cursor);
751     }
752     else if (GTK_IS_EDITABLE (self->client_gtk_widget))
753     {
754       self->editable_preedit_position =
755         gtk_editable_get_position(GTK_EDITABLE(self->client_gtk_widget));
756     }
757     
758     self->show_preedit = TRUE;
759     g_signal_emit_by_name(self, "preedit-changed", self->preedit_buffer->str);
760     
761     g_free(up_string);
762   }
763   else
764   {
765     self->show_preedit = FALSE;
766     
767     if (self->preedit_buffer != NULL && self->preedit_buffer->len != 0)
768     {
769       g_string_truncate(self->preedit_buffer, 0);
770       g_signal_emit_by_name(self, "preedit-changed", self->preedit_buffer->str);
771     }
772   }
773 }
774
775 static void
776 hildon_im_context_commit_preedit_data(HildonIMContext *self)
777 {
778   gchar *prefix_to_commit;
779   
780   if (self->preedit_buffer != NULL && self->preedit_buffer->len != 0
781       && self->client_gtk_widget)
782   {
783     GtkTextIter iter;
784     GtkTextBuffer *buffer;
785
786     if (GTK_IS_TEXT_VIEW (self->client_gtk_widget))
787     {
788       buffer = get_buffer(self->client_gtk_widget);
789       gtk_text_buffer_get_iter_at_mark (buffer, &iter, self->text_view_preedit_mark);
790       gtk_text_buffer_place_cursor (buffer, &iter);
791     }
792     else if (GTK_IS_EDITABLE (self->client_gtk_widget))
793     {
794       gtk_editable_set_position(GTK_EDITABLE(self->client_gtk_widget),
795                                 self->editable_preedit_position);
796     }
797
798     prefix_to_commit = g_strdup(self->preedit_buffer->str);
799     
800     if (self->space_after_commit)
801     {
802       gunichar next_char = get_unichar_by_offset_from_cursor(self, 0);
803       
804       if (next_char == 0x0 
805           || (g_unichar_type(next_char) !=  G_UNICODE_SPACE_SEPARATOR
806               && (next_char != 0x09)
807               && !g_unichar_ispunct(next_char)))
808       {
809         g_string_append(self->preedit_buffer, " ");
810         self->committed_preedit = TRUE;
811       }
812     }
813     
814     if (commit_text(self, self->preedit_buffer->str))
815     {
816       hildon_im_context_send_committed_preedit (self, prefix_to_commit);
817     }
818     
819     set_preedit_buffer(self, NULL);
820     
821     g_free(prefix_to_commit);
822   }
823 }
824
825 static void
826 hildon_im_clipboard_copied(HildonIMContext *self)
827 {
828   XEvent ev;
829   gint xerror;
830   
831   memset(&ev, 0, sizeof(ev));
832   ev.xclient.type = ClientMessage;
833   ev.xclient.window = self->im_window;
834   ev.xclient.message_type =
835     hildon_im_protocol_get_atom( HILDON_IM_CLIPBOARD_COPIED );
836   ev.xclient.format = HILDON_IM_CLIPBOARD_FORMAT;
837
838   gdk_error_trap_push();
839   XSendEvent(GDK_DISPLAY(), self->im_window, False, 0, &ev);
840
841   xerror = gdk_error_trap_pop();
842   if (xerror)
843   {
844     if (xerror == BadWindow)
845     {
846       self->im_window = get_window_id(hildon_im_protocol_get_atom(HILDON_IM_WINDOW));
847       ev.xclient.window = self->im_window;
848       XSendEvent(GDK_DISPLAY(), self->im_window, False, 0, &ev);
849     }
850     else
851     {
852       g_warning("Received the X error %d\n", xerror);
853     }
854   }
855 }
856
857 static void
858 hildon_im_clipboard_selection_query(HildonIMContext *self)
859 {
860   XEvent ev;
861   gint xerror;
862   
863   memset(&ev, 0, sizeof(ev));
864   ev.xclient.type = ClientMessage;
865   ev.xclient.window = self->im_window;
866   ev.xclient.message_type =
867     hildon_im_protocol_get_atom( HILDON_IM_CLIPBOARD_SELECTION_REPLY );
868   ev.xclient.format = HILDON_IM_CLIPBOARD_SELECTION_REPLY_FORMAT;
869 #ifdef MAEMO_CHANGES
870   ev.xclient.data.l[0] = hildon_gtk_im_context_has_selection(GTK_IM_CONTEXT(self));
871 #endif
872
873   gdk_error_trap_push();
874   XSendEvent(GDK_DISPLAY(), self->im_window, False, 0, &ev);
875
876   xerror = gdk_error_trap_pop();
877   if (xerror)
878   {
879     if (xerror == BadWindow)
880     {
881       self->im_window = get_window_id(hildon_im_protocol_get_atom(HILDON_IM_WINDOW));
882       ev.xclient.window = self->im_window;
883       XSendEvent(GDK_DISPLAY(), self->im_window, False, 0, &ev);
884     }
885     else
886     {
887       g_warning("Received the X error %d\n", xerror);
888     }
889   }
890 }
891
892 static gboolean
893 surroundings_search_predicate (gunichar c, gpointer data)
894 {
895   return !g_unichar_isspace(c);
896 }
897
898 static gboolean
899 get_textview_surrounding(GtkTextView *text_view,
900                          GtkTextIter *iter,  /* the beginning of the selection or the location of the cursor */
901                          gchar **surrounding,
902                          gint *iter_index)
903 {
904   GtkTextIter start;
905   GtkTextIter end;
906   gint pos;
907   gchar *text;
908   gchar *text_between = NULL;
909   GtkTextBuffer *buffer;
910
911   buffer = get_buffer(GTK_WIDGET(text_view));
912   if (iter != NULL)
913     end = start = *iter;
914   
915   gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
916
917   gtk_text_iter_set_line_offset(&start, 0);
918   gtk_text_iter_forward_to_line_end (&end);
919
920   /* Include the previous non-whitespace character in the surrounding */
921   if (gtk_text_iter_backward_char (&start))
922     gtk_text_iter_backward_find_char(&start, surroundings_search_predicate,
923                                      NULL, NULL);
924
925   text_between = gtk_text_iter_get_slice(&start, iter);
926
927   if (text_between != NULL)
928     pos = g_utf8_strlen(text_between, -1);
929   else
930     pos = 0;
931
932   text = gtk_text_iter_get_slice(&start, &end);
933
934   *surrounding = text;
935   *iter_index = pos;
936
937   g_free(text_between);
938
939   return TRUE;
940 }
941
942 gboolean
943 hildon_im_context_get_surrounding(GtkIMContext *context,
944                                   gchar **text,
945                                   gint *cursor_index)
946 {
947   HildonIMContext *self;
948   gboolean result = FALSE;
949
950   g_return_val_if_fail(HILDON_IS_IM_CONTEXT(context), FALSE);
951   self = HILDON_IM_CONTEXT(context);
952
953   /* Override the textview surrounding handler */
954   if (GTK_IS_TEXT_VIEW(self->client_gtk_widget))
955   {
956     GtkTextIter cursor;
957     GtkTextBuffer *buffer;
958     
959     buffer = get_buffer(self->client_gtk_widget);
960     gtk_text_buffer_get_iter_at_mark(buffer, &cursor,
961                                     gtk_text_buffer_get_insert(buffer));
962     result = get_textview_surrounding(GTK_TEXT_VIEW(self->client_gtk_widget),
963                                       &cursor,
964                                       text,
965                                       cursor_index);
966   }
967   else
968   {
969     gchar *local_text = NULL;
970     gint local_index = 0;
971
972     result = parent_class->get_surrounding(context,
973                                            text ? text : &local_text,
974                                            cursor_index ? cursor_index : &local_index);
975
976     if (result)
977     {
978       g_free(local_text);
979     }
980   }
981
982   return result;
983 }
984
985 static void
986 hildon_im_context_commit_surrounding(HildonIMContext *self)
987 {
988   gchar *surrounding;
989   gint cpos;
990   gboolean has_surrounding;
991
992   has_surrounding = gtk_im_context_get_surrounding (GTK_IM_CONTEXT(self),
993                                                     &surrounding, &cpos);
994   if (has_surrounding)
995   {
996     gint offset_start, offset_end;
997     gchar *str, *start, *end = NULL;
998     gboolean deleted;
999
1000     str = &surrounding[cpos];
1001
1002     start = surrounding;
1003     for (end = str; end[0] != '\0'; end++);
1004
1005     if (end-start > 0)
1006     {
1007       /* Offset to the start of the line, from the insertion point */
1008       offset_start = str - start;
1009       /* Offset to the end of the line */
1010       offset_end = end - start;
1011
1012       /* Remove the surrounding context on the line with the cursor */
1013       deleted = gtk_im_context_delete_surrounding(GTK_IM_CONTEXT(self),
1014                                                   -offset_start,
1015                                                   offset_end);
1016     }
1017   }
1018
1019   /* Place the new surrounding context at the insertion point */
1020   commit_text(self, self->surrounding);
1021
1022   if (has_surrounding)
1023   {
1024     g_free(surrounding);
1025   }
1026 }
1027
1028 static GSList *
1029 get_pango_attribute_list_for_insert (GtkIMContext *context, gchar *str_to_apply_attr)
1030 {
1031   GSList *attr_list = NULL, *insert_attr_list = NULL, *list;
1032   GtkTextIter iter;
1033   GtkTextMark *insert_mark;
1034   GtkTextBuffer *buffer;
1035   HildonIMContext *self;
1036   
1037   self = HILDON_IM_CONTEXT(context);
1038
1039   if (!GTK_IS_TEXT_VIEW (self->client_gtk_widget))
1040     return NULL;
1041
1042   buffer = get_buffer (self->client_gtk_widget);
1043   if (buffer == NULL)
1044     return NULL;
1045
1046   insert_mark = gtk_text_buffer_get_insert (buffer);
1047   gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert_mark);
1048   /* We backward because we want the iter of the previous char */
1049   gtk_text_iter_backward_char (&iter);
1050   insert_attr_list = gtk_text_iter_get_tags (&iter);
1051
1052   list = insert_attr_list;
1053   for (; list != NULL; list = g_slist_next (list))
1054   {
1055     PangoFontDescription *font_desc = NULL;
1056     GtkTextTag *tag = GTK_TEXT_TAG (list->data);
1057     g_object_get (tag, "font-desc", &font_desc, NULL);
1058     if (font_desc != NULL)
1059     {
1060       PangoAttribute *attr;
1061       attr = pango_attr_font_desc_new (font_desc);
1062       attr->start_index = 0;
1063       attr->end_index = strlen (str_to_apply_attr);
1064
1065       attr_list = g_slist_append (attr_list, attr);
1066     }
1067   }
1068   g_slist_free (insert_attr_list);
1069
1070   return attr_list;
1071 }
1072
1073 static void
1074 hildon_im_context_get_preedit_string (GtkIMContext *context,
1075                                       gchar **str,
1076                                       PangoAttrList **attrs,
1077                                       gint *cursor_pos)
1078 {
1079   HildonIMContext *self;
1080   GSList *insert_attr_list = NULL, *list;
1081   GtkStyle *style = NULL;
1082   PangoAttribute *attr1, *attr2, *attr3;
1083   
1084   g_return_if_fail(HILDON_IS_IM_CONTEXT(context));
1085   self = HILDON_IM_CONTEXT(context);
1086   
1087   if (cursor_pos != NULL)
1088     *cursor_pos = 0;
1089   
1090   if (str != NULL)
1091   {
1092     if (self->preedit_buffer != NULL && self->show_preedit)
1093     {
1094       *str = g_strdup (self->preedit_buffer->str);
1095     }
1096     else
1097     {
1098       *str = g_strdup ("");
1099     }
1100   }
1101
1102   if (attrs != NULL && self->client_gtk_widget != NULL)
1103   {
1104     if (GTK_IS_WIDGET(self->client_gtk_widget))
1105     {
1106       style = gtk_widget_get_style (self->client_gtk_widget);
1107     }
1108     
1109     if (style == NULL)
1110     {
1111       style = gtk_widget_get_default_style ();
1112     }
1113     
1114     attr1 = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
1115     attr1->start_index = 0;
1116     attr1->end_index = strlen (*str);
1117     attr2 = pango_attr_background_new (style->bg[GTK_STATE_SELECTED].red,
1118                                        style->bg[GTK_STATE_SELECTED].green,
1119                                        style->bg[GTK_STATE_SELECTED].blue);
1120     attr2->start_index = 0;
1121     attr2->end_index = strlen (*str);
1122     attr3 = pango_attr_foreground_new (style->fg[GTK_STATE_SELECTED].red,
1123                                        style->fg[GTK_STATE_SELECTED].green,
1124                                        style->fg[GTK_STATE_SELECTED].blue);
1125     attr3->start_index = 0;
1126     attr3->end_index = strlen (*str);
1127     
1128     *attrs = pango_attr_list_new ();
1129     pango_attr_list_insert (*attrs, attr1);
1130     pango_attr_list_insert (*attrs, attr2);
1131     pango_attr_list_insert (*attrs, attr3);
1132
1133     insert_attr_list = get_pango_attribute_list_for_insert (context, *str);
1134     for (list = insert_attr_list; list != NULL; list = g_slist_next (list))
1135     {
1136       pango_attr_list_insert (*attrs, (PangoAttribute *) list->data);
1137     }
1138     g_slist_free (insert_attr_list);
1139   }
1140   
1141   return;
1142 }
1143
1144 static void
1145 hildon_im_context_set_use_preedit (GtkIMContext *context,
1146                                    gboolean use_preedit)
1147 {
1148   /* TODO something... */
1149   return;
1150 }
1151
1152 static void
1153 hildon_im_context_set_client_cursor_location(HildonIMContext *self,
1154                                              gboolean is_relative,
1155                                              gint offset)
1156 {
1157   GtkWidget *widget;
1158
1159   widget = self->client_gtk_widget;
1160
1161   if (widget != NULL && (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget)))
1162   {
1163     if (GTK_IS_EDITABLE(widget))
1164     {
1165       if (is_relative)
1166       {
1167         gtk_editable_set_position(GTK_EDITABLE(widget), 
1168                                   gtk_editable_get_position(GTK_EDITABLE(widget)) + offset);
1169       }
1170       else
1171       {
1172         gtk_editable_set_position(GTK_EDITABLE(widget), offset);
1173       }
1174     }
1175     else if (GTK_IS_TEXT_VIEW(widget))
1176     {
1177       GtkTextBuffer *buffer;
1178       GtkTextMark *insert;
1179       GtkTextIter iter;
1180
1181       buffer = get_buffer(widget);
1182
1183       insert = gtk_text_buffer_get_mark(buffer, "insert");
1184       gtk_text_buffer_get_iter_at_mark(buffer, &iter, insert);
1185
1186       if (is_relative)
1187       {
1188         if (offset == 0)
1189           gtk_text_buffer_select_range (buffer, &iter, &iter);
1190         else if (offset >= 0)
1191           gtk_text_iter_forward_chars(&iter, offset);
1192         else
1193           gtk_text_iter_backward_chars(&iter, -offset);
1194       }
1195       else
1196       {
1197         gtk_text_buffer_get_iter_at_offset(buffer, &iter, offset);
1198       }
1199
1200       gtk_text_buffer_place_cursor(buffer, &iter);
1201     }
1202   }
1203 }
1204
1205 static void
1206 hildon_im_context_clear_selection(HildonIMContext *self)
1207 {
1208   if (GTK_IS_TEXT_VIEW(self->client_gtk_widget))
1209   {
1210     GtkTextMark *selection, *insert;
1211     GtkTextBuffer *buffer;
1212     GtkTextIter iter;
1213
1214     buffer = get_buffer(self->client_gtk_widget);
1215     selection = gtk_text_buffer_get_selection_bound(buffer);
1216     insert = gtk_text_buffer_get_insert(buffer);
1217     gtk_text_buffer_get_iter_at_mark(buffer, &iter, insert);
1218     gtk_text_buffer_move_mark(buffer, selection, &iter);
1219   }
1220   else if (GTK_IS_EDITABLE(self->client_gtk_widget))
1221   {
1222     gint pos;
1223
1224     pos = gtk_editable_get_position(GTK_EDITABLE(self->client_gtk_widget));
1225     gtk_editable_select_region(GTK_EDITABLE(self->client_gtk_widget), pos, pos);
1226   }
1227 }
1228
1229 static void
1230 hildon_im_context_do_backspace (HildonIMContext *self)
1231 {
1232   if (self->commit_mode == HILDON_IM_COMMIT_REDIRECT &&
1233       GTK_IS_TEXT_VIEW(self->client_gtk_widget))
1234   {
1235     GtkTextBuffer *buffer;
1236     GtkTextIter iter;
1237
1238     buffer = get_buffer(self->client_gtk_widget);
1239     gtk_text_buffer_get_iter_at_mark(buffer, &iter,
1240                                      gtk_text_buffer_get_insert(buffer));
1241     gtk_text_buffer_backspace(buffer, &iter, TRUE, TRUE);
1242   }
1243   else if (self->commit_mode == HILDON_IM_COMMIT_REDIRECT &&
1244            GTK_IS_EDITABLE(self->client_gtk_widget))
1245   {
1246     gint position = gtk_editable_get_position(GTK_EDITABLE(self->client_gtk_widget));
1247     gtk_editable_delete_text(GTK_EDITABLE(self->client_gtk_widget),
1248                              position - 1, position);
1249   }
1250   else
1251   {
1252     hildon_im_context_send_fake_key(GDK_BackSpace, TRUE);
1253     hildon_im_context_send_fake_key(GDK_BackSpace, FALSE);
1254   }
1255 }
1256
1257 static gboolean
1258 hildon_im_context_do_del (HildonIMContext *self)
1259 {
1260   gint cpos1;
1261   gchar *sur;
1262
1263   /* This is only for non-GTK+ widgets. For normal GTK+ text entries,
1264      'del' key is handled normally at X level */
1265   self->last_was_shift_backspace = TRUE;
1266
1267   if ( (! GTK_IS_TEXT_VIEW (self->client_gtk_widget)) &&
1268        (! GTK_IS_EDITABLE (self->client_gtk_widget)) )
1269   {
1270     gtk_im_context_get_surrounding (GTK_IM_CONTEXT (self), &sur, &cpos1);
1271
1272     if (g_utf8_strlen (sur, -1) > cpos1)
1273     {
1274       hildon_im_context_send_fake_key (GDK_Shift_L, FALSE);
1275       hildon_im_context_send_fake_key (GDK_Right, TRUE);
1276       hildon_im_context_send_fake_key (GDK_Right, FALSE);
1277       hildon_im_context_send_fake_key (GDK_BackSpace, TRUE);
1278       hildon_im_context_send_fake_key (GDK_Shift_L, TRUE);
1279     }
1280     g_free (sur);
1281
1282     return TRUE;
1283   }
1284   else
1285   {
1286     return FALSE;
1287   }
1288 }
1289
1290 /* Filter function to intercept and process XClientMessages */
1291 static GdkFilterReturn
1292 client_message_filter(GdkXEvent *xevent,GdkEvent *event,
1293                       HildonIMContext *self)
1294 {
1295   GdkFilterReturn result = GDK_FILTER_CONTINUE;
1296   XEvent *xe = (XEvent *)xevent;
1297
1298   g_return_val_if_fail(HILDON_IS_IM_CONTEXT(self), GDK_FILTER_CONTINUE);
1299
1300
1301   if (xe->type == DestroyNotify)
1302   { 
1303     self->client_gdk_window = NULL;
1304   }
1305   else if (xe->type == ClientMessage)
1306   {
1307     XClientMessageEvent *cme = xevent;
1308
1309     if (cme->message_type == hildon_im_protocol_get_atom(HILDON_IM_INSERT_UTF8)
1310         && cme->format == HILDON_IM_INSERT_UTF8_FORMAT)
1311     {
1312       HildonIMInsertUtf8Message *msg = (HildonIMInsertUtf8Message *)&cme->data;
1313
1314       hildon_im_context_insert_utf8( self, msg->msg_flag, msg->utf8_str );
1315       result = GDK_FILTER_REMOVE;
1316     }
1317     else if (cme->message_type == hildon_im_protocol_get_atom(HILDON_IM_COM)
1318         && cme->format == HILDON_IM_COM_FORMAT)
1319     {
1320       HildonIMComMessage *msg = (HildonIMComMessage *)&cme->data;
1321
1322       /* if autocap was suddenly deactivated, cleanup shift stickiness */
1323       if ( (! (msg->options & HILDON_IM_AUTOCASE)) &&
1324            (self->options & HILDON_IM_AUTOCASE) )
1325         {
1326           self->mask &= ~HILDON_IM_SHIFT_STICKY_MASK;
1327           hildon_im_context_send_command (self, HILDON_IM_SHIFT_UNSTICKY);
1328         }
1329
1330       self->options = msg->options;
1331
1332       switch(msg->type)
1333       {
1334         case HILDON_IM_CONTEXT_WIDGET_CHANGED:
1335           self->mask = 0;
1336           break;
1337         case HILDON_IM_CONTEXT_HANDLE_ENTER:
1338           hildon_im_context_send_fake_key(GDK_KP_Enter, TRUE);
1339           hildon_im_context_send_fake_key(GDK_KP_Enter, FALSE);
1340           break;
1341         case HILDON_IM_CONTEXT_ENTER_ON_FOCUS:
1342           enter_on_focus_pending = TRUE;
1343           break;
1344         case HILDON_IM_CONTEXT_CONFIRM_SENTENCE_START:
1345           hildon_im_context_check_sentence_start(self);
1346           break;
1347         case HILDON_IM_CONTEXT_HANDLE_TAB:
1348           hildon_im_context_send_fake_key(GDK_Tab, TRUE);
1349           hildon_im_context_send_fake_key(GDK_Tab, FALSE);
1350           break;
1351         case HILDON_IM_CONTEXT_HANDLE_BACKSPACE:
1352           hildon_im_context_do_backspace (self);
1353           break;
1354         case HILDON_IM_CONTEXT_HANDLE_SPACE:
1355           hildon_im_context_insert_utf8(self, HILDON_IM_MSG_CONTINUE, " ");
1356           break;
1357         case HILDON_IM_CONTEXT_BUFFERED_MODE:
1358           set_preedit_buffer (self, NULL);
1359           self->commit_mode = HILDON_IM_COMMIT_BUFFERED;
1360           break;
1361         case HILDON_IM_CONTEXT_DIRECT_MODE:
1362           set_preedit_buffer (self, NULL);
1363           self->commit_mode = HILDON_IM_COMMIT_DIRECT;
1364           break;
1365         case HILDON_IM_CONTEXT_REDIRECT_MODE:
1366           set_preedit_buffer (self, NULL);
1367           self->commit_mode = HILDON_IM_COMMIT_REDIRECT;
1368           hildon_im_context_clear_selection(self);
1369           break;
1370         case HILDON_IM_CONTEXT_SURROUNDING_MODE:
1371           set_preedit_buffer (self, NULL);
1372           self->commit_mode = HILDON_IM_COMMIT_SURROUNDING;
1373           break;
1374         case HILDON_IM_CONTEXT_PREEDIT_MODE:
1375           set_preedit_buffer (self, NULL);
1376           /* Preedit is a temporary mode that will be reset after
1377            * the next text has been received.
1378            */
1379           self->previous_commit_mode = self->commit_mode;
1380           self->commit_mode = HILDON_IM_COMMIT_PREEDIT;
1381           break;
1382         case HILDON_IM_CONTEXT_REQUEST_SURROUNDING:
1383           hildon_im_context_send_surrounding(self, FALSE);
1384           if (self->is_url_entry)
1385           {
1386             hildon_im_context_send_command(self, HILDON_IM_SELECT_ALL);
1387           }
1388           break;
1389         case HILDON_IM_CONTEXT_REQUEST_SURROUNDING_FULL:
1390           hildon_im_context_send_surrounding(self, TRUE);
1391           if (self->is_url_entry)
1392           {
1393             hildon_im_context_send_command(self, HILDON_IM_SELECT_ALL);
1394           }
1395           break;
1396         case HILDON_IM_CONTEXT_FLUSH_PREEDIT:
1397           hildon_im_context_commit_preedit_data(self);
1398           break;
1399         case HILDON_IM_CONTEXT_CANCEL_PREEDIT:
1400           set_preedit_buffer (self, NULL);
1401           break;
1402 #ifdef MAEMO_CHANGES
1403         case HILDON_IM_CONTEXT_CLIPBOARD_COPY:
1404             hildon_gtk_im_context_copy(GTK_IM_CONTEXT(self));
1405           break;
1406         case HILDON_IM_CONTEXT_CLIPBOARD_CUT:
1407             hildon_gtk_im_context_cut(GTK_IM_CONTEXT(self));
1408           break;
1409         case HILDON_IM_CONTEXT_CLIPBOARD_PASTE:
1410             hildon_gtk_im_context_paste(GTK_IM_CONTEXT(self));
1411           break;
1412 #endif
1413         case HILDON_IM_CONTEXT_CLIPBOARD_SELECTION_QUERY:
1414             hildon_im_clipboard_selection_query(self);
1415           break;
1416         case HILDON_IM_CONTEXT_OPTION_CHANGED:
1417           break;
1418         case HILDON_IM_CONTEXT_SPACE_AFTER_COMMIT:
1419           self->space_after_commit = TRUE;
1420           break;
1421         case HILDON_IM_CONTEXT_NO_SPACE_AFTER_COMMIT:
1422           self->space_after_commit = FALSE;
1423           break;
1424         case HILDON_IM_CONTEXT_SHIFT_LOCKED:
1425           self->mask |= HILDON_IM_SHIFT_LOCK_MASK;
1426           break;
1427         case HILDON_IM_CONTEXT_SHIFT_UNLOCKED:
1428           self->mask &= ~HILDON_IM_SHIFT_LOCK_MASK;
1429         case HILDON_IM_CONTEXT_SHIFT_UNSTICKY:
1430           self->mask &= ~HILDON_IM_SHIFT_STICKY_MASK;
1431           break;
1432         case HILDON_IM_CONTEXT_LEVEL_LOCKED:
1433           self->mask |= HILDON_IM_LEVEL_LOCK_MASK;
1434           break;
1435         case HILDON_IM_CONTEXT_LEVEL_UNLOCKED:
1436           self->mask &= ~HILDON_IM_LEVEL_LOCK_MASK;
1437         case HILDON_IM_CONTEXT_LEVEL_UNSTICKY:
1438           self->mask &= ~HILDON_IM_LEVEL_STICKY_MASK;
1439           break;
1440         default:
1441           g_warning("Invalid communication message from IM");
1442           break;
1443       }
1444       result = GDK_FILTER_REMOVE;
1445     }
1446     else if (cme->message_type == hildon_im_protocol_get_atom(HILDON_IM_SURROUNDING_CONTENT)
1447         && cme->format == HILDON_IM_SURROUNDING_CONTENT_FORMAT)
1448     {
1449       HildonIMSurroundingContentMessage *msg =
1450         (HildonIMSurroundingContentMessage *)&cme->data;
1451       gchar *new_surrounding;
1452
1453       if (msg->msg_flag == HILDON_IM_MSG_START && self->surrounding)
1454       {
1455         g_free(self->surrounding);
1456         self->surrounding = g_strdup("");
1457       }
1458
1459       if (msg->msg_flag == HILDON_IM_MSG_END && self->surrounding)
1460       {
1461         hildon_im_context_commit_surrounding(self);
1462         result = GDK_FILTER_REMOVE;
1463       }
1464       else
1465       {
1466         new_surrounding = g_strconcat(self->surrounding,
1467                                       msg->surrounding, NULL);
1468         
1469         g_free(self->surrounding);
1470         self->surrounding = new_surrounding;
1471
1472         result = GDK_FILTER_REMOVE;
1473       }
1474     }
1475     else if (cme->message_type == hildon_im_protocol_get_atom(HILDON_IM_SURROUNDING)
1476         && cme->format == HILDON_IM_SURROUNDING_FORMAT)
1477     {
1478       HildonIMSurroundingMessage *msg =
1479         (HildonIMSurroundingMessage *)&cme->data;
1480
1481       hildon_im_context_set_client_cursor_location(self,
1482                                                    msg->offset_is_relative,
1483                                                    msg->cursor_offset);
1484       result = GDK_FILTER_REMOVE;
1485
1486     }
1487     else if (cme->message_type ==
1488              hildon_im_protocol_get_atom (HILDON_IM_LONG_PRESS_SETTINGS))
1489     {
1490       HildonIMLongPressSettingsMessage *msg =
1491         (HildonIMLongPressSettingsMessage *) &cme->data;
1492
1493       self->enable_long_press = msg->enable_long_press;
1494       self->long_press_timeout = (msg->long_press_timeout > 0)
1495         ? msg->long_press_timeout : DEFAULT_LONG_PRESS_TIMEOUT;
1496
1497       result = GDK_FILTER_REMOVE;
1498     }
1499   }
1500   
1501   return result;
1502 }
1503 /* Virtual functions */
1504
1505 static void
1506 hildon_im_context_widget_changed(HildonIMContext *self)
1507 {
1508   /* Count how many "changed" signals we get for
1509      hildon_im_context_insert_utf8() */
1510   self->changed_count++;
1511 }
1512
1513 static void
1514 hildon_im_context_widget_hide(GtkIMContext *context)
1515 {
1516   HildonIMContext *self;
1517
1518   g_return_if_fail(HILDON_IS_IM_CONTEXT(context));
1519   self = HILDON_IM_CONTEXT(context);
1520
1521   if (self->client_gtk_widget &&
1522       GTK_WIDGET_HAS_FOCUS(self->client_gtk_widget))
1523   {
1524     hildon_im_context_hide(context);
1525   }
1526 }
1527
1528 static void
1529 hildon_im_context_widget_copy_clipboard(GtkIMContext *context)
1530 {
1531   HildonIMContext *self;
1532   gboolean copied = FALSE;
1533
1534   g_return_if_fail(HILDON_IS_IM_CONTEXT(context));
1535   self = HILDON_IM_CONTEXT(context);
1536
1537   if (self->client_gtk_widget)
1538   {
1539     if (GTK_IS_EDITABLE(self->client_gtk_widget))
1540     {
1541       copied = gtk_editable_get_selection_bounds(
1542         GTK_EDITABLE(self->client_gtk_widget), NULL, NULL);
1543     }
1544     else if (GTK_IS_TEXT_VIEW(self->client_gtk_widget))
1545     {
1546       GtkTextBuffer *buffer;
1547
1548       buffer = get_buffer(self->client_gtk_widget);
1549       copied = gtk_text_buffer_get_selection_bounds(buffer,
1550                                                     NULL, NULL);
1551     }
1552   }
1553
1554   if (copied)
1555   {
1556     hildon_im_clipboard_copied(HILDON_IM_CONTEXT(context));
1557   }
1558 }
1559
1560 /* Register the client widgets GdkWindow where the input will appear */
1561 static void
1562 hildon_im_context_set_client_window(GtkIMContext *context,
1563                                     GdkWindow *window)
1564 {
1565   HildonIMContext *self;
1566
1567   g_return_if_fail( HILDON_IS_IM_CONTEXT( context ) );
1568   self = HILDON_IM_CONTEXT(context);
1569
1570   if (self->client_changed_signal_handler > 0)
1571   {
1572     g_signal_handler_disconnect(self->client_gtk_widget,
1573                                 self->client_changed_signal_handler);
1574     self->client_changed_signal_handler = 0;
1575   }
1576
1577   if (self->client_hide_signal_handler > 0)
1578   {
1579     g_signal_handler_disconnect(self->client_gtk_widget,
1580                                 self->client_hide_signal_handler);
1581     self->client_hide_signal_handler = 0;
1582   }
1583
1584   if (self->client_copy_clipboard_signal_handler > 0)
1585   {
1586     g_signal_handler_disconnect(self->client_gtk_widget,
1587                                 self->client_copy_clipboard_signal_handler);
1588     self->client_copy_clipboard_signal_handler = 0;
1589   }
1590
1591   if (self->client_gdk_window != NULL) {
1592     /* Need to clean up old window unhook gdk_event_filter etc */
1593     gdk_window_remove_filter(self->client_gdk_window,
1594                              (GdkFilterFunc)client_message_filter, self );
1595
1596     if (window == NULL && !self->is_internal_widget)
1597     {
1598       hildon_im_context_send_command(self, HILDON_IM_HIDE);
1599     }
1600   }
1601
1602   self->is_url_entry = FALSE;
1603   self->committed_preedit = FALSE;
1604   self->client_gdk_window = window;
1605   self->client_gtk_widget = NULL;
1606   self->text_view_preedit_mark = NULL;
1607   self->editable_preedit_position = 0;
1608   self->last_key_event = NULL;
1609   self->mask = 0;
1610
1611   if (window)
1612   {
1613     /* Filter the window for ClientMessages*/
1614     gpointer widget;
1615
1616     gdk_window_add_filter(window, (GdkFilterFunc)client_message_filter, self);
1617
1618     /* If the widget contents change, we want to know about it. */
1619     gdk_window_get_user_data(window, &widget);
1620     if (widget != NULL)
1621     {
1622       self->client_gtk_widget = widget;
1623
1624       if (GTK_IS_WIDGET(widget))
1625       {
1626         self->client_hide_signal_handler = g_signal_connect_swapped(widget,
1627                "hide", G_CALLBACK(hildon_im_context_widget_hide), self);
1628
1629         if (GTK_IS_ENTRY(widget) || GTK_IS_TEXT_VIEW(widget))
1630         {
1631           self->client_copy_clipboard_signal_handler =
1632             g_signal_connect_swapped(widget, "copy-clipboard",
1633               G_CALLBACK(hildon_im_context_widget_copy_clipboard), self);
1634         }
1635
1636         if (GTK_IS_TEXT_VIEW(widget))
1637         {
1638           GtkTextIter start;
1639           gtk_text_buffer_get_start_iter (get_buffer(widget),
1640                                           &start);
1641           self->text_view_preedit_mark = gtk_text_buffer_create_mark (
1642                                 get_buffer(widget),
1643                                 "preedit", &start, FALSE);
1644         }
1645
1646         /* The commit mode depends on the type of widget */
1647         if (GTK_IS_TEXT_VIEW(self->client_gtk_widget) ||
1648             GTK_IS_EDITABLE(self->client_gtk_widget))
1649         {
1650           self->commit_mode = HILDON_IM_COMMIT_REDIRECT;
1651         }
1652         else
1653         {
1654           /* Other widgets (direct IM implementations like browsers)
1655                are edited in the fullscreen mode and then commited all at once */
1656           self->commit_mode = HILDON_IM_COMMIT_SURROUNDING;
1657         }
1658
1659 #ifndef MAEMO_CHANGES
1660         g_signal_connect(self->client_gtk_widget, "button-press-event",
1661             G_CALLBACK(button_press_release), self);
1662         g_signal_connect(self->client_gtk_widget, "button-release-event",
1663             G_CALLBACK(button_press_release), self);
1664 #endif
1665       }
1666
1667       if (g_strcmp0(gtk_widget_get_name(widget), HILDON_IM_INTERNAL_TEXTVIEW) == 0)
1668       {
1669         self->is_internal_widget = TRUE;
1670       }
1671       else if (g_strcmp0 (gtk_widget_get_name(widget), MAEMO_BROWSER_URL_ENTRY) == 0)
1672       {
1673         self->is_url_entry = TRUE;
1674       }
1675
1676       if (GTK_IS_EDITABLE(widget))
1677       {
1678         self->client_changed_signal_handler = g_signal_connect_swapped(widget,
1679                 "changed", G_CALLBACK(hildon_im_context_widget_changed), self);
1680       }
1681
1682       gtk_widget_set_extension_events(self->client_gtk_widget,
1683                                       GDK_EXTENSION_EVENTS_ALL);
1684     }
1685   }
1686 }
1687
1688 static void
1689 hildon_im_context_focus_in(GtkIMContext *context)
1690 {
1691   HildonIMContext *self;
1692   g_return_if_fail(HILDON_IS_IM_CONTEXT(context));
1693   self = HILDON_IM_CONTEXT(context);
1694
1695   self->has_focus = TRUE;
1696
1697   if (self->is_internal_widget)
1698   {
1699     return;
1700   }
1701
1702   hildon_im_context_send_command(self, HILDON_IM_SETCLIENT);
1703
1704   hildon_im_context_send_command (self, HILDON_IM_SHIFT_UNSTICKY);
1705   hildon_im_context_send_command (self, HILDON_IM_MOD_UNSTICKY);
1706
1707   if (enter_on_focus_pending)
1708   {
1709     hildon_im_context_send_fake_key(GDK_KP_Enter, TRUE);
1710     hildon_im_context_send_fake_key(GDK_KP_Enter, FALSE);
1711     enter_on_focus_pending = FALSE;
1712   }
1713 }
1714
1715 static void
1716 hildon_im_context_focus_out(GtkIMContext *context)
1717 {
1718   HildonIMContext *self;
1719   g_return_if_fail(HILDON_IS_IM_CONTEXT(context));
1720   self = HILDON_IM_CONTEXT(context);
1721
1722   self->has_focus = FALSE;
1723
1724   set_preedit_buffer (self, NULL);
1725
1726   /* clear any long-press data */
1727   hildon_im_context_abort_long_press (self);
1728   if (self->long_press_last_key_event != NULL)
1729   {
1730     gdk_event_free ((GdkEvent *) self->long_press_last_key_event);
1731     self->long_press_last_key_event = NULL;
1732   }
1733 }
1734
1735 static gboolean
1736 hildon_im_context_font_has_char(HildonIMContext *self, guint32 c)
1737 {
1738   PangoLayout *layout = NULL;
1739   gboolean has_char;
1740   cairo_t *cr;
1741   gchar utf8[7];
1742   gint len;
1743
1744   g_return_val_if_fail(HILDON_IS_IM_CONTEXT(self), TRUE);
1745   g_return_val_if_fail(self->client_gtk_widget &&
1746                        self->client_gtk_widget->window, TRUE);
1747
1748   cr = gdk_cairo_create(self->client_gtk_widget->window);
1749   len = g_unichar_to_utf8(c, utf8);
1750   layout = pango_cairo_create_layout(cr);
1751   pango_layout_set_text(layout, utf8, len);
1752   has_char = pango_layout_get_unknown_glyphs_count(layout) == 0;
1753
1754   g_object_unref(layout);
1755   cairo_destroy(cr);
1756
1757   return has_char;
1758 }
1759
1760 static guint32
1761 dead_key_to_unicode_combining_character(guint keyval)
1762 {
1763   guint32 combining;
1764
1765   switch (keyval)
1766   {
1767     case GDK_dead_grave:            combining = 0x0300; break;
1768     case GDK_dead_acute:            combining = 0x0301; break;
1769     case GDK_dead_circumflex:       combining = 0x0302; break;
1770     case GDK_dead_tilde:            combining = 0x0303; break;
1771     case GDK_dead_macron:           combining = 0x0304; break;
1772     case GDK_dead_breve:            combining = 0x032e; break;
1773     case GDK_dead_abovedot:         combining = 0x0307; break;
1774     case GDK_dead_diaeresis:        combining = 0x0308; break;
1775     case GDK_dead_abovering:        combining = 0x030a; break;
1776     case GDK_dead_doubleacute:      combining = 0x030b; break;
1777     case GDK_dead_caron:            combining = 0x030c; break;
1778     case GDK_dead_cedilla:          combining = 0x0327; break;
1779     case GDK_dead_ogonek:           combining = 0x0328; break;
1780     case GDK_dead_iota:             combining = 0; break; /* Cannot be combined */
1781     case GDK_dead_voiced_sound:     combining = 0; break; /* Cannot be combined */
1782     case GDK_dead_semivoiced_sound: combining = 0; break; /* Cannot be combined */
1783     case GDK_dead_belowdot:         combining = 0x0323; break;
1784     case GDK_dead_hook:             combining = 0x0309; break;
1785     case GDK_dead_horn:             combining = 0x031b; break;
1786     default: combining = 0; break; /* Unknown dead key */
1787   }
1788
1789   return combining;
1790 }
1791
1792 static guint32
1793 combining_character_to_unicode (guint32 combining)
1794 {
1795   guint32 unicode;
1796
1797   switch (combining)
1798   {
1799     case 0x0300: unicode = 0x0060; break;
1800     case 0x0301: unicode = 0x00b4; break;
1801     case 0x0302: unicode = 0x005e; break;
1802     case 0x0303: unicode = 0x007e; break;
1803     case 0x0304: unicode = 0x00af; break;
1804     case 0x0307: unicode = 0x02d9; break;
1805     case 0x0308: unicode = 0x00a8; break;
1806     case 0x0309: unicode = 0x0294; break;
1807     case 0x030a: unicode = 0x00b0; break;
1808     case 0x030b: unicode = 0x0022; break;
1809     case 0x030c: unicode = 0x02c7; break;
1810     case 0x031b: unicode = 0x031b; break;
1811     case 0x0323: unicode = 0x02d4; break;
1812     case 0x0327: unicode = 0x00b8; break;
1813     case 0x0328: unicode = 0x02db; break;
1814     case 0x032e: unicode = 0x032e; break;
1815     default: unicode = 0; break; /* Unknown combining char */
1816   }
1817
1818   return unicode;
1819 }
1820
1821 static guint
1822 hildon_im_context_get_event_keyval_for_level(HildonIMContext *self,
1823                                              GdkEventKey *event,
1824                                              gint level)
1825 {
1826   guint keyval;
1827   GdkKeymapKey *keys = NULL;
1828   guint *keyvals = NULL;
1829   gint n_entries;
1830   gboolean has_entries;
1831
1832   has_entries = gdk_keymap_get_entries_for_keycode(gdk_keymap_get_default(),
1833                                                    event->hardware_keycode,
1834                                                    &keys,
1835                                                    &keyvals,
1836                                                    &n_entries);
1837   if (has_entries && level < n_entries &&
1838       keys[level].group == event->group)
1839   {
1840     GdkKeymapKey k;
1841
1842     k.keycode = event->hardware_keycode;
1843     k.group = event->group;
1844     k.level = level;
1845
1846     keyval = gdk_keymap_lookup_key(gdk_keymap_get_default(), &k);
1847   }
1848   else
1849   {
1850     keyval = event->keyval;
1851   }
1852
1853   if (keys)
1854     g_free(keys);
1855   if (keyvals)
1856     g_free(keyvals);
1857
1858   return keyval;
1859 }
1860
1861 static void
1862 hildon_im_context_set_mask_state(HildonIMContext *self,
1863                                  HildonIMInternalModifierMask *mask,
1864                                  HildonIMInternalModifierMask lock_mask,
1865                                  HildonIMInternalModifierMask sticky_mask,
1866                                  gboolean was_press_and_release)
1867 {
1868 #ifdef MAEMO_CHANGES
1869   HildonGtkInputMode input_mode;
1870   g_object_get(self, "hildon-input-mode", &input_mode, NULL);
1871
1872   /* Locking Fn is disabled in TELE and NUMERIC */
1873   if (!(input_mode & HILDON_GTK_INPUT_MODE_ALPHA) &&
1874       !(input_mode & HILDON_GTK_INPUT_MODE_HEXA)  &&
1875       ((input_mode & HILDON_GTK_INPUT_MODE_TELE) || 
1876        (input_mode & HILDON_GTK_INPUT_MODE_NUMERIC)))
1877   {
1878     if (*mask & lock_mask)
1879     {
1880       /* already locked, remove lock and set it to sticky */
1881       *mask &= ~(lock_mask | sticky_mask);
1882       *mask |= sticky_mask;
1883     }
1884     else if (*mask & sticky_mask)
1885     {
1886       /* the key is already sticky, it's fine */
1887     }
1888     else if (was_press_and_release)
1889     {
1890       /* Pressing the key for the first time stickies the key for one character,
1891        * but only if no characters were entered while holding the key down */
1892       *mask |= sticky_mask;
1893     }
1894     return;
1895   }
1896 #endif
1897   if (*mask & lock_mask)
1898   {
1899     /* Pressing the key while already locked clears the state */
1900     if (lock_mask & HILDON_IM_SHIFT_LOCK_MASK)
1901       hildon_im_context_send_command(self, HILDON_IM_SHIFT_UNLOCKED);
1902     else if (lock_mask & HILDON_IM_LEVEL_LOCK_MASK)
1903       hildon_im_context_send_command(self, HILDON_IM_MOD_UNLOCKED);    
1904     
1905     *mask &= ~(lock_mask | sticky_mask);
1906   }
1907   else if (*mask & sticky_mask)
1908   {
1909     /* When the key is already sticky, a second press locks the key */
1910     *mask |= lock_mask;
1911
1912     if (lock_mask & HILDON_IM_SHIFT_LOCK_MASK)
1913       hildon_im_context_send_command(self, HILDON_IM_SHIFT_LOCKED);
1914     else if (lock_mask & HILDON_IM_LEVEL_LOCK_MASK)
1915       hildon_im_context_send_command(self, HILDON_IM_MOD_LOCKED);
1916   }
1917   else if (was_press_and_release)
1918   {
1919     /* Pressing the key for the first time stickies the key for one character,
1920      * but only if no characters were entered while holding the key down */
1921     *mask |= sticky_mask;
1922     if (sticky_mask & HILDON_IM_SHIFT_STICKY_MASK)
1923       hildon_im_context_send_command (self, HILDON_IM_SHIFT_STICKY);
1924     else if (sticky_mask & HILDON_IM_LEVEL_STICKY_MASK)
1925       hildon_im_context_send_command (self, HILDON_IM_MOD_STICKY);
1926   }
1927   
1928 }
1929
1930 static void
1931 perform_level_translation (GdkEventKey *event, GdkModifierType state)
1932 {
1933   guint translated_keyval;
1934
1935   gdk_keymap_translate_keyboard_state(gdk_keymap_get_default(),
1936                                       event->hardware_keycode,
1937                                       state,
1938                                       event->group,
1939                                       &translated_keyval,
1940                                       NULL, NULL, NULL);
1941   event->keyval = translated_keyval;
1942 }
1943
1944 static void
1945 reset_shift_and_level_keys_if_needed (HildonIMContext *context, GdkEventKey *event)
1946 {
1947   if (event->is_modifier)
1948     return;
1949
1950   /* If not locked, pressing any character resets shift state */
1951   if (event->keyval != GDK_Shift_L && event->keyval != GDK_Shift_R &&
1952       (context->mask & HILDON_IM_SHIFT_LOCK_MASK) == 0)
1953   {
1954     context->mask &= ~HILDON_IM_SHIFT_STICKY_MASK;
1955     hildon_im_context_send_command (context, HILDON_IM_SHIFT_UNSTICKY);
1956   }
1957   /* If not locked, pressing any character resets level state */
1958   if (event->keyval != LEVEL_KEY && (context->mask & HILDON_IM_LEVEL_LOCK_MASK) == 0)
1959   {
1960     context->mask &= ~HILDON_IM_LEVEL_STICKY_MASK;
1961     hildon_im_context_send_command (context, HILDON_IM_MOD_UNSTICKY);
1962   }
1963 }
1964
1965 static gboolean
1966 key_released (HildonIMContext *context, GdkEventKey *event, guint last_keyval)
1967 {
1968   gboolean level_key_is_sticky = context->mask & HILDON_IM_LEVEL_STICKY_MASK;
1969   gboolean level_key_is_locked = context->mask & HILDON_IM_LEVEL_LOCK_MASK;
1970   gboolean level_key_is_down = event->state & LEVEL_KEY_MOD_MASK;
1971
1972   if ((context->long_press_last_key_event != NULL) &&
1973       (event->hardware_keycode ==
1974        context->long_press_last_key_event->hardware_keycode))
1975   {
1976     hildon_im_context_abort_long_press (context);
1977     gdk_event_free ((GdkEvent *) context->long_press_last_key_event);
1978     context->long_press_last_key_event = NULL;
1979   }
1980
1981   if (event->keyval == COMPOSE_KEY)
1982       context->mask &= ~HILDON_IM_COMPOSE_MASK;
1983
1984   if (event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R)
1985   {
1986     if (! context->last_was_shift_backspace)
1987       hildon_im_context_set_mask_state(context,
1988                                        &context->mask,
1989                                        HILDON_IM_SHIFT_LOCK_MASK,
1990                                        HILDON_IM_SHIFT_STICKY_MASK,
1991                                        last_keyval == GDK_Shift_L || last_keyval == GDK_Shift_R);
1992     else
1993       context->last_was_shift_backspace = FALSE;
1994   }
1995   else if (event->keyval == LEVEL_KEY)
1996   {
1997     hildon_im_context_set_mask_state(context,
1998                                      &context->mask,
1999                                      HILDON_IM_LEVEL_LOCK_MASK,
2000                                      HILDON_IM_LEVEL_STICKY_MASK,
2001                                      last_keyval == LEVEL_KEY);
2002   }
2003
2004   if (level_key_is_sticky || level_key_is_locked || level_key_is_down)
2005   {
2006     GdkModifierType state = LEVEL_KEY_MOD_MASK;
2007
2008     if ((context->mask & HILDON_IM_SHIFT_LOCK_MASK) != 0 ||
2009         (context->mask & HILDON_IM_SHIFT_STICKY_MASK) != 0)
2010     {
2011       state |= GDK_SHIFT_MASK;
2012     }
2013
2014     perform_level_translation (event, state);
2015
2016     if (event->keyval == COMPOSE_KEY)
2017       context->mask &= ~HILDON_IM_COMPOSE_MASK;
2018   }
2019
2020   hildon_im_context_check_sentence_start (context);
2021
2022   hildon_im_context_send_key_event(context, event->type, event->state,
2023                                    event->keyval, event->hardware_keycode);
2024
2025   reset_shift_and_level_keys_if_needed (context, event);
2026
2027   return FALSE;
2028 }
2029
2030 static void
2031 hildon_im_context_invert_case (GdkEventKey *event)
2032 {
2033   guint lower, upper;
2034
2035   gdk_keyval_convert_case (event->keyval, &lower, &upper);
2036
2037   if (gdk_keyval_is_upper (event->keyval))
2038   {
2039     event->keyval = lower;
2040   }
2041   else
2042   {
2043     event->keyval = upper;
2044   }
2045 }
2046
2047 static void
2048 perform_shift_translation (GdkEventKey *event, GdkModifierType state)
2049 {
2050   guint lower, upper;
2051
2052   gdk_keyval_convert_case(event->keyval, &lower, &upper);
2053   /* Simulate shift key being held down in sticky state for non-printables  */
2054   if (lower == upper)
2055   {
2056     gdk_keymap_translate_keyboard_state(gdk_keymap_get_default(),
2057                                         event->hardware_keycode,
2058                                         state,
2059                                         event->group,
2060                                         &event->keyval,
2061                                         NULL, NULL, NULL);
2062   }
2063   /* For printable characters sticky shift negates the case,
2064      including any autocapitalization changes */
2065   else
2066   {
2067     hildon_im_context_invert_case (event);
2068   }
2069 }
2070
2071 static gboolean
2072 process_enter_key (HildonIMContext *context, GdkEventKey *event)
2073 {
2074   hildon_im_context_send_key_event(context, event->type, event->state,
2075                                      event->keyval, event->hardware_keycode);
2076
2077   /* Enter advances focus as if tab was pressed */
2078   if (event->keyval == GDK_KP_Enter || event->keyval == GDK_ISO_Enter)
2079   {
2080     if (g_type_class_peek(GTK_TYPE_ENTRY) != NULL)
2081     {
2082       if (g_signal_handler_find(context->client_gtk_widget,
2083                                 G_SIGNAL_MATCH_ID,
2084                                 g_signal_lookup("activate", GTK_TYPE_ENTRY),
2085                                 0, NULL, NULL, NULL))
2086         return FALSE;
2087     }
2088
2089     if (GTK_IS_ENTRY(context->client_gtk_widget) &&
2090         !gtk_entry_get_activates_default(GTK_ENTRY(context->client_gtk_widget)))
2091     {
2092       hildon_im_gtk_focus_next_text_widget(context->client_gtk_widget,
2093                                            GTK_DIR_TAB_FORWARD);
2094       return TRUE;
2095     }
2096
2097     return FALSE;
2098   }
2099
2100   return TRUE;
2101 }
2102
2103 static gboolean
2104 client_has_selection (HildonIMContext *context)
2105 {
2106   if (GTK_IS_TEXT_VIEW (context->client_gtk_widget))
2107   {
2108     GtkTextBuffer *buffer;
2109     buffer = get_buffer (context->client_gtk_widget);
2110     return gtk_text_buffer_get_has_selection (buffer);
2111   }
2112   if (GTK_IS_EDITABLE (context->client_gtk_widget))
2113   {
2114     return gtk_editable_get_selection_bounds (GTK_EDITABLE (context->client_gtk_widget),
2115                                               NULL, NULL);
2116   }
2117   return FALSE;
2118 }
2119
2120 static gboolean
2121 insert_text (HildonIMContext *context, gchar *text, gint offset)
2122 {
2123   if (text == NULL || !strlen (text))
2124     return FALSE;
2125
2126   if (GTK_IS_EDITABLE (context->client_gtk_widget))
2127   {
2128     gint cursor = gtk_editable_get_position (GTK_EDITABLE (context->client_gtk_widget));
2129     cursor += offset;
2130     gtk_editable_insert_text (GTK_EDITABLE (context->client_gtk_widget),
2131                               text,
2132                               -1,
2133                               &cursor);
2134   }
2135   else if (GTK_IS_TEXT_VIEW (context->client_gtk_widget))
2136   {
2137     GtkTextIter iter;
2138     GtkTextBuffer *buffer = get_buffer(context->client_gtk_widget);
2139     if (buffer == NULL)
2140       return FALSE;
2141     gtk_text_buffer_get_iter_at_mark (buffer,
2142                                       &iter,
2143                                       gtk_text_buffer_get_insert(buffer));
2144     gtk_text_iter_forward_chars (&iter, offset);
2145     gtk_text_buffer_insert (buffer, &iter, text, -1);
2146   }
2147
2148   return TRUE;
2149 }
2150
2151 static guint32
2152 compose_character (HildonIMContext *context, guint32 character, guint32 combining_character)
2153 {
2154   guint32 composed_char = character, tmp_char;
2155   gchar combined[12], *composed;
2156   gint base_length, combinator_length;
2157
2158   base_length = g_unichar_to_utf8(character, combined);
2159   combinator_length = g_unichar_to_utf8(combining_character, &combined[base_length]);
2160
2161   composed = g_utf8_normalize(combined,
2162                               base_length + combinator_length,
2163                               G_NORMALIZE_DEFAULT_COMPOSE);
2164
2165   /* Prevent composing of characters that are valid,
2166    * but not available in the font used */
2167   tmp_char = g_utf8_get_char (composed);
2168   if (hildon_im_context_font_has_char(context, tmp_char))
2169     composed_char = tmp_char;
2170
2171   g_free(composed);
2172
2173   context->combining_char = 0;
2174
2175   return composed_char;
2176 }
2177
2178 static guint32
2179 get_representation_for_dead_character (GdkEventKey *event, guint32 dead_character)
2180 {
2181   gint32 last;
2182   last = dead_key_to_unicode_combining_character (event->keyval);
2183
2184   if ((last == dead_character) || event->keyval == GDK_space)
2185     return combining_character_to_unicode (dead_character);
2186
2187   return gdk_keyval_to_unicode (event->keyval);
2188 }
2189
2190 static void
2191 hildon_im_context_delete_penultimate_char (HildonIMContext *self)
2192 {
2193   if (GTK_IS_TEXT_VIEW (self->client_gtk_widget))
2194   {
2195     GtkTextBuffer *buffer;
2196     GtkTextIter iter1, iter2;
2197
2198     buffer = get_buffer (self->client_gtk_widget);
2199     gtk_text_buffer_get_iter_at_mark (buffer,
2200                                       &iter1,
2201                                       gtk_text_buffer_get_insert (buffer));
2202     gtk_text_iter_backward_char (&iter1);
2203     iter2 = iter1;
2204     gtk_text_iter_backward_char (&iter1);
2205     gtk_text_buffer_delete (buffer,
2206                             &iter1, &iter2);
2207   }
2208   else if (GTK_IS_EDITABLE(self->client_gtk_widget))
2209   {
2210     gint pos;
2211
2212     pos = gtk_editable_get_position (GTK_EDITABLE (self->client_gtk_widget));
2213     gtk_editable_delete_text (GTK_EDITABLE(self->client_gtk_widget),
2214                               pos - 2,
2215                               pos - 1);
2216   }
2217   else
2218   {
2219     hildon_im_context_send_fake_key (GDK_Left, TRUE);
2220     hildon_im_context_send_fake_key (GDK_Left, FALSE);
2221
2222     hildon_im_context_send_fake_key (GDK_BackSpace, TRUE);
2223     hildon_im_context_send_fake_key (GDK_BackSpace, FALSE);
2224
2225     hildon_im_context_send_fake_key (GDK_Right, TRUE);
2226     hildon_im_context_send_fake_key (GDK_Right, FALSE);
2227   }
2228 }
2229
2230 static gboolean
2231 hildon_im_context_on_long_press_timeout (gpointer user_data)
2232 {
2233   HildonIMContext *self = HILDON_IM_CONTEXT (user_data);
2234   HildonIMInternalModifierMask mask_backup;
2235   gint cpos1 = 0, cpos2 = 0;
2236
2237   mask_backup = self->mask;
2238
2239   if ((self->mask & HILDON_IM_LEVEL_LOCK_MASK) != 0)
2240   {
2241     self->mask &= ~ (HILDON_IM_LEVEL_LOCK_MASK | HILDON_IM_LEVEL_STICKY_MASK);
2242
2243     if ( (self->mask & HILDON_IM_SHIFT_STICKY_MASK) != 0)
2244     {
2245       perform_level_translation (self->long_press_last_key_event,
2246                                  GDK_SHIFT_MASK);
2247       self->mask &= ~HILDON_IM_SHIFT_STICKY_MASK;
2248     }
2249     else
2250     {
2251       perform_level_translation (self->long_press_last_key_event, 0);
2252     }
2253   }
2254   else
2255   {
2256     self->mask |= HILDON_IM_LEVEL_STICKY_MASK;
2257   }
2258
2259   gtk_im_context_get_surrounding (GTK_IM_CONTEXT (self), NULL, &cpos1);
2260
2261   self->enable_long_press = FALSE;
2262   key_pressed (self, self->long_press_last_key_event);
2263   self->enable_long_press = TRUE;
2264
2265   gtk_im_context_get_surrounding (GTK_IM_CONTEXT (self), NULL, &cpos2);
2266
2267   if ( (cpos2 > cpos1) ||
2268        (! GTK_IS_EDITABLE (self->client_gtk_widget)) ||
2269        (! GTK_IS_TEXT_VIEW (self->client_gtk_widget)) )
2270     {
2271       hildon_im_context_delete_penultimate_char (self);
2272     }
2273
2274   self->mask = mask_backup;
2275
2276   self->long_press_timeout_src_id = 0;
2277
2278   return FALSE;
2279 }
2280
2281 static void
2282 hildon_im_context_abort_long_press (HildonIMContext *context)
2283 {
2284   if (context->long_press_timeout_src_id != 0)
2285   {
2286     g_source_remove (context->long_press_timeout_src_id);
2287     context->long_press_timeout_src_id = 0;
2288   }
2289 }
2290
2291 static gboolean
2292 key_pressed (HildonIMContext *context, GdkEventKey *event)
2293 {
2294 #ifdef MAEMO_CHANGES
2295   HildonGtkInputMode input_mode, default_input_mode;
2296 #endif
2297
2298   guint32 c = 0;
2299
2300   /* We manually lower the case because when shift is down
2301    * the event will be upper case and that will be
2302    * inconsistent with the rest of the shift states*/
2303   event->keyval = gdk_keyval_to_lower (event->keyval);
2304
2305   gboolean is_suggesting_autocompleted_word;
2306
2307   gboolean shift_key_is_down = event->state & GDK_SHIFT_MASK;
2308   gboolean shift_key_is_locked = context->mask & HILDON_IM_SHIFT_LOCK_MASK;
2309   gboolean shift_key_is_sticky = context->mask & HILDON_IM_SHIFT_STICKY_MASK;
2310
2311   gboolean level_key_is_sticky = context->mask & HILDON_IM_LEVEL_STICKY_MASK;
2312   gboolean level_key_is_locked = context->mask & HILDON_IM_LEVEL_LOCK_MASK;
2313   gboolean level_key_is_down = event->state & LEVEL_KEY_MOD_MASK;
2314
2315   gboolean enter_key_is_down = event->keyval == GDK_Return   ||
2316                                event->keyval == GDK_KP_Enter ||
2317                                event->keyval == GDK_ISO_Enter;
2318
2319   gboolean ctrl_key_is_down = event->state & GDK_CONTROL_MASK;
2320
2321   gboolean tab_key_is_down = event->keyval == GDK_Tab;
2322
2323   gboolean invert_level_behavior = FALSE;
2324   
2325   GdkModifierType translation_state = 0;
2326
2327   /* avoid key repeating when a long-press is in course  */
2328   if ((context->enable_long_press) &&
2329       (context->long_press_last_key_event != NULL))
2330   {
2331     if (event->hardware_keycode !=
2332         context->long_press_last_key_event->hardware_keycode)
2333     {
2334       hildon_im_context_abort_long_press (context);
2335     }
2336     else
2337     {
2338       return TRUE;
2339     }
2340   }
2341
2342   if (event->keyval == GDK_Delete)
2343     if (hildon_im_context_do_del (context))
2344       return TRUE;
2345
2346   is_suggesting_autocompleted_word = context->preedit_buffer != NULL &&
2347                                      context->preedit_buffer->len != 0;
2348
2349   if (event->keyval == COMPOSE_KEY)
2350     context->mask |= HILDON_IM_COMPOSE_MASK;
2351
2352   if (ctrl_key_is_down)
2353   {
2354     hildon_im_context_send_key_event(context, event->type, event->state,
2355                                     event->keyval, event->hardware_keycode);
2356     return FALSE;
2357   }
2358
2359   /* word completion manipulation */
2360   if (is_suggesting_autocompleted_word)
2361   {
2362     if (event->keyval == GDK_Right)
2363     {
2364       hildon_im_context_commit_preedit_data(context);
2365       return TRUE;
2366     }
2367     else
2368     {
2369       set_preedit_buffer(context, NULL);
2370       context->committed_preedit = FALSE;
2371       
2372       if (event->keyval == GDK_BackSpace || event->keyval == GDK_Left)
2373       {
2374         return TRUE;
2375       }
2376     }
2377   }
2378
2379   /* The IM determines the action for the return and enter keys */
2380   if (enter_key_is_down)
2381   {
2382     context->committed_preedit = FALSE;
2383     return process_enter_key (context, event);
2384   }
2385
2386   if (tab_key_is_down && GTK_IS_ENTRY (context->client_gtk_widget))
2387   {
2388     hildon_im_gtk_focus_next_text_widget(context->client_gtk_widget,
2389                                          ctrl_key_is_down ? GTK_DIR_TAB_FORWARD :
2390                                                             GTK_DIR_TAB_BACKWARD);
2391     context->committed_preedit = FALSE;
2392     return TRUE;
2393   }
2394
2395 #ifdef MAEMO_CHANGES
2396   g_object_get(context, "hildon-input-mode", &input_mode, NULL);
2397   g_object_get(context, "hildon-input-default", &default_input_mode, NULL);
2398   /* If the input mode is TELE, the behavior of the level key is inverted */
2399   gboolean should_invert = default_input_mode == HILDON_GTK_INPUT_MODE_NUMERIC;
2400   if (!should_invert)
2401   {
2402     should_invert = (input_mode & HILDON_GTK_INPUT_MODE_ALPHA) == 0  &&
2403                     (input_mode & HILDON_GTK_INPUT_MODE_HEXA)  == 0  &&
2404                     ((input_mode & HILDON_GTK_INPUT_MODE_TELE) ||
2405                     (input_mode & HILDON_GTK_INPUT_MODE_SPECIAL));
2406   }
2407   if ((context->options & HILDON_IM_AUTOLEVEL_NUMERIC &&
2408       (input_mode & HILDON_GTK_INPUT_MODE_FULL) == HILDON_GTK_INPUT_MODE_NUMERIC)
2409       || should_invert)
2410   {
2411     invert_level_behavior = TRUE;
2412   }
2413 #endif
2414
2415   if (context->options & HILDON_IM_LOCK_LEVEL)
2416   {
2417     event->keyval = hildon_im_context_get_event_keyval_for_level(context,
2418                                                                  event,
2419                                                                  LOCKABLE_LEVEL);
2420   }
2421
2422
2423   if (shift_key_is_sticky || shift_key_is_locked || shift_key_is_down)
2424   {
2425     translation_state |= GDK_SHIFT_MASK;
2426   }
2427
2428   /* When the level key is in sticky or locked state, translate the
2429    * keyboard state as if that level key was being held down. */
2430   if (level_key_is_sticky || level_key_is_locked || level_key_is_down)
2431   {
2432     /* When the level key is in sticky or locked state,  and we're not
2433      * in numeric/tele mode, translate the keyboard