Git init
[framework/uifw/isf.git] / ism / extras / gtk2_immodule / gtkimcontextscim.cpp
1 /** @file gtkimcontextscim.cpp
2  *  @brief immodule for GTK2.
3  */
4 /* ISF is based on SCIM 1.4.7 and extended for supporting more mobile fitable. */
5
6 /*
7  * Smart Common Input Method
8  *
9  * Copyright (c) 2002-2005 James Su <suzhe@tsinghua.org.cn>
10  *
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
14  * License as published by the Free Software Foundation; either
15  * version 2 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with this program; if not, write to the
24  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
25  * Boston, MA  02111-1307  USA
26  *
27  * $Id: gtkimcontextscim.cpp,v 1.170.2.13 2007/06/16 06:23:38 suzhe Exp $
28  */
29
30 #define Uses_SCIM_DEBUG
31 #define Uses_SCIM_BACKEND
32 #define Uses_SCIM_IMENGINE
33 #define Uses_SCIM_IMENGINE_MODULE
34 #define Uses_SCIM_HELPER_MODULE
35 #define Uses_SCIM_CONFIG
36 #define Uses_SCIM_CONFIG_MODULE
37 #define Uses_SCIM_CONFIG_PATH
38 #define Uses_SCIM_TRANSACTION
39 #define Uses_SCIM_HOTKEY
40 #define Uses_SCIM_PANEL_CLIENT
41 #define Uses_C_STRING
42 #define Uses_C_STDIO
43 #define Uses_C_STDLIB
44 #define Uses_STL_IOSTREAM
45 #define Uses_SCIM_UTILITY
46
47 #include <sys/types.h>
48 #include <sys/wait.h>
49 #include <sys/time.h>
50 #include <unistd.h>
51 #include <gtk/gtk.h>
52 #include <gdk/gdk.h>
53 #include <gdk/gdkkeysyms.h>
54
55 #undef GDK_WINDOWING_X11
56 #ifdef GDK_WINDOWING_X11
57 #include <X11/X.h>
58 #include <X11/Xlib.h>
59 #include <X11/keysym.h>
60 #include <gdk/gdkx.h>
61 #endif
62
63 #include <sys/times.h>
64 static struct timeval _scim_start;
65
66 #include "scim_private.h"
67 #include "scim.h"
68
69 #ifdef GDK_WINDOWING_X11
70 #include "scim_x11_utils.h"
71 #endif
72
73 using namespace scim;
74
75 #include "gtkimcontextscim.h"
76
77 //#define USING_ISF_MAINWINDOW_AUTOSCROLL
78
79
80 #define GTK_TYPE_IM_CONTEXT_SCIM             _gtk_type_im_context_scim
81 #define GTK_IM_CONTEXT_SCIM(obj)             (GTK_CHECK_CAST ((obj), GTK_TYPE_IM_CONTEXT_SCIM, GtkIMContextSCIM))
82 #define GTK_IM_CONTEXT_SCIM_CLASS(klass)     (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_IM_CONTEXT_SCIM, GtkIMContextSCIMClass))
83 #define GTK_IS_IM_CONTEXT_SCIM(obj)          (GTK_CHECK_TYPE ((obj), GTK_TYPE_IM_CONTEXT_SCIM))
84 #define GTK_IS_IM_CONTEXT_SCIM_CLASS(klass)  (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_IM_CONTEXT_SCIM))
85 #define GTK_IM_CONTEXT_SCIM_GET_CLASS(obj)   (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_IM_CONTEXT_SCIM, GtkIMContextSCIMClass))
86
87 #define SCIM_CONFIG_FRONTEND_GTK_IMMODULE_USE_KEY_SNOOPER  "/FrontEnd/GtkIMModule/UseKeySnooper"
88 #define ISE_KEY_FLUSH 0x8001
89 #ifdef ISF_PROF
90 static void gettime (const char* str)
91 {
92     struct  tms tiks_buf;
93     double  clock_tiks = (double)sysconf (_SC_CLK_TCK);
94     clock_t times_tiks = times (&tiks_buf);
95     double  times_secs = (double)times_tiks / clock_tiks;
96     double  utime      = (double)tiks_buf.tms_utime / clock_tiks;
97     double  stime      = (double)tiks_buf.tms_stime / clock_tiks;
98     printf ("Times:   %3f \t User:   %.3f \t System:   %.3f \n", (double)times_secs, (double)utime , (double)stime);
99 }
100 #endif
101
102 static void print_time (const char *str_info)
103 {
104 #ifdef ISF_PROF
105     struct timeval current_time;
106     int            used_time = 0;
107
108     gettimeofday (&current_time, NULL);
109     used_time = 1000000 * (current_time.tv_sec - _scim_start.tv_sec) + current_time.tv_usec - _scim_start.tv_usec;
110     printf (mzc_red "%s : %d usec" mzc_normal ".\n", str_info, used_time);
111     //printf ("Current time : %d sec %d usec\n", current_time.tv_sec, current_time.tv_usec);
112     gettime (str_info);
113 #endif
114 }
115
116 /* Typedef */
117 struct _GtkIMContextSCIMImpl
118 {
119     GtkIMContextSCIM        *parent;
120     IMEngineInstancePointer  si;
121     GdkWindow               *client_window;
122     WideString               preedit_string;
123     AttributeList            preedit_attrlist;
124     gint                     preedit_caret;
125     gint                     cursor_x;
126     gint                     cursor_y;
127     gint                     cursor_top_y;
128     gboolean                 use_preedit;
129     bool                     is_on;
130     bool                     shared_si;
131     bool                     preedit_started;
132     bool                     preedit_updating;
133     bool                     need_commit_preedit;
134
135     GtkIMContextSCIMImpl    *next;
136 };
137
138 /* Input Context handling functions. */
139 static GtkIMContextSCIMImpl * new_ic_impl               (GtkIMContextSCIM       *parent);
140 static void                   delete_ic_impl            (GtkIMContextSCIMImpl   *impl);
141 static void                   delete_all_ic_impl        ();
142
143 static GtkIMContextSCIM     * find_ic                   (int                     siid);
144
145 /* Methods declaration */
146 static void     gtk_im_context_scim_class_init          (GtkIMContextSCIMClass  *klass,
147                                                          gpointer               *klass_data);
148 static void     gtk_im_context_scim_init                (GtkIMContextSCIM       *context_scim,
149                                                          GtkIMContextSCIMClass  *klass);
150 static void     gtk_im_context_scim_finalize            (GObject                *obj);
151 static void     gtk_im_context_scim_finalize_partial    (GtkIMContextSCIM       *context_scim);
152 static void     gtk_im_context_scim_set_client_window   (GtkIMContext           *context,
153                                                          GdkWindow              *client_window);
154 static gboolean gtk_im_context_scim_filter_keypress     (GtkIMContext           *context,
155                                                          GdkEventKey            *key);
156 static void     gtk_im_context_scim_reset               (GtkIMContext           *context);
157 static void     gtk_im_context_scim_focus_in            (GtkIMContext           *context);
158 static void     gtk_im_context_scim_focus_out           (GtkIMContext           *context);
159 static void     gtk_im_context_scim_set_cursor_location (GtkIMContext           *context,
160                                                          GdkRectangle           *area);
161 static void     gtk_im_context_scim_set_use_preedit     (GtkIMContext           *context,
162                                                          gboolean                use_preedit);
163 static void     gtk_im_context_scim_get_preedit_string  (GtkIMContext           *context,
164                                                          gchar                 **str,
165                                                          PangoAttrList         **attrs,
166                                                          gint                   *cursor_pos);
167
168 static gboolean gtk_scim_key_snooper                    (GtkWidget              *grab_widget,
169                                                          GdkEventKey            *event,
170                                                          gpointer                data);
171
172 static void     gtk_im_slave_commit_cb                  (GtkIMContext           *context,
173                                                          const char             *str,
174                                                          GtkIMContextSCIM       *context_scim);
175
176 /* private functions */
177 static void     panel_slot_reload_config                (int                     context);
178 static void     panel_slot_exit                         (int                     context);
179 static void     panel_slot_update_lookup_table_page_size(int                     context,
180                                                          int                     page_size);
181 static void     panel_slot_lookup_table_page_up         (int                     context);
182 static void     panel_slot_lookup_table_page_down       (int                     context);
183 static void     panel_slot_trigger_property             (int                     context,
184                                                          const String           &property);
185 static void     panel_slot_process_helper_event         (int                     context,
186                                                          const String           &target_uuid,
187                                                          const String           &helper_uuid,
188                                                          const Transaction      &trans);
189 static void     panel_slot_move_preedit_caret           (int                     context,
190                                                          int                     caret_pos);
191 static void     panel_slot_select_candidate             (int                     context,
192                                                          int                     cand_index);
193 static void     panel_slot_process_key_event            (int                     context,
194                                                          const KeyEvent         &key);
195 static void     panel_slot_commit_string                (int                     context,
196                                                          const WideString       &wstr);
197 static void     panel_slot_forward_key_event            (int                     context,
198                                                          const KeyEvent         &key);
199 static void     panel_slot_request_help                 (int                     context);
200 static void     panel_slot_request_factory_menu         (int                     context);
201 static void     panel_slot_change_factory               (int                     context,
202                                                          const String           &uuid);
203 static void     panel_slot_reset_keyboard_ise           (int                     context);
204 static void     panel_slot_show_preedit_string          (int                     context);
205 static void     panel_slot_hide_preedit_string          (int                     context);
206 static void     panel_slot_update_preedit_string        (int                     context,
207                                                          const WideString       &str,
208                                                          const AttributeList    &attrs);
209
210 static void     panel_req_focus_in                      (GtkIMContextSCIM       *ic);
211 static void     panel_req_update_screen                 (GtkIMContextSCIM       *ic);
212 static void     panel_req_update_factory_info           (GtkIMContextSCIM       *ic);
213 static void     panel_req_update_spot_location          (GtkIMContextSCIM       *ic);
214 static void     panel_req_show_help                     (GtkIMContextSCIM       *ic);
215 static void     panel_req_show_factory_menu             (GtkIMContextSCIM       *ic);
216
217 /* Panel iochannel handler*/
218 static bool     panel_initialize                        ();
219 static void     panel_finalize                          ();
220 static gboolean panel_iochannel_handler                 (GIOChannel             *source,
221                                                          GIOCondition            condition,
222                                                          gpointer                user_data);
223
224 /* utility functions */
225 static bool     filter_hotkeys                          (GtkIMContextSCIM       *ic,
226                                                          const KeyEvent         &key);
227 static void     turn_on_ic                              (GtkIMContextSCIM       *ic);
228 static void     turn_off_ic                             (GtkIMContextSCIM       *ic);
229
230 static void     set_ic_capabilities                     (GtkIMContextSCIM       *ic);
231
232 static KeyEvent keyevent_gdk_to_scim                    (const GtkIMContextSCIM *ic,
233                                                          const GdkEventKey      &gdkevent);
234
235 static GdkEventKey keyevent_scim_to_gdk                 (const GtkIMContextSCIM *ic,
236                                                          const KeyEvent         &scimkey, gboolean send_event);
237
238 static void     initialize                              (void);
239
240 static void     finalize                                (void);
241
242 static void     open_next_factory                       (GtkIMContextSCIM       *ic);
243 static void     open_previous_factory                   (GtkIMContextSCIM       *ic);
244 static void     open_specific_factory                   (GtkIMContextSCIM       *ic,
245                                                          const String           &uuid);
246
247 static void     attach_instance                         (const IMEngineInstancePointer &si);
248
249 /* slot functions */
250 static void     slot_show_preedit_string                (IMEngineInstanceBase   *si);
251 static void     slot_show_aux_string                    (IMEngineInstanceBase   *si);
252 static void     slot_show_lookup_table                  (IMEngineInstanceBase   *si);
253
254 static void     slot_hide_preedit_string                (IMEngineInstanceBase   *si);
255 static void     slot_hide_aux_string                    (IMEngineInstanceBase   *si);
256 static void     slot_hide_lookup_table                  (IMEngineInstanceBase   *si);
257
258 static void     slot_update_preedit_caret               (IMEngineInstanceBase   *si,
259                                                          int                     caret);
260 static void     slot_update_preedit_string              (IMEngineInstanceBase   *si,
261                                                          const WideString       &str,
262                                                          const AttributeList    &attrs);
263 static void     slot_update_aux_string                  (IMEngineInstanceBase   *si,
264                                                          const WideString       &str,
265                                                          const AttributeList    &attrs);
266 static void     slot_commit_string                      (IMEngineInstanceBase   *si,
267                                                          const WideString       &str);
268 static void     slot_forward_key_event                  (IMEngineInstanceBase   *si,
269                                                          const KeyEvent         &key);
270 static void     slot_update_lookup_table                (IMEngineInstanceBase   *si,
271                                                          const LookupTable      &table);
272
273 static void     slot_register_properties                (IMEngineInstanceBase   *si,
274                                                          const PropertyList     &properties);
275 static void     slot_update_property                    (IMEngineInstanceBase   *si,
276                                                          const Property         &property);
277 static void     slot_beep                               (IMEngineInstanceBase   *si);
278 static void     slot_start_helper                       (IMEngineInstanceBase   *si,
279                                                          const String           &helper_uuid);
280 static void     slot_stop_helper                        (IMEngineInstanceBase   *si,
281                                                          const String           &helper_uuid);
282 static void     slot_send_helper_event                  (IMEngineInstanceBase   *si,
283                                                          const String           &helper_uuid,
284                                                          const Transaction      &trans);
285 static bool     slot_get_surrounding_text               (IMEngineInstanceBase   *si,
286                                                          WideString             &text,
287                                                          int                    &cursor,
288                                                          int                     maxlen_before,
289                                                          int                     maxlen_after);
290 static bool     slot_delete_surrounding_text            (IMEngineInstanceBase   *si,
291                                                          int                     offset,
292                                                          int                     len);
293
294 static void     reload_config_callback                  (const ConfigPointer    &config);
295
296 static void     fallback_commit_string_cb               (IMEngineInstanceBase   *si,
297                                                          const WideString       &str);
298
299
300
301
302 /* Local variables declaration */
303 static String                                           _language;
304 static GtkIMContextSCIMImpl                            *_used_ic_impl_list          = 0;
305 static GtkIMContextSCIMImpl                            *_free_ic_impl_list          = 0;
306 static GtkIMContextSCIM                                *_ic_list                    = 0;
307
308 static GType                                            _gtk_type_im_context_scim   = 0;
309 static GObjectClass                                    *_parent_klass               = 0;
310
311 static KeyboardLayout                                   _keyboard_layout            = SCIM_KEYBOARD_Default;
312 static int                                              _valid_key_mask             = SCIM_KEY_AllMasks;
313
314 static FrontEndHotkeyMatcher                            _frontend_hotkey_matcher;
315 static IMEngineHotkeyMatcher                            _imengine_hotkey_matcher;
316
317 static IMEngineInstancePointer                          _default_instance;
318
319 static ConfigModule                                    *_config_module              = 0;
320 static ConfigPointer                                    _config;
321 static BackEndPointer                                   _backend;
322
323 static GtkIMContextSCIM                                *_focused_ic                 = 0;
324 static GtkWidget                                       *_focused_widget             = 0;
325
326 static bool                                             _scim_initialized           = false;
327
328 static GdkColor                                         _normal_bg;
329 static GdkColor                                         _normal_text;
330 static GdkColor                                         _active_bg;
331 static GdkColor                                         _active_text;
332
333 static gint                                             _snooper_id                 = 0;
334 static bool                                             _snooper_installed          = false;
335
336 static int                                              _instance_count             = 0;
337 static int                                              _context_count              = 0;
338
339 static IMEngineFactoryPointer                           _fallback_factory;
340 static IMEngineInstancePointer                          _fallback_instance;
341
342 static PanelClient                                      _panel_client;
343
344 static GIOChannel                                      *_panel_iochannel            = 0;
345 static guint                                            _panel_iochannel_read_source= 0;
346 static guint                                            _panel_iochannel_err_source = 0;
347 static guint                                            _panel_iochannel_hup_source = 0;
348
349 static bool                                             _on_the_spot                = true;
350 static bool                                             _shared_input_method        = false;
351 static bool                                             _use_key_snooper            = false;
352
353 static GdkColor                                        *_theme_color                = NULL;
354
355 // A hack to shutdown the immodule cleanly even if im_module_exit () is not called when exiting.
356 class FinalizeHandler
357 {
358 public:
359     FinalizeHandler () {
360         SCIM_DEBUG_FRONTEND(1) << "FinalizeHandler::FinalizeHandler ()\n";
361     }
362     ~FinalizeHandler () {
363         SCIM_DEBUG_FRONTEND(1) << "FinalizeHandler::~FinalizeHandler ()\n";
364         gtk_im_context_scim_shutdown ();
365     }
366 };
367
368 static FinalizeHandler                                  _finalize_handler;
369
370 /* Function Implementations */
371
372 static GtkIMContextSCIMImpl *
373 new_ic_impl (GtkIMContextSCIM *parent)
374 {
375     GtkIMContextSCIMImpl *impl = NULL;
376
377     if (_free_ic_impl_list != NULL) {
378         impl = _free_ic_impl_list;
379         _free_ic_impl_list = _free_ic_impl_list->next;
380     } else {
381         impl = new GtkIMContextSCIMImpl;
382         if (impl == NULL)
383             return NULL;
384     }
385
386     impl->next = _used_ic_impl_list;
387     _used_ic_impl_list = impl;
388
389     impl->parent = parent;
390
391     return impl;
392 }
393
394 static void
395 delete_ic_impl (GtkIMContextSCIMImpl   *impl)
396 {
397     GtkIMContextSCIMImpl *rec = _used_ic_impl_list, *last = 0;
398
399     for (; rec != 0; last = rec, rec = rec->next) {
400         if (rec == impl) {
401             if (last != 0)
402                 last->next = rec->next;
403             else
404                 _used_ic_impl_list = rec->next;
405
406             rec->next = _free_ic_impl_list;
407             _free_ic_impl_list = rec;
408
409             rec->parent = 0;
410             rec->si.reset ();
411             rec->client_window = 0;
412             rec->preedit_string = WideString ();
413             rec->preedit_attrlist.clear ();
414
415             return;
416         }
417     }
418 }
419
420
421 static void
422 delete_all_ic_impl ()
423 {
424     GtkIMContextSCIMImpl *it = _used_ic_impl_list;
425
426
427     while (it != 0) {
428         _used_ic_impl_list = it->next;
429         delete it;
430         it = _used_ic_impl_list;
431     }
432
433     it = _free_ic_impl_list;
434     while (it != 0) {
435         _free_ic_impl_list = it->next;
436         delete it;
437         it = _free_ic_impl_list;
438     }
439 }
440
441 static GtkIMContextSCIM *
442 find_ic (int id)
443 {
444     GtkIMContextSCIMImpl *rec = _used_ic_impl_list;
445
446     while (rec != 0) {
447         if (rec->parent && rec->parent->id == id)
448             return rec->parent;
449         rec = rec->next;
450     }
451
452     return 0;
453 }
454
455
456 /* Public functions */
457 /**
458  * gtk_im_context_scim_new
459  *
460  * This function will be called by gtk.
461  * Create a instance of type GtkIMContext.
462  *
463  * Return value: A pointer to the newly created GtkIMContextSCIM instance
464  *
465  **/
466 GtkIMContext *
467 gtk_im_context_scim_new (void)
468 {
469     print_time ("enter gtk_im_context_scim_new");
470     SCIM_DEBUG_FRONTEND(1) << "gtk_im_context_scim_new...\n";
471
472     GtkIMContextSCIM *result = NULL;
473
474     result = GTK_IM_CONTEXT_SCIM (g_object_new (GTK_TYPE_IM_CONTEXT_SCIM, NULL));
475
476     print_time ("exit gtk_im_context_scim_new");
477     return GTK_IM_CONTEXT (result);
478 }
479
480 /**
481  * gtk_im_context_scim_new
482  *
483  * Register the new type GtkIMContextSCIM to glib
484  *
485  **/
486 void
487 gtk_im_context_scim_register_type (GTypeModule *type_module)
488 {
489     gettimeofday (&_scim_start, NULL);
490     print_time ("enter gtk_im_context_scim_register_type");
491     static const GTypeInfo im_context_scim_info =
492     {
493         sizeof               (GtkIMContextSCIMClass),
494         (GBaseInitFunc)       NULL,
495         (GBaseFinalizeFunc)   NULL,
496         (GClassInitFunc)      gtk_im_context_scim_class_init,
497         NULL,                 /* class_finalize */
498         NULL,                 /* class_data */
499         sizeof               (GtkIMContextSCIM),
500         0,
501         (GtkObjectInitFunc)  gtk_im_context_scim_init,
502     };
503
504     SCIM_DEBUG_FRONTEND(1) << "gtk_im_context_scim_register_type...\n";
505
506     if (!_gtk_type_im_context_scim) {
507         _gtk_type_im_context_scim =
508             g_type_module_register_type (type_module,
509                                          GTK_TYPE_IM_CONTEXT,
510                                          "GtkIMContextSCIM",
511                                          &im_context_scim_info,
512                                          (GTypeFlags) 0);
513     }
514     print_time ("exit gtk_im_context_scim_register_type");
515 }
516
517 /**
518  * gtk_im_context_scim_shutdown
519  *
520  * It will be called when the scim im module is unloaded by gtk. It will do some cleanup
521  * job.
522  *
523  **/
524 void
525 gtk_im_context_scim_shutdown (void)
526 {
527     print_time ("enter gtk_im_context_scim_shutdown");
528     SCIM_DEBUG_FRONTEND(1) << "gtk_im_context_scim_shutdown...\n";
529
530     if (_scim_initialized) {
531         SCIM_DEBUG_FRONTEND(1) << "gtk_im_context_scim_shutdown: call finalize ()...\n";
532         finalize ();
533         _scim_initialized = false;
534     }
535     print_time ("exit gtk_im_context_scim_shutdown");
536 }
537
538 /* Private functions */
539 static void
540 gtk_im_context_scim_class_init (GtkIMContextSCIMClass *klass,
541                                 gpointer              *klass_data)
542 {
543     print_time ("enter gtk_im_context_scim_class_init");
544     SCIM_DEBUG_FRONTEND(1) << "gtk_im_context_scim_class_init...\n";
545
546     GtkIMContextClass *im_context_klass = GTK_IM_CONTEXT_CLASS (klass);
547     GObjectClass      *gobject_klass    = G_OBJECT_CLASS (klass);
548
549     _parent_klass = (GObjectClass *) g_type_class_peek_parent (klass);
550
551     im_context_klass->set_client_window   = gtk_im_context_scim_set_client_window;
552     im_context_klass->filter_keypress     = gtk_im_context_scim_filter_keypress;
553     im_context_klass->reset               = gtk_im_context_scim_reset;
554     im_context_klass->get_preedit_string  = gtk_im_context_scim_get_preedit_string;
555     im_context_klass->focus_in            = gtk_im_context_scim_focus_in;
556     im_context_klass->focus_out           = gtk_im_context_scim_focus_out;
557     im_context_klass->set_cursor_location = gtk_im_context_scim_set_cursor_location;
558     im_context_klass->set_use_preedit     = gtk_im_context_scim_set_use_preedit;
559     gobject_klass->finalize               = gtk_im_context_scim_finalize;
560
561     if (!_scim_initialized) {
562         initialize ();
563         _scim_initialized = true;
564     }
565     print_time ("exit gtk_im_context_scim_class_init");
566 }
567
568 static void
569 gtk_im_context_scim_init (GtkIMContextSCIM      *context_scim,
570                           GtkIMContextSCIMClass *klass)
571 {
572     print_time ("enter gtk_im_context_scim_init");
573     SCIM_DEBUG_FRONTEND(1) << "gtk_im_context_scim_init...\n";
574
575     context_scim->impl = NULL;
576
577     /* slave exists for using gtk+'s table based input method */
578     context_scim->slave = gtk_im_context_simple_new ();
579     g_signal_connect(G_OBJECT(context_scim->slave),
580                      "commit",
581                      G_CALLBACK(gtk_im_slave_commit_cb),
582                      context_scim);
583
584     if (_backend.null ()) return;
585
586     IMEngineInstancePointer si;
587
588     // Use the default instance if "shared input method" mode is enabled.
589     if (_shared_input_method && !_default_instance.null ()) {
590         si = _default_instance;
591         SCIM_DEBUG_FRONTEND(2) << "use default instance: " << si->get_id () << " " << si->get_factory_uuid () << "\n";
592     }
593
594     // Not in "shared input method" mode, or no default instance, create an instance.
595     if (si.null ()) {
596         IMEngineFactoryPointer factory = _backend->get_default_factory (_language, "UTF-8");
597         if (factory.null ()) return;
598         si = factory->create_instance ("UTF-8", _instance_count++);
599         if (si.null ()) return;
600         attach_instance (si);
601         SCIM_DEBUG_FRONTEND(2) << "create new instance: " << si->get_id () << " " << si->get_factory_uuid () << "\n";
602     }
603
604     // If "shared input method" mode is enabled, and there is no default instance,
605     // then store this instance as default one.
606     if (_shared_input_method && _default_instance.null ()) {
607         SCIM_DEBUG_FRONTEND(2) << "update default instance.\n";
608         _default_instance = si;
609     }
610
611     context_scim->impl = new_ic_impl (context_scim);
612     if (context_scim->impl == NULL)
613     {
614         std::cerr << "memory allocation failed in " << __FUNCTION__ << "\n";
615         return;
616     }
617
618     context_scim->impl->si = si;
619     context_scim->impl->client_window = NULL;
620     context_scim->impl->preedit_caret = 0;
621     context_scim->impl->cursor_x = 0;
622     context_scim->impl->cursor_y = 0;
623     context_scim->impl->cursor_top_y = 0;
624     context_scim->impl->is_on = FALSE;
625     context_scim->impl->shared_si = _shared_input_method;
626     context_scim->impl->use_preedit = _on_the_spot;
627     context_scim->impl->preedit_started = false;
628     context_scim->impl->preedit_updating = false;
629     context_scim->impl->need_commit_preedit = false;
630
631     if (_context_count == 0) _context_count = time (NULL);
632     context_scim->id = _context_count++;
633     if (!_ic_list)
634         context_scim->next = NULL;
635     else
636         context_scim->next = _ic_list;
637     _ic_list = context_scim;
638
639     if (_shared_input_method)
640         context_scim->impl->is_on = _config->read (String (SCIM_CONFIG_FRONTEND_IM_OPENED_BY_DEFAULT), context_scim->impl->is_on);
641
642     _panel_client.prepare (context_scim->id);
643     _panel_client.register_input_context (context_scim->id, si->get_factory_uuid ());
644     _panel_client.start_default_ise (context_scim->id);
645     set_ic_capabilities (context_scim);
646     _panel_client.send ();
647
648     SCIM_DEBUG_FRONTEND(2) << "input context created: id = " << context_scim->id << "\n";
649     print_time ("exit gtk_im_context_scim_init");
650 }
651
652 static void
653 gtk_im_context_scim_finalize_partial (GtkIMContextSCIM *context_scim)
654 {
655     SCIM_DEBUG_FRONTEND(1) << "gtk_im_context_scim_finalize_partial id=" << context_scim->id << "\n";
656
657     if (context_scim->impl) {
658         _panel_client.prepare (context_scim->id);
659
660         if (context_scim == _focused_ic)
661             context_scim->impl->si->focus_out ();
662
663         // Delete the instance.
664         // FIXME:
665         // In case the instance send out some helper event,
666         // and this context has been focused out,
667         // we need set the focused_ic to this context temporary.
668         GtkIMContextSCIM *old_focused = _focused_ic;
669         _focused_ic = context_scim;
670         context_scim->impl->si.reset ();
671         _focused_ic = old_focused;
672
673         if (context_scim == _focused_ic) {
674             _panel_client.turn_off (context_scim->id);
675             _panel_client.focus_out (context_scim->id);
676         }
677
678         _panel_client.remove_input_context (context_scim->id);
679         _panel_client.send ();
680
681         if (context_scim->impl->client_window)
682             g_object_unref (context_scim->impl->client_window);
683
684         delete_ic_impl (context_scim->impl);
685
686         context_scim->impl = 0;
687     }
688
689     if (context_scim == _focused_ic)
690         _focused_ic = 0;
691 }
692
693 static void
694 gtk_im_context_scim_finalize (GObject *obj)
695 {
696     print_time ("enter gtk_im_context_scim_finalize");
697     GtkIMContextSCIM *context_scim = GTK_IM_CONTEXT_SCIM (obj);
698
699     if (context_scim == NULL)
700         return;
701
702     SCIM_DEBUG_FRONTEND(1) << "gtk_im_context_scim_finalize id=" << context_scim->id << "\n";
703
704     if ((_ic_list != NULL))
705     {
706         if(context_scim->id != _ic_list->id) {
707             GtkIMContextSCIM * pre = _ic_list;
708             GtkIMContextSCIM * cur = _ic_list->next;
709             while (cur != NULL) {
710                 if (cur->id == context_scim->id) {
711                     pre->next = cur->next;
712                     break;
713                 }
714                 pre = cur;
715                 cur = cur->next;
716             }
717         } else
718             _ic_list = _ic_list->next;
719     }
720
721     if (_theme_color != NULL) {
722         gdk_color_free (_theme_color);
723         _theme_color = NULL;
724     }
725
726     g_signal_handlers_disconnect_by_func(context_scim->slave,
727                                          (void *)gtk_im_slave_commit_cb,
728                                          (void *)context_scim);
729     g_object_unref(context_scim->slave);
730
731     gtk_im_context_scim_finalize_partial (context_scim);
732
733     _parent_klass->finalize (obj);
734     print_time ("exit gtk_im_context_scim_finalize");
735 }
736
737 /**
738  * gtk_im_context_scim_set_client_window:
739  * @context: a #GtkIMContext
740  * @window:  the client window. This may be %NULL to indicate
741  *           that the previous client window no longer exists.
742  *
743  * This function will be called by gtk.
744  * Set the client window for the input context; this is the
745  * #GdkWindow in which the input appears. This window is
746  * used in order to correctly position status windows, and may
747  * also be used for purposes internal to the input method.
748  *
749  **/
750 static void
751 gtk_im_context_scim_set_client_window (GtkIMContext *context,
752                                        GdkWindow    *client_window)
753 {
754     print_time ("enter gtk_im_context_scim_set_client_window");
755     SCIM_DEBUG_FRONTEND(1) << "gtk_im_context_scim_set_client_window...\n";
756
757     GtkIMContextSCIM *context_scim = GTK_IM_CONTEXT_SCIM (context);
758
759     if (context_scim && context_scim->impl) {
760         if (client_window)
761             g_object_ref (client_window);
762
763         if (context_scim->impl->client_window)
764             g_object_unref (context_scim->impl->client_window);
765
766         context_scim->impl->client_window = client_window;
767     }
768     print_time ("exit gtk_im_context_scim_set_client_window");
769 }
770
771 /**
772  * gtk_im_context_scim_filter_keypress:
773  * @context: a #GtkIMContext
774  * @event: the key event
775  *
776  * This function will be called by gtk.
777  * Allow an input method to internally handle key press and release
778  * events. If this function returns %TRUE, then no further processing
779  * should be done for this key event.
780  *
781  * Return value: %TRUE if the input method handled the key event.
782  *
783  **/
784 static gboolean
785 gtk_im_context_scim_filter_keypress (GtkIMContext *context,
786                                      GdkEventKey  *event)
787 {
788     print_time ("enter gtk_im_context_scim_filter_keypress");
789     SCIM_DEBUG_FRONTEND(1) << "gtk_im_context_scim_filter_keypress...\n";
790
791     if (event->keyval == 255 && (event->keyval & GDK_SHIFT_MASK))
792         return (gboolean)TRUE;
793
794     GtkIMContextSCIM *context_scim = GTK_IM_CONTEXT_SCIM (context);
795
796     gboolean ret = FALSE;
797
798     if (context_scim) {
799         if (!_snooper_installed)
800             ret = gtk_scim_key_snooper (0, event, 0);
801
802         if (!ret && context_scim->slave)
803             ret = gtk_im_context_filter_keypress (context_scim->slave, event);
804     }
805
806     print_time ("exit gtk_im_context_scim_filter_keypress");
807     return ret;
808 }
809
810 /**
811  * gtk_im_context_scim_reset:
812  * @context: a #GtkIMContext
813  *
814  * This function will be called by gtk.
815  * Notify the input method that a change such as a change in cursor
816  * position has been made. This will typically cause the input
817  * method to clear the preedit state.
818  **/
819 static void
820 gtk_im_context_scim_reset (GtkIMContext *context)
821 {
822     print_time ("enter gtk_im_context_scim_reset");
823     SCIM_DEBUG_FRONTEND(1) << "gtk_im_context_scim_reset...\n";
824
825     GtkIMContextSCIM *context_scim = GTK_IM_CONTEXT_SCIM (context);
826
827     if (context_scim && context_scim->impl && context_scim == _focused_ic) {
828         WideString wstr = context_scim->impl->preedit_string;
829
830         _panel_client.prepare (context_scim->id);
831         context_scim->impl->si->reset ();
832         _panel_client.send ();
833
834         if (context_scim->impl->need_commit_preedit)
835         {
836             panel_slot_hide_preedit_string (context_scim->id);
837
838             if (wstr.length ())
839                 g_signal_emit_by_name (context_scim, "commit", utf8_wcstombs (wstr).c_str ());
840
841             _panel_client.prepare (context_scim->id);
842             _panel_client.reset_input_context (context_scim->id);
843             _panel_client.send ();
844         }
845     }
846     print_time ("exit gtk_im_context_scim_reset");
847 }
848
849 /**
850  * gtk_im_context_scim_focus_in:
851  * @context: a #GtkIMContext
852  *
853  * This function will be called by gtk.
854  * Notify the input method that the widget to which this
855  * input context corresponds has gained focus. The input method
856  * may, for example, change the displayed feedback to reflect
857  * this change.
858  **/
859 static void
860 gtk_im_context_scim_focus_in (GtkIMContext *context)
861 {
862     print_time ("enter gtk_im_context_scim_focus_in");
863     GtkIMContextSCIM *context_scim = GTK_IM_CONTEXT_SCIM (context);
864
865     if (context_scim)
866         SCIM_DEBUG_FRONTEND(1) << "gtk_im_context_scim_focus_in(" << context_scim->id << ")...\n";
867
868     if (_focused_ic) {
869         if (_focused_ic == context_scim) {
870             SCIM_DEBUG_FRONTEND(1) << "It's already focused.\n";
871             return;
872         }
873         SCIM_DEBUG_FRONTEND(1) << "Focus out previous IC first: " << _focused_ic->id << "\n";
874         gtk_im_context_scim_focus_out (GTK_IM_CONTEXT (_focused_ic));
875     }
876
877     // Only use key snooper when use_key_snooper option is enabled and a gtk main loop is running.
878     if (_use_key_snooper && !_snooper_installed && gtk_main_level () > 0) {
879         SCIM_DEBUG_FRONTEND(2) << "Install key snooper.\n";
880         _snooper_id = gtk_key_snooper_install ((GtkKeySnoopFunc)gtk_scim_key_snooper, NULL);
881         _snooper_installed = true;
882     }
883
884     bool need_cap = false;
885     bool need_reset = false;
886     bool need_reg = false;
887
888     if (context_scim && context_scim->impl) {
889         _focused_ic = context_scim;
890         _panel_client.prepare (context_scim->id);
891
892         // Handle the "Shared Input Method" mode.
893         if (_shared_input_method) {
894             SCIM_DEBUG_FRONTEND(2) << "shared input method.\n";
895             IMEngineFactoryPointer factory = _backend->get_default_factory (_language, "UTF-8");
896             if (!factory.null ()) {
897                 if (_default_instance.null () || _default_instance->get_factory_uuid () != factory->get_uuid ()) {
898                     _default_instance = factory->create_instance ("UTF-8", _default_instance.null () ? _instance_count++ : _default_instance->get_id ());
899                     attach_instance (_default_instance);
900                     SCIM_DEBUG_FRONTEND(2) << "create new default instance: " << _default_instance->get_id () << " " << _default_instance->get_factory_uuid () << "\n";
901                 }
902
903                 context_scim->impl->shared_si = true;
904                 context_scim->impl->si = _default_instance;
905
906                 context_scim->impl->is_on = _config->read (String (SCIM_CONFIG_FRONTEND_IM_OPENED_BY_DEFAULT), context_scim->impl->is_on);
907                 context_scim->impl->preedit_string.clear ();
908                 context_scim->impl->preedit_attrlist.clear ();
909                 context_scim->impl->preedit_caret = 0;
910                 context_scim->impl->preedit_started = false;
911                 need_cap = true;
912                 need_reset = true;
913                 need_reg = true;
914             }
915         } else if (context_scim->impl->shared_si) {
916             SCIM_DEBUG_FRONTEND(2) << "exit shared input method.\n";
917             IMEngineFactoryPointer factory = _backend->get_default_factory (_language, "UTF-8");
918             if (!factory.null ()) {
919                 context_scim->impl->si = factory->create_instance ("UTF-8", _instance_count++);
920                 context_scim->impl->preedit_string.clear ();
921                 context_scim->impl->preedit_attrlist.clear ();
922                 context_scim->impl->preedit_caret = 0;
923                 context_scim->impl->preedit_started = false;
924                 attach_instance (context_scim->impl->si);
925                 need_cap = true;
926                 need_reg = true;
927                 context_scim->impl->shared_si = false;
928                 SCIM_DEBUG_FRONTEND(2) << "create new instance: " << context_scim->impl->si->get_id () << " " << context_scim->impl->si->get_factory_uuid () << "\n";
929             }
930         }
931
932         context_scim->impl->si->set_frontend_data (static_cast <void*> (context_scim));
933
934         if (need_reg) _panel_client.register_input_context (context_scim->id, context_scim->impl->si->get_factory_uuid ());
935         if (need_cap) set_ic_capabilities (context_scim);
936         if (need_reset) context_scim->impl->si->reset ();
937
938         panel_req_focus_in (context_scim);
939 //        panel_req_update_screen (context_scim);
940         panel_req_update_spot_location (context_scim);
941         panel_req_update_factory_info (context_scim);
942
943         if (context_scim->impl->is_on) {
944             _panel_client.turn_on (context_scim->id);
945 //            _panel_client.hide_preedit_string (context_scim->id);
946 //            _panel_client.hide_aux_string (context_scim->id);
947 //            _panel_client.hide_lookup_table (context_scim->id);
948             context_scim->impl->si->focus_in ();
949         } else {
950             _panel_client.turn_off (context_scim->id);
951         }
952
953         if (!context_scim->impl->is_on)
954             turn_on_ic (context_scim);
955
956         _panel_client.send ();
957     }
958
959     // Get theme color for preedit background color
960 /*    if (_theme_color != NULL) {
961         gdk_color_free (_theme_color);
962         _theme_color = NULL;
963     }*/
964
965     /*
966     if (_theme_color == NULL)
967     {
968         GtkWidget *temp_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
969         gtk_widget_realize (temp_window);
970         gtk_widget_style_get (temp_window, "theme-color", &_theme_color, NULL);
971         if (GTK_IS_WIDGET (temp_window))
972             gtk_widget_destroy (temp_window);
973     }
974     */
975
976     print_time ("exit gtk_im_context_scim_focus_in");
977 }
978
979 /**
980  * gtk_im_context_scim_focus_out:
981  * @context: a #GtkIMContext
982  *
983  * This function will be called by gtk.
984  * Notify the input method that the widget to which this
985  * input context corresponds has lost focus. The input method
986  * may, for example, change the displayed feedback or reset the contexts
987  * state to reflect this change.
988  **/
989 static void
990 gtk_im_context_scim_focus_out (GtkIMContext *context)
991 {
992     print_time ("enter gtk_im_context_scim_focus_out");
993     GtkIMContextSCIM *context_scim = GTK_IM_CONTEXT_SCIM (context);
994
995     if (context_scim)
996         SCIM_DEBUG_FRONTEND(1) << "gtk_im_context_scim_focus_out(" << context_scim->id << ")...\n";
997
998     if (_snooper_installed) {
999         SCIM_DEBUG_FRONTEND(2) << "Remove key snooper.\n";
1000         gtk_key_snooper_remove (_snooper_id);
1001         _snooper_installed = false;
1002     }
1003
1004     if (context_scim && context_scim->impl && context_scim == _focused_ic) {
1005
1006         WideString wstr = context_scim->impl->preedit_string;
1007
1008         //sehwan added
1009         if (context_scim->impl->need_commit_preedit)
1010         {
1011             panel_slot_hide_preedit_string (context_scim->id);
1012
1013             if (wstr.length ())
1014                 g_signal_emit_by_name (context_scim, "commit", utf8_wcstombs (wstr).c_str ());
1015
1016             _panel_client.prepare (context_scim->id);
1017             _panel_client.reset_input_context (context_scim->id);
1018             _panel_client.send ();
1019         }
1020
1021         _panel_client.prepare (context_scim->id);
1022         context_scim->impl->si->focus_out ();
1023         context_scim->impl->si->reset ();
1024         //if (context_scim->impl->shared_si) context_scim->impl->si->reset ();
1025         _panel_client.focus_out (context_scim->id);
1026         _panel_client.send ();
1027         _focused_ic = 0;
1028     }
1029     print_time ("exit gtk_im_context_scim_focus_out");
1030 }
1031
1032 /**
1033  * gtk_im_context_scim_set_cursor_location:
1034  * @context: a #GtkIMContext
1035  * @area: new location
1036  *
1037  * This function will be called gtk.
1038  * Notify the input method that a change in cursor
1039  * position has been made. The location is relative to the client
1040  * window.
1041  **/
1042 static void
1043 gtk_im_context_scim_set_cursor_location (GtkIMContext *context,
1044                                          GdkRectangle *area)
1045 {
1046     print_time ("enter gtk_im_context_scim_set_cursor_location");
1047     SCIM_DEBUG_FRONTEND(4) << "gtk_im_context_scim_set_cursor_location...\n";
1048
1049     gint x, y;
1050     GtkIMContextSCIM *context_scim = GTK_IM_CONTEXT_SCIM (context);
1051 #ifdef USING_ISF_MAINWINDOW_AUTOSCROLL
1052     gtk_ise_set_cursor_location (area);
1053 #endif
1054     ISF_PROF_DEBUG("last message");
1055 #if 1
1056     if (context_scim && context_scim->impl && context_scim->impl->client_window && context_scim == _focused_ic) {
1057         // Don't update spot location while updating preedit string.
1058         if (context_scim->impl->preedit_updating)
1059             return;
1060
1061         gdk_window_get_origin(context_scim->impl->client_window, &x, &y);
1062         if (context_scim->impl->cursor_x != x + area->x + area->width ||
1063             context_scim->impl->cursor_y != y + area->y + area->height) {
1064             context_scim->impl->cursor_x = x + area->x + area->width;
1065             context_scim->impl->cursor_y = y + area->y + area->height;
1066             context_scim->impl->cursor_top_y = y + area->y;
1067 //            if (area->y > 0)
1068 //                context_scim->impl->cursor_top_y = y + area->y - 4;
1069
1070             _panel_client.prepare (context_scim->id);
1071             panel_req_update_spot_location (context_scim);
1072             _panel_client.send ();
1073             SCIM_DEBUG_FRONTEND(2) << "new cursor location = " << context_scim->impl->cursor_x << "," << context_scim->impl->cursor_y << "\n";
1074         }
1075     }
1076 #endif
1077     print_time ("exit gtk_im_context_scim_set_cursor_location");
1078 }
1079
1080 /**
1081  * gtk_im_context_scim_set_use_preedit:
1082  * @context: a #GtkIMContext
1083  * @area: new location
1084  *
1085  * This function will be called by gtk.
1086  * Notify the input method that a change in cursor
1087  * position has been made. The location is relative to the client
1088  * window.
1089  **/
1090 static void
1091 gtk_im_context_scim_set_use_preedit (GtkIMContext *context,
1092                                      gboolean      use_preedit)
1093 {
1094     print_time ("enter gtk_im_context_scim_set_use_preedit");
1095     SCIM_DEBUG_FRONTEND(1) << "gtk_im_context_scim_set_use_preedit = " << (use_preedit ? "true" : "false") << "...\n";
1096
1097     GtkIMContextSCIM *context_scim = GTK_IM_CONTEXT_SCIM (context);
1098
1099     if (!_on_the_spot) return;
1100
1101     if (context_scim && context_scim->impl) {
1102         bool old = context_scim->impl->use_preedit;
1103         context_scim->impl->use_preedit = use_preedit;
1104         if (context_scim == _focused_ic) {
1105             _panel_client.prepare (context_scim->id);
1106
1107             if (old != use_preedit)
1108                 set_ic_capabilities (context_scim);
1109
1110             if (context_scim->impl->preedit_string.length ())
1111                 slot_show_preedit_string (context_scim->impl->si);
1112
1113             _panel_client.send ();
1114         }
1115     }
1116     print_time ("exit gtk_im_context_scim_set_use_preedit");
1117 }
1118
1119 /**
1120  * gtk_im_context_scim_get_preedit_string:
1121  * @context:    a #GtkIMContext
1122  * @str:        location to store the retrieved string. The
1123  *              string retrieved must be freed with g_free ().
1124  * @attrs:      location to store the retrieved attribute list.
1125  *              When you are done with this list, you must
1126  *              unreference it with pango_attr_list_unref().
1127  * @cursor_pos: location to store position of cursor (in characters)
1128  *              within the preedit string.
1129  *
1130  * This function will be called by gtk
1131  * Retrieve the current preedit string for the input context,
1132  * and a list of attributes to apply to the string.
1133  * This string should be displayed inserted at the insertion
1134  * point.
1135  **/
1136 static void
1137 gtk_im_context_scim_get_preedit_string (GtkIMContext   *context,
1138                                         gchar         **str,
1139                                         PangoAttrList **attrs,
1140                                         gint           *cursor_pos)
1141 {
1142     print_time ("enter gtk_im_context_scim_get_preedit_string");
1143     SCIM_DEBUG_FRONTEND(1) << "gtk_im_context_scim_get_preedit_string...\n";
1144
1145     GtkIMContextSCIM *context_scim = GTK_IM_CONTEXT_SCIM (context);
1146
1147     if (context_scim && context_scim->impl && context_scim->impl->is_on) {
1148         String mbs = utf8_wcstombs (context_scim->impl->preedit_string);
1149
1150         if (str) {
1151             if (mbs.length ())
1152                 *str = g_strdup (mbs.c_str ());
1153             else
1154                 *str = g_strdup ("");
1155         }
1156
1157         if (cursor_pos) {
1158             *cursor_pos = context_scim->impl->preedit_caret;
1159         }
1160
1161         if (attrs) {
1162             *attrs = pango_attr_list_new ();
1163
1164             if (mbs.length ()) {
1165                 guint start_index, end_index;
1166                 guint wlen = context_scim->impl->preedit_string.length ();
1167
1168                 PangoAttribute *attr = NULL;
1169                 AttributeList::const_iterator i;
1170                 //bool underline = false;
1171                 bool *attrs_flag = new bool [mbs.length ()];
1172
1173                 memset (attrs_flag, 0, mbs.length () * sizeof (bool));
1174
1175                 for (i = context_scim->impl->preedit_attrlist.begin ();
1176                      i != context_scim->impl->preedit_attrlist.end (); ++i) {
1177                     start_index = i->get_start ();
1178                     end_index = i->get_end ();
1179
1180                     if (end_index <= wlen && start_index < end_index && i->get_type () != SCIM_ATTR_DECORATE_NONE) {
1181                         start_index = g_utf8_offset_to_pointer (mbs.c_str (), i->get_start ()) - mbs.c_str ();
1182                         end_index = g_utf8_offset_to_pointer (mbs.c_str (), i->get_end ()) - mbs.c_str ();
1183
1184                         if (i->get_type () == SCIM_ATTR_DECORATE) {
1185                             if (i->get_value () == scim::SCIM_ATTR_DECORATE_UNDERLINE) {
1186                                 attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
1187                                 if (attr == NULL)
1188                                     continue;
1189                                 attr->start_index = start_index;
1190                                 attr->end_index = end_index;
1191                                 pango_attr_list_insert (*attrs, attr);
1192                                 //underline = true;
1193                             } else if (i->get_value () == SCIM_ATTR_DECORATE_REVERSE) {
1194                                 attr = pango_attr_foreground_new (_normal_bg.red, _normal_bg.green, _normal_bg.blue);
1195                                 if (attr == NULL)
1196                                     continue;
1197                                 attr->start_index = start_index;
1198                                 attr->end_index = end_index;
1199                                 pango_attr_list_insert (*attrs, attr);
1200
1201                                 attr = pango_attr_background_new (_normal_text.red, _normal_text.green, _normal_text.blue);
1202                                 if (attr == NULL)
1203                                     continue;
1204                                 attr->start_index = start_index;
1205                                 attr->end_index = end_index;
1206                                 pango_attr_list_insert (*attrs, attr);
1207                             } else if (i->get_value () == SCIM_ATTR_DECORATE_HIGHLIGHT) {
1208                                 attr = pango_attr_foreground_new (_active_text.red, _active_text.green, _active_text.blue);
1209                                 if (attr == NULL)
1210                                     continue;
1211                                 //attr = pango_attr_foreground_new (65535, 65535, 65535);
1212                                 attr->start_index = start_index;
1213                                 attr->end_index = end_index;
1214                                 pango_attr_list_insert (*attrs, attr);
1215
1216                                 attr = pango_attr_background_new (_active_bg.red, _active_bg.green, _active_bg.blue);
1217                                 if (attr == NULL)
1218                                     continue;
1219                                 //attr = pango_attr_background_new (32767, 32767, 32767);
1220                                 attr->start_index = start_index;
1221                                 attr->end_index = end_index;
1222                                 pango_attr_list_insert (*attrs, attr);
1223                             }
1224                         } else if (i->get_type () == SCIM_ATTR_FOREGROUND) {
1225                             unsigned int color = i->get_value ();
1226
1227                             attr = pango_attr_foreground_new (SCIM_RGB_COLOR_RED(color) * 256, SCIM_RGB_COLOR_GREEN(color) * 256, SCIM_RGB_COLOR_BLUE(color) * 256);
1228                             if (attr == NULL)
1229                                 continue;
1230                             attr->start_index = start_index;
1231                             attr->end_index = end_index;
1232                             pango_attr_list_insert (*attrs, attr);
1233                         } else if (i->get_type () == SCIM_ATTR_BACKGROUND) {
1234                             unsigned int color = i->get_value ();
1235
1236                             attr = pango_attr_background_new (SCIM_RGB_COLOR_RED(color) * 256, SCIM_RGB_COLOR_GREEN(color) * 256, SCIM_RGB_COLOR_BLUE(color) * 256);
1237                             if (attr == NULL)
1238                                 continue;
1239                             attr->start_index = start_index;
1240                             attr->end_index = end_index;
1241                             pango_attr_list_insert (*attrs, attr);
1242                         }
1243
1244                         // Record which character has attribute.
1245                         for (guint pos = start_index; pos < end_index; ++pos)
1246                             attrs_flag [pos] = 1;
1247                     }
1248                 }
1249
1250                 // Add underline for all characters which don't have attribute.
1251                 for (guint pos = 0; pos < mbs.length (); ++pos) {
1252                     if (!attrs_flag [pos]) {
1253                         guint begin_pos = pos;
1254
1255                         while (pos < mbs.length () && !attrs_flag [pos])
1256                             ++pos;
1257
1258                         // use REVERSE style as default
1259                         attr = pango_attr_foreground_new (_normal_bg.red, _normal_bg.green, _normal_bg.blue);
1260                         if (attr == NULL)
1261                             continue;
1262                         attr->start_index = begin_pos;
1263                         attr->end_index = pos;
1264                         pango_attr_list_insert (*attrs, attr);
1265
1266                         attr = pango_attr_background_new (_normal_text.red, _normal_text.green, _normal_text.blue);
1267                         if (attr == NULL)
1268                             continue;
1269                         attr->start_index = begin_pos;
1270                         attr->end_index = pos;
1271                         pango_attr_list_insert (*attrs, attr);
1272
1273                         /*
1274                         attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
1275                         attr->start_index = begin_pos;
1276                         attr->end_index = pos;
1277                         pango_attr_list_insert (*attrs, attr);
1278                         */
1279                     }
1280                 }
1281
1282                 delete [] attrs_flag;
1283             }
1284         }
1285     } else {
1286         if (str)
1287             *str = g_strdup ("");
1288
1289         if (cursor_pos)
1290             *cursor_pos = 0;
1291
1292         if (attrs)
1293             *attrs = pango_attr_list_new ();
1294     }
1295     print_time ("exit gtk_im_context_scim_get_preedit_string");
1296 }
1297
1298 static gboolean
1299 gtk_scim_key_snooper (GtkWidget    *grab_widget,
1300                       GdkEventKey  *event,
1301                       gpointer      data)
1302 {
1303     SCIM_DEBUG_FRONTEND(3) << "gtk_scim_key_snooper...\n";
1304
1305     gboolean ret = FALSE;
1306
1307     if (_focused_ic && _focused_ic->impl && (event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE) && !event->send_event) {
1308         _focused_widget = grab_widget;
1309
1310         KeyEvent key = keyevent_gdk_to_scim (_focused_ic, *event);
1311
1312         key.mask &= _valid_key_mask;
1313
1314         key.layout = _keyboard_layout;
1315
1316         _panel_client.prepare (_focused_ic->id);
1317
1318         if (!filter_hotkeys (_focused_ic, key)) {
1319             if (!_focused_ic->impl->is_on || !_focused_ic->impl->si->process_key_event (key)) {
1320                 SCIM_DEBUG_FRONTEND(3) << "Failed to process: "
1321                                        << (!_focused_ic->impl->is_on ? "IC is off" : "IC doesn't process")
1322                                        << ".\n";
1323                 ret = _fallback_instance->process_key_event (key);
1324             } else {
1325                 ret = TRUE;
1326             }
1327         } else {
1328             ret = TRUE;
1329         }
1330
1331         _panel_client.send ();
1332
1333         _focused_widget = 0;
1334     } else {
1335         SCIM_DEBUG_FRONTEND(3) << "Failed snooper: "
1336                                << ((!_focused_ic || !_focused_ic->impl) ? "Invalid focused ic" :
1337                                    (event->send_event ? "send event is set" : "unknown"))
1338                                << "\n";
1339     }
1340
1341     return ret;
1342 }
1343
1344 static void
1345 gtk_im_slave_commit_cb (GtkIMContext     *context,
1346                         const char       *str,
1347                         GtkIMContextSCIM *context_scim)
1348 {
1349     g_return_if_fail(str);
1350     g_signal_emit_by_name(context_scim, "commit", str);
1351 }
1352
1353 /* Panel Slot functions */
1354 static void
1355 panel_slot_reload_config (int context)
1356 {
1357     SCIM_DEBUG_FRONTEND(1) << "panel_slot_reload_config...\n";
1358     _config->reload ();
1359 }
1360
1361 static void
1362 panel_slot_exit (int /* context */)
1363 {
1364     SCIM_DEBUG_FRONTEND(1) << "panel_slot_exit...\n";
1365
1366     finalize ();
1367 }
1368
1369 static void
1370 panel_slot_update_lookup_table_page_size (int context, int page_size)
1371 {
1372     GtkIMContextSCIM *ic = find_ic (context);
1373     SCIM_DEBUG_FRONTEND(1) << "panel_slot_update_lookup_table_page_size context=" << context << " page_size=" << page_size << " ic=" << ic << "\n";
1374     if (ic && ic->impl) {
1375         _panel_client.prepare (ic->id);
1376         ic->impl->si->update_lookup_table_page_size (page_size);
1377         _panel_client.send ();
1378     }
1379 }
1380
1381 static void
1382 panel_slot_lookup_table_page_up (int context)
1383 {
1384     GtkIMContextSCIM *ic = find_ic (context);
1385     SCIM_DEBUG_FRONTEND(1) << "panel_slot_lookup_table_page_up context=" << context << " ic=" << ic << "\n";
1386     if (ic && ic->impl) {
1387         _panel_client.prepare (ic->id);
1388         ic->impl->si->lookup_table_page_up ();
1389         _panel_client.send ();
1390     }
1391 }
1392
1393 static void
1394 panel_slot_lookup_table_page_down (int context)
1395 {
1396     GtkIMContextSCIM *ic = find_ic (context);
1397     SCIM_DEBUG_FRONTEND(1) << "panel_slot_lookup_table_page_down context=" << context << " ic=" << ic << "\n";
1398     if (ic && ic->impl) {
1399         _panel_client.prepare (ic->id);
1400         ic->impl->si->lookup_table_page_down ();
1401         _panel_client.send ();
1402     }
1403 }
1404
1405 static void
1406 panel_slot_trigger_property (int context, const String &property)
1407 {
1408     GtkIMContextSCIM *ic = find_ic (context);
1409     SCIM_DEBUG_FRONTEND(1) << "panel_slot_trigger_property context=" << context << " property=" << property << " ic=" << ic << "\n";
1410     if (ic && ic->impl) {
1411         _panel_client.prepare (ic->id);
1412         ic->impl->si->trigger_property (property);
1413         _panel_client.send ();
1414     }
1415 }
1416
1417 static void
1418 panel_slot_process_helper_event (int context, const String &target_uuid, const String &helper_uuid, const Transaction &trans)
1419 {
1420     GtkIMContextSCIM *ic = find_ic (context);
1421     SCIM_DEBUG_FRONTEND(1) << "panel_slot_process_helper_event context=" << context << " target=" << target_uuid
1422                            << " helper=" << helper_uuid << " ic=" << ic << " ic->impl=" << (ic ? ic->impl : 0) << " ic-uuid="
1423                            << ((ic && ic->impl) ? ic->impl->si->get_factory_uuid () : "" ) << "\n";
1424     if (ic && ic->impl && ic->impl->si->get_factory_uuid () == target_uuid) {
1425         _panel_client.prepare (ic->id);
1426         SCIM_DEBUG_FRONTEND(2) << "call process_helper_event\n";
1427         ic->impl->si->process_helper_event (helper_uuid, trans);
1428         _panel_client.send ();
1429     }
1430 }
1431
1432 static void
1433 panel_slot_move_preedit_caret (int context, int caret_pos)
1434 {
1435     GtkIMContextSCIM *ic = find_ic (context);
1436     SCIM_DEBUG_FRONTEND(1) << "panel_slot_move_preedit_caret context=" << context << " caret=" << caret_pos << " ic=" << ic << "\n";
1437     if (ic && ic->impl) {
1438         _panel_client.prepare (ic->id);
1439         ic->impl->si->move_preedit_caret (caret_pos);
1440         _panel_client.send ();
1441     }
1442 }
1443
1444 static void
1445 panel_slot_select_candidate (int context, int cand_index)
1446 {
1447     GtkIMContextSCIM *ic = find_ic (context);
1448     SCIM_DEBUG_FRONTEND(1) << "panel_slot_select_candidate context=" << context << " candidate=" << cand_index << " ic=" << ic << "\n";
1449     if (ic && ic->impl) {
1450         _panel_client.prepare (ic->id);
1451         ic->impl->si->select_candidate (cand_index);
1452         _panel_client.send ();
1453     }
1454 }
1455
1456 static void
1457 panel_slot_process_key_event (int context, const KeyEvent &key)
1458 {
1459     GtkIMContextSCIM *ic = find_ic (context);
1460     SCIM_DEBUG_FRONTEND(1) << "panel_slot_process_key_event context=" << context << " key=" << key.get_key_string () << " ic=" << ic << "\n";
1461
1462     if (_focused_ic != ic)
1463         return;
1464
1465     if (ic && ic->impl) {
1466         // Just send it to key_snooper and bypass to client directly (because send_event is set to TRUE).
1467         GdkEventKey gdkevent = keyevent_scim_to_gdk (ic, key, FALSE);
1468         gdk_event_put ((GdkEvent *)&gdkevent);
1469     }
1470 }
1471
1472 static void
1473 panel_slot_commit_string (int context, const WideString &wstr)
1474 {
1475     GtkIMContextSCIM *ic = find_ic (context);
1476     SCIM_DEBUG_FRONTEND(1) << "panel_slot_commit_string context=" << context << " str=" << utf8_wcstombs (wstr) << " ic=" << ic << "\n";
1477
1478     if (_focused_ic != ic)
1479         return;
1480
1481     if (ic && ic->impl)
1482         g_signal_emit_by_name (ic, "commit", utf8_wcstombs (wstr).c_str ());
1483 }
1484
1485 static void
1486 panel_slot_forward_key_event (int context, const KeyEvent &key)
1487 {
1488     GtkIMContextSCIM *ic = find_ic (context);
1489     SCIM_DEBUG_FRONTEND(1) << "panel_slot_forward_key_event context=" << context << " key=" << key.get_key_string () << " ic=" << ic << "\n";
1490
1491     if (_focused_ic != ic)
1492         return;
1493
1494     if (ic && ic->impl) {
1495         // Just send it to key_snooper and bypass to client directly (because send_event is set to TRUE).
1496         GdkEventKey gdkevent = keyevent_scim_to_gdk (ic, key, TRUE);
1497         gdk_event_put ((GdkEvent *)&gdkevent);
1498     }
1499 }
1500
1501 static void
1502 panel_slot_request_help (int context)
1503 {
1504     GtkIMContextSCIM *ic = find_ic (context);
1505     SCIM_DEBUG_FRONTEND(1) << "panel_slot_request_help context=" << context << " ic=" << ic << "\n";
1506     if (ic && ic->impl) {
1507         _panel_client.prepare (ic->id);
1508         panel_req_show_help (ic);
1509         _panel_client.send ();
1510     }
1511 }
1512
1513 static void
1514 panel_slot_request_factory_menu (int context)
1515 {
1516     GtkIMContextSCIM *ic = find_ic (context);
1517     SCIM_DEBUG_FRONTEND(1) << "panel_slot_request_factory_menu context=" << context << " ic=" << ic << "\n";
1518     if (ic && ic->impl) {
1519         _panel_client.prepare (ic->id);
1520         panel_req_show_factory_menu (ic);
1521         _panel_client.send ();
1522     }
1523 }
1524
1525 static void
1526 panel_slot_change_factory (int context, const String &uuid)
1527 {
1528     GtkIMContextSCIM *ic = find_ic (context);
1529     SCIM_DEBUG_FRONTEND(1) << "panel_slot_change_factory context=" << context << " factory=" << uuid << " ic=" << ic << "\n";
1530     if (ic && ic->impl) {
1531         ic->impl->si->reset ();
1532         _panel_client.prepare (ic->id);
1533         open_specific_factory (ic, uuid);
1534         _panel_client.send ();
1535     }
1536 }
1537
1538 static void
1539 panel_slot_reset_keyboard_ise (int context)
1540 {
1541     GtkIMContextSCIM *ic = find_ic (context);
1542     SCIM_DEBUG_FRONTEND(1) << "panel_slot_reset_keyboard_ise context=" << context << " ic=" << ic << "\n";
1543     if (ic && ic->impl)
1544     {
1545         WideString wstr = ic->impl->preedit_string;
1546         if (ic->impl->need_commit_preedit)
1547         {
1548             panel_slot_hide_preedit_string (ic->id);
1549
1550             if (wstr.length ())
1551                 g_signal_emit_by_name (ic, "commit", utf8_wcstombs (wstr).c_str ());
1552         }
1553         ic->impl->si->reset ();
1554     }
1555 }
1556
1557 static void
1558 panel_slot_show_preedit_string (int context)
1559 {
1560     GtkIMContextSCIM *ic = find_ic (context);
1561     SCIM_DEBUG_FRONTEND(1) << "panel_slot_show_preedit_string context=" << context << "\n";
1562
1563     if (ic && ic->impl)
1564     {
1565         if (!ic->impl->is_on)
1566             ic->impl->is_on = true;
1567
1568         if (ic->impl->use_preedit) {
1569             if (!ic->impl->preedit_started) {
1570                 KeyEvent key (0xff, SCIM_KEY_ShiftMask);
1571
1572                 GdkEventKey gdkevent = keyevent_scim_to_gdk (ic, key, TRUE);
1573
1574                 gdk_event_put ((GdkEvent *)&gdkevent);
1575
1576                 g_signal_emit_by_name (_focused_ic, "preedit-start");
1577                 ic->impl->preedit_started     = true;
1578                 ic->impl->need_commit_preedit = true;
1579             }
1580         }
1581     }
1582 }
1583
1584 static void
1585 panel_slot_hide_preedit_string (int context)
1586 {
1587     GtkIMContextSCIM *ic = find_ic (context);
1588     SCIM_DEBUG_FRONTEND(1) << "panel_slot_hide_preedit_string context=" << context << "\n";
1589
1590     if (ic && ic->impl)
1591     {
1592         if (!ic->impl->is_on)
1593             ic->impl->is_on = true;
1594
1595         bool emit = false;
1596         if (ic->impl->preedit_string.length ()) {
1597             ic->impl->preedit_string = WideString ();
1598             ic->impl->preedit_caret = 0;
1599             ic->impl->preedit_attrlist.clear ();
1600             emit = true;
1601         }
1602         if (ic->impl->use_preedit) {
1603             if (emit) g_signal_emit_by_name (ic, "preedit-changed");
1604             if (ic->impl->preedit_started) {
1605                 g_signal_emit_by_name (ic, "preedit-end");
1606                 ic->impl->preedit_started     = false;
1607                 ic->impl->need_commit_preedit = false;
1608             }
1609         }
1610     }
1611 }
1612
1613 static void
1614 panel_slot_update_preedit_string (int context,
1615                                   const WideString    &str,
1616                                   const AttributeList &attrs)
1617 {
1618     GtkIMContextSCIM *ic = find_ic (context);
1619     SCIM_DEBUG_FRONTEND(1) << "panel_slot_update_preedit_string context=" << context << "\n";
1620
1621     if (ic && ic->impl)
1622     {
1623         if (!ic->impl->is_on)
1624             ic->impl->is_on = true;
1625
1626         if (ic->impl->preedit_string != str || str.length ()) {
1627             ic->impl->preedit_string   = str;
1628             ic->impl->preedit_attrlist = attrs;
1629
1630             if (ic->impl->use_preedit) {
1631                 if (!ic->impl->preedit_started) {
1632                     g_signal_emit_by_name(_focused_ic, "preedit-start");
1633                     ic->impl->preedit_started = true;
1634                     ic->impl->need_commit_preedit = true;
1635                 }
1636                 ic->impl->preedit_caret    = str.length ();
1637                 ic->impl->preedit_updating = true;
1638                 g_signal_emit_by_name(ic, "preedit-changed");
1639                 ic->impl->preedit_updating = false;
1640             }
1641         }
1642     }
1643 }
1644
1645 /* Panel Requestion functions. */
1646 static void
1647 panel_req_update_screen (GtkIMContextSCIM *ic)
1648 {
1649 #if GDK_MULTIHEAD_SAFE
1650     if (ic->impl->client_window) {
1651         GdkScreen *screen = gdk_drawable_get_screen (GDK_DRAWABLE (ic->impl->client_window));
1652         if (screen) {
1653             int number = gdk_screen_get_number (screen);
1654             _panel_client.update_screen (ic->id, number);
1655         }
1656     }
1657 #endif
1658 }
1659
1660 static void
1661 panel_req_show_help (GtkIMContextSCIM *ic)
1662 {
1663     String help;
1664
1665     help =  String (_("Smart Common Input Method platform ")) +
1666             String (SCIM_VERSION) +
1667             String (_("\n(C) 2002-2005 James Su <suzhe@tsinghua.org.cn>\n\n"));
1668
1669     if (ic && ic->impl) {
1670         IMEngineFactoryPointer sf = _backend->get_factory (ic->impl->si->get_factory_uuid ());
1671         if (sf) {
1672             help += utf8_wcstombs (sf->get_name ());
1673             help += String (_(":\n\n"));
1674
1675             help += utf8_wcstombs (sf->get_help ());
1676             help += String (_("\n\n"));
1677
1678             help += utf8_wcstombs (sf->get_credits ());
1679         }
1680
1681         _panel_client.show_help (ic->id, help);
1682     }
1683 }
1684
1685 static void
1686 panel_req_show_factory_menu (GtkIMContextSCIM *ic)
1687 {
1688     std::vector<IMEngineFactoryPointer> factories;
1689     std::vector <PanelFactoryInfo> menu;
1690
1691     _backend->get_factories_for_encoding (factories, "UTF-8");
1692
1693     for (size_t i = 0; i < factories.size (); ++ i) {
1694         menu.push_back (PanelFactoryInfo (
1695                                     factories [i]->get_uuid (),
1696                                     utf8_wcstombs (factories [i]->get_name ()),
1697                                     factories [i]->get_language (),
1698                                     factories [i]->get_icon_file ()));
1699     }
1700
1701     if (menu.size ())
1702         _panel_client.show_factory_menu (ic->id, menu);
1703 }
1704
1705 static void
1706 panel_req_update_factory_info (GtkIMContextSCIM *ic)
1707 {
1708     if (ic && ic->impl && ic == _focused_ic) {
1709         PanelFactoryInfo info;
1710         if (ic->impl->is_on) {
1711             IMEngineFactoryPointer sf = _backend->get_factory (ic->impl->si->get_factory_uuid ());
1712             if (sf)
1713                 info = PanelFactoryInfo (sf->get_uuid (), utf8_wcstombs (sf->get_name ()), sf->get_language (), sf->get_icon_file ());
1714         } else {
1715             info = PanelFactoryInfo (String (""), String (_("English/Keyboard")), String ("C"), String (SCIM_KEYBOARD_ICON_FILE));
1716         }
1717         _panel_client.update_factory_info (ic->id, info);
1718     }
1719 }
1720
1721 static void
1722 panel_req_focus_in (GtkIMContextSCIM *ic)
1723 {
1724     _panel_client.focus_in (ic->id, ic->impl->si->get_factory_uuid ());
1725 }
1726
1727 static void
1728 panel_req_update_spot_location (GtkIMContextSCIM *ic)
1729 {
1730     _panel_client.update_spot_location (ic->id, ic->impl->cursor_x, ic->impl->cursor_y, ic->impl->cursor_top_y);
1731 }
1732
1733
1734 static bool
1735 filter_hotkeys (GtkIMContextSCIM *ic, const KeyEvent &key)
1736 {
1737     bool ret = false;
1738
1739     _frontend_hotkey_matcher.push_key_event (key);
1740     _imengine_hotkey_matcher.push_key_event (key);
1741
1742     FrontEndHotkeyAction hotkey_action = _frontend_hotkey_matcher.get_match_result ();
1743
1744     if (hotkey_action == SCIM_FRONTEND_HOTKEY_TRIGGER) {
1745         if (!ic->impl->is_on)
1746             turn_on_ic (ic);
1747         else
1748             turn_off_ic (ic);
1749         ret = true;
1750     } else if (hotkey_action == SCIM_FRONTEND_HOTKEY_ON) {
1751         if (!ic->impl->is_on)
1752             turn_on_ic (ic);
1753         ret = true;
1754     } else if (hotkey_action == SCIM_FRONTEND_HOTKEY_OFF) {
1755         if (ic->impl->is_on)
1756             turn_off_ic (ic);
1757         ret = true;
1758     } else if (hotkey_action == SCIM_FRONTEND_HOTKEY_NEXT_FACTORY) {
1759         open_next_factory (ic);
1760         ret = true;
1761     } else if (hotkey_action == SCIM_FRONTEND_HOTKEY_PREVIOUS_FACTORY) {
1762         open_previous_factory (ic);
1763         ret = true;
1764     } else if (hotkey_action == SCIM_FRONTEND_HOTKEY_SHOW_FACTORY_MENU) {
1765         panel_req_show_factory_menu (ic);
1766         ret = true;
1767     } else if (_imengine_hotkey_matcher.is_matched ()) {
1768         ISEInfo info = _imengine_hotkey_matcher.get_match_result ();
1769         ISE_TYPE type = info.type;
1770         if (type == IMENGINE_T)
1771             open_specific_factory (ic, info.uuid);
1772         else if (type == HELPER_T)
1773             _panel_client.start_helper (ic->id, info.uuid);
1774         ret = true;
1775     }
1776     return ret;
1777 }
1778
1779 static bool
1780 panel_initialize ()
1781 {
1782     String display_name;
1783
1784     {
1785 #if GDK_MULTIHEAD_SAFE
1786         const char *p = gdk_display_get_name (gdk_display_get_default ());
1787 #else
1788         const char *p = getenv ("DISPLAY");
1789 #endif
1790         if (p) display_name = String (p);
1791     }
1792
1793     SCIM_DEBUG_FRONTEND(1) << "panel_initialize..\n";
1794
1795     if (_panel_client.open_connection (_config->get_name (), display_name) >= 0) {
1796         int fd = _panel_client.get_connection_number ();
1797         _panel_iochannel = g_io_channel_unix_new (fd);
1798
1799         _panel_iochannel_read_source = g_io_add_watch (_panel_iochannel, G_IO_IN,  panel_iochannel_handler, 0);
1800         _panel_iochannel_err_source  = g_io_add_watch (_panel_iochannel, G_IO_ERR, panel_iochannel_handler, 0);
1801         _panel_iochannel_hup_source  = g_io_add_watch (_panel_iochannel, G_IO_HUP, panel_iochannel_handler, 0);
1802
1803         SCIM_DEBUG_FRONTEND(2) << " Panel FD= " << fd << "\n";
1804
1805         /*
1806         * When the PanelAgent process crash, the application will try restarting and
1807         * connecting the PanelAgent process.
1808         * After it, it will register the previous input contexts to the PanelAgent.
1809         */
1810         GtkIMContextSCIM *context_scim = _ic_list;
1811         while (context_scim != NULL) {
1812             _panel_client.prepare (context_scim->id);
1813             _panel_client.register_input_context (context_scim->id, context_scim->impl->si->get_factory_uuid ());
1814             _panel_client.start_default_ise (context_scim->id);
1815             _panel_client.send ();
1816             context_scim = context_scim->next;
1817         }
1818
1819         if (_focused_ic) {
1820             _panel_client.prepare (_focused_ic->id);
1821             panel_req_focus_in (_focused_ic);
1822             _panel_client.send ();
1823         }
1824
1825         return true;
1826     }
1827     return false;
1828 }
1829
1830 static void
1831 panel_finalize ()
1832 {
1833     _panel_client.close_connection ();
1834
1835     if (_panel_iochannel) {
1836         g_io_channel_unref (_panel_iochannel);
1837         g_source_remove (_panel_iochannel_read_source);
1838         g_source_remove (_panel_iochannel_err_source);
1839         g_source_remove (_panel_iochannel_hup_source);
1840         _panel_iochannel = 0;
1841         _panel_iochannel_read_source = 0;
1842         _panel_iochannel_err_source = 0;
1843         _panel_iochannel_hup_source = 0;
1844     }
1845 }
1846
1847 static gboolean
1848 panel_iochannel_handler (GIOChannel *source, GIOCondition condition, gpointer user_data)
1849 {
1850     if (condition == G_IO_IN) {
1851         if (!_panel_client.filter_event ()) {
1852             panel_finalize ();
1853             panel_initialize ();
1854             return (gboolean)FALSE;
1855         }
1856     } else if (condition == G_IO_ERR || condition == G_IO_HUP) {
1857         panel_finalize ();
1858         panel_initialize ();
1859         return (gboolean)FALSE;
1860     }
1861     return (gboolean)TRUE;
1862 }
1863
1864 static void
1865 turn_on_ic (GtkIMContextSCIM *ic)
1866 {
1867     if (ic && ic->impl && !ic->impl->is_on) {
1868         ic->impl->is_on = true;
1869
1870         if (ic == _focused_ic) {
1871             panel_req_focus_in (ic);
1872 //            panel_req_update_screen (ic);
1873             panel_req_update_spot_location (ic);
1874             panel_req_update_factory_info (ic);
1875             _panel_client.turn_on (ic->id);
1876 //            _panel_client.hide_preedit_string (ic->id);
1877 //            _panel_client.hide_aux_string (ic->id);
1878 //            _panel_client.hide_lookup_table (ic->id);
1879             ic->impl->si->focus_in ();
1880         }
1881
1882         //Record the IC on/off status
1883         if (_shared_input_method)
1884             _config->write (String (SCIM_CONFIG_FRONTEND_IM_OPENED_BY_DEFAULT), true);
1885
1886         if (ic->impl->use_preedit && ic->impl->preedit_string.length ()) {
1887             g_signal_emit_by_name(ic, "preedit-start");
1888             g_signal_emit_by_name(ic, "preedit-changed");
1889             ic->impl->preedit_started = true;
1890         }
1891     }
1892 }
1893
1894 static void
1895 turn_off_ic (GtkIMContextSCIM *ic)
1896 {
1897     if (ic && ic->impl && ic->impl->is_on) {
1898         ic->impl->is_on = false;
1899
1900         if (ic == _focused_ic) {
1901             ic->impl->si->focus_out ();
1902
1903 //            panel_req_update_factory_info (ic);
1904             _panel_client.turn_off (ic->id);
1905         }
1906
1907         //Record the IC on/off status
1908         if (_shared_input_method)
1909             _config->write (String (SCIM_CONFIG_FRONTEND_IM_OPENED_BY_DEFAULT), false);
1910
1911         if (ic->impl->use_preedit && ic->impl->preedit_string.length ()) {
1912             g_signal_emit_by_name(ic, "preedit-changed");
1913             g_signal_emit_by_name(ic, "preedit-end");
1914             ic->impl->preedit_started = false;
1915         }
1916     }
1917 }
1918
1919 static void
1920 set_ic_capabilities (GtkIMContextSCIM *ic)
1921 {
1922     if (ic && ic->impl) {
1923         unsigned int cap = SCIM_CLIENT_CAP_ALL_CAPABILITIES;
1924
1925         if (!_on_the_spot || !ic->impl->use_preedit)
1926             cap -= SCIM_CLIENT_CAP_ONTHESPOT_PREEDIT;
1927
1928         ic->impl->si->update_client_capabilities (cap);
1929     }
1930 }
1931
1932 static KeyEvent
1933 keyevent_gdk_to_scim (const GtkIMContextSCIM *ic, const GdkEventKey &gdkevent)
1934 {
1935     KeyEvent key;
1936
1937     // Use Key Symbole provided by gtk.
1938     key.code = gdkevent.keyval;
1939
1940 #ifdef GDK_WINDOWING_X11
1941     Display *display = 0;
1942
1943     if (ic && ic->impl && ic->impl->client_window)
1944         display = GDK_WINDOW_XDISPLAY (ic->impl->client_window);
1945     else
1946         display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
1947
1948     key.mask = scim_x11_keymask_x11_to_scim (display, gdkevent.state);
1949
1950     // Special treatment for two backslash keys on jp106 keyboard.
1951     if (key.code == SCIM_KEY_backslash) {
1952         int keysym_size = 0;
1953         KeySym *keysyms = XGetKeyboardMapping (display, gdkevent.hardware_keycode, 1, &keysym_size);
1954         if (keysyms != NULL) {
1955             if (keysyms[0] == XK_backslash &&
1956                 (keysym_size > 1 && keysyms[1] == XK_underscore))
1957                 key.mask |= SCIM_KEY_QuirkKanaRoMask;
1958             XFree (keysyms);
1959         }
1960     }
1961 #else
1962     if (gdkevent.state & GDK_SHIFT_MASK) key.mask |=SCIM_KEY_ShiftMask;
1963     if (gdkevent.state & GDK_LOCK_MASK) key.mask |=SCIM_KEY_CapsLockMask;
1964     if (gdkevent.state & GDK_CONTROL_MASK) key.mask |=SCIM_KEY_ControlMask;
1965     if (gdkevent.state & GDK_MOD1_MASK) key.mask |=SCIM_KEY_AltMask;
1966     if (gdkevent.state & GDK_MOD2_MASK) key.mask |=SCIM_KEY_NumLockMask;
1967 #endif
1968
1969     if (gdkevent.type == GDK_KEY_RELEASE) key.mask |= SCIM_KEY_ReleaseMask;
1970
1971     return key;
1972 }
1973
1974 static GdkKeymap *
1975 get_gdk_keymap (GdkWindow *window)
1976 {
1977     GdkKeymap *keymap = NULL;
1978
1979 #if GDK_MULTIHEAD_SAFE
1980     if (window)
1981         keymap = gdk_keymap_get_for_display (gdk_drawable_get_display (window));
1982     else
1983 #endif
1984         keymap = gdk_keymap_get_default ();
1985
1986     return keymap;
1987 }
1988
1989 static guint32
1990 get_time (void)
1991 {
1992     int tint;
1993     struct timeval tv;
1994     struct timezone tz;           /* is not used since ages */
1995     gettimeofday (&tv, &tz);
1996     tint = (int) tv.tv_sec * 1000;
1997     tint = tint / 1000 * 1000;
1998     tint = tint + tv.tv_usec / 1000;
1999     return ((guint32) tint);
2000 }
2001
2002 static GdkEventKey
2003 keyevent_scim_to_gdk (const GtkIMContextSCIM *ic,
2004                       const KeyEvent   &scimkey, gboolean send_event)
2005 {
2006     GdkEventKey gdkevent;
2007
2008 #ifdef GDK_WINDOWING_X11
2009     Display *display = 0;
2010
2011     if (ic && ic->impl && ic->impl->client_window)
2012         display = GDK_WINDOW_XDISPLAY (ic->impl->client_window);
2013     else
2014         display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
2015
2016     gdkevent.state = scim_x11_keymask_scim_to_x11 (display, scimkey.mask);
2017 #else
2018     gdkevent.state = 0;
2019     if (scimkey.is_shift_down ()) gdkevent.state |= GDK_SHIFT_MASK;
2020     if (scimkey.is_caps_lock_down ()) gdkevent.state |= GDK_LOCK_MASK;
2021     if (scimkey.is_control_down ()) gdkevent.state |= GDK_CONTROL_MASK;
2022     if (scimkey.is_alt_down ()) gdkevent.state |= GDK_MOD1_MASK;
2023     if (scimkey.is_num_lock_down ()) gdkevent.state |= GDK_MOD2_MASK;
2024 #endif
2025
2026     if (scimkey.is_key_release ()) gdkevent.state |= GDK_RELEASE_MASK;
2027
2028     gdkevent.type = (scimkey.is_key_release () ? GDK_KEY_RELEASE : GDK_KEY_PRESS);
2029     gdkevent.window = ((ic && ic->impl) ? ic->impl->client_window : 0);
2030     gdkevent.send_event = send_event;
2031     gdkevent.time = get_time ();
2032     gdkevent.keyval = scimkey.code;
2033     gdkevent.length = 0;
2034     gdkevent.string = 0;
2035     gdkevent.is_modifier = 0;
2036
2037     GdkKeymap *keymap = get_gdk_keymap (gdkevent.window);
2038
2039     GdkKeymapKey *keys = NULL;
2040     gint          n_keys;
2041
2042     if (gdk_keymap_get_entries_for_keyval (keymap, gdkevent.keyval, &keys, &n_keys)) {
2043         gdkevent.hardware_keycode = keys [0].keycode;
2044         gdkevent.group = keys [0].group;
2045     } else {
2046         gdkevent.hardware_keycode = 0;
2047         gdkevent.group = 0;
2048     }
2049
2050     if (keys) g_free (keys);
2051
2052     return gdkevent;
2053 }
2054
2055 static bool
2056 check_socket_frontend ()
2057 {
2058     SocketAddress address;
2059     SocketClient client;
2060
2061     uint32 magic;
2062
2063     address.set_address (scim_get_default_socket_frontend_address ());
2064
2065     if (!client.connect (address))
2066         return false;
2067
2068     if (!scim_socket_open_connection (magic,
2069                                       String ("ConnectionTester"),
2070                                       String ("SocketFrontEnd"),
2071                                       client,
2072                                       1000)) {
2073         return false;
2074     }
2075
2076     return true;
2077 }
2078
2079 static void
2080 initialize (void)
2081 {
2082     std::vector<String>     config_list;
2083     std::vector<String>     engine_list;
2084     std::vector<String>     helper_list;
2085     std::vector<String>     load_engine_list;
2086
2087     std::vector<String>::iterator it;
2088
2089     std::vector<String>     debug_mask_list;
2090     int                     debug_verbose = 0;
2091
2092     size_t                  i;
2093
2094     bool                    manual = false;
2095
2096     bool                    socket = true;
2097
2098     String                  config_module_name = "simple";
2099
2100     // Get debug info
2101     {
2102         char *str = NULL;
2103
2104         str = getenv ("GTK_IM_SCIM_DEBUG_VERBOSE");
2105
2106         if (str != NULL)
2107             debug_verbose = atoi (str);
2108
2109         DebugOutput::set_verbose_level (debug_verbose);
2110
2111         str = getenv ("GTK_IM_SCIM_DEBUG_MASK");
2112
2113         if (str != NULL) {
2114             scim_split_string_list (debug_mask_list, String (str), ',');
2115             if (debug_mask_list.size ()) {
2116                 DebugOutput::disable_debug (SCIM_DEBUG_AllMask);
2117                 for (i=0; i<debug_mask_list.size (); i++)
2118                     DebugOutput::enable_debug_by_name (debug_mask_list [i]);
2119             }
2120         }
2121     }
2122
2123     SCIM_DEBUG_FRONTEND(1) << "Initializing GTK2 IMModule...\n";
2124
2125     // Get system language.
2126     _language = scim_get_locale_language (scim_get_current_locale ());
2127
2128     if (socket) {
2129         // If no Socket FrontEnd is running, then launch one.
2130         // And set manual to false.
2131         bool check_result = check_socket_frontend ();
2132         if (!check_result) {
2133             std::cerr << "Launching a daemon with Socket FrontEnd...\n";
2134             //get modules list
2135             scim_get_imengine_module_list (engine_list);
2136             scim_get_helper_module_list (helper_list);
2137
2138             for (it = engine_list.begin (); it != engine_list.end (); it++) {
2139                 if (*it != "socket")
2140                     load_engine_list.push_back (*it);
2141             }
2142             for (it = helper_list.begin (); it != helper_list.end (); it++)
2143                 load_engine_list.push_back (*it);
2144             const char *new_argv [] = { "--no-stay", 0 };
2145             scim_launch (true,
2146                          config_module_name,
2147                          (load_engine_list.size () ? scim_combine_string_list (load_engine_list, ',') : "none"),
2148                          "socket",
2149                          (char **)new_argv);
2150             manual = false;
2151         }
2152
2153         // If there is one Socket FrontEnd running and it's not manual mode,
2154         // then just use this Socket Frontend.
2155         if (!manual) {
2156             for (int i = 0; i < 100; ++i) {
2157                 if (check_result) {
2158                     config_module_name = "socket";
2159                     load_engine_list.clear ();
2160                     load_engine_list.push_back ("socket");
2161                     break;
2162                 }
2163                 scim_usleep (100000);
2164                 check_result = check_socket_frontend ();
2165             }
2166         }
2167     }
2168
2169     if (config_module_name != "dummy") {
2170         //load config module
2171         SCIM_DEBUG_FRONTEND(1) << "Loading Config module: " << config_module_name << "...\n";
2172         _config_module = new ConfigModule (config_module_name);
2173
2174         //create config instance
2175         if (_config_module != NULL && _config_module->valid ())
2176             _config = _config_module->create_config ();
2177     }
2178
2179     if (_config.null ()) {
2180         SCIM_DEBUG_FRONTEND(1) << "Config module cannot be loaded, using dummy Config.\n";
2181
2182         if (_config_module) delete _config_module;
2183         _config_module = NULL;
2184
2185         _config = new DummyConfig ();
2186         config_module_name = "dummy";
2187     }
2188
2189     // Init colors.
2190     gdk_color_parse ("white",      &_normal_bg);
2191     gdk_color_parse ("black",      &_normal_text);
2192     gdk_color_parse ("black",      &_active_bg);
2193     gdk_color_parse ("white",      &_active_text);
2194
2195     reload_config_callback (_config);
2196     _config->signal_connect_reload (slot (reload_config_callback));
2197
2198     // create backend
2199     _backend = new CommonBackEnd (_config, load_engine_list.size () ? load_engine_list : engine_list);
2200
2201     if (_backend.null ()) {
2202         fprintf (stderr, "GTK IM Module : Cannot create BackEnd Object!\n");
2203     } else {
2204         _backend->initialize (_config, load_engine_list.size () ? load_engine_list : engine_list, false, false);
2205         _fallback_factory = _backend->get_factory (SCIM_COMPOSE_KEY_FACTORY_UUID);
2206     }
2207
2208     if (_fallback_factory.null ()) _fallback_factory = new DummyIMEngineFactory ();
2209
2210     _fallback_instance = _fallback_factory->create_instance (String ("UTF-8"), 0);
2211     _fallback_instance->signal_connect_commit_string (slot (fallback_commit_string_cb));
2212
2213     // Attach Panel Client signal.
2214     _panel_client.signal_connect_reload_config                 (slot (panel_slot_reload_config));
2215     _panel_client.signal_connect_exit                          (slot (panel_slot_exit));
2216     _panel_client.signal_connect_update_lookup_table_page_size (slot (panel_slot_update_lookup_table_page_size));
2217     _panel_client.signal_connect_lookup_table_page_up          (slot (panel_slot_lookup_table_page_up));
2218     _panel_client.signal_connect_lookup_table_page_down        (slot (panel_slot_lookup_table_page_down));
2219     _panel_client.signal_connect_trigger_property              (slot (panel_slot_trigger_property));
2220     _panel_client.signal_connect_process_helper_event          (slot (panel_slot_process_helper_event));
2221     _panel_client.signal_connect_move_preedit_caret            (slot (panel_slot_move_preedit_caret));
2222     _panel_client.signal_connect_select_candidate              (slot (panel_slot_select_candidate));
2223     _panel_client.signal_connect_process_key_event             (slot (panel_slot_process_key_event));
2224     _panel_client.signal_connect_commit_string                 (slot (panel_slot_commit_string));
2225     _panel_client.signal_connect_forward_key_event             (slot (panel_slot_forward_key_event));
2226     _panel_client.signal_connect_request_help                  (slot (panel_slot_request_help));
2227     _panel_client.signal_connect_request_factory_menu          (slot (panel_slot_request_factory_menu));
2228     _panel_client.signal_connect_change_factory                (slot (panel_slot_change_factory));
2229     _panel_client.signal_connect_reset_keyboard_ise            (slot (panel_slot_reset_keyboard_ise));
2230     _panel_client.signal_connect_show_preedit_string           (slot (panel_slot_show_preedit_string));
2231     _panel_client.signal_connect_hide_preedit_string           (slot (panel_slot_hide_preedit_string));
2232     _panel_client.signal_connect_update_preedit_string         (slot (panel_slot_update_preedit_string));
2233
2234     if (!panel_initialize ()) {
2235         fprintf (stderr, "GTK IM Module : Cannot connect to Panel!\n");
2236     }
2237 }
2238
2239 static void
2240 finalize (void)
2241 {
2242     SCIM_DEBUG_FRONTEND(1) << "Finalizing GTK2 IMModule...\n";
2243
2244     if (_snooper_installed) {
2245         gtk_key_snooper_remove(_snooper_id);
2246         _snooper_installed=false;
2247         _snooper_id = 0;
2248     }
2249
2250     // Reset this first so that the shared instance could be released correctly afterwards.
2251     _default_instance.reset ();
2252
2253     SCIM_DEBUG_FRONTEND(2) << "Finalize all IC partially.\n";
2254     while (_used_ic_impl_list) {
2255         // In case in "shared input method" mode,
2256         // all contexts share only one instance,
2257         // so we need point the reference pointer correctly before finalizing.
2258         _used_ic_impl_list->si->set_frontend_data (static_cast <void*> (_used_ic_impl_list->parent));
2259         gtk_im_context_scim_finalize_partial (_used_ic_impl_list->parent);
2260     }
2261
2262     delete_all_ic_impl ();
2263
2264     _fallback_instance.reset ();
2265     _fallback_factory.reset ();
2266
2267     SCIM_DEBUG_FRONTEND(2) << " Releasing BackEnd...\n";
2268     _backend.reset ();
2269
2270     SCIM_DEBUG_FRONTEND(2) << " Releasing Config...\n";
2271     _config.reset ();
2272
2273     if (_config_module) {
2274         SCIM_DEBUG_FRONTEND(2) << " Deleting _config_module...\n";
2275         delete _config_module;
2276         _config_module = 0;
2277     }
2278
2279     _focused_ic = 0;
2280     _focused_widget = 0;
2281
2282     _scim_initialized = false;
2283
2284     panel_finalize ();
2285 }
2286
2287 static void
2288 open_next_factory (GtkIMContextSCIM *ic)
2289 {
2290     SCIM_DEBUG_FRONTEND(2) << "open_next_factory context=" << ic->id << "\n";
2291     IMEngineFactoryPointer sf = _backend->get_next_factory ("", "UTF-8", ic->impl->si->get_factory_uuid ());
2292
2293     if (!sf.null ()) {
2294         turn_off_ic (ic);
2295         ic->impl->si = sf->create_instance ("UTF-8", ic->impl->si->get_id ());
2296         ic->impl->si->set_frontend_data (static_cast <void*> (ic));
2297         ic->impl->preedit_string = WideString ();
2298         ic->impl->preedit_caret = 0;
2299         attach_instance (ic->impl->si);
2300         _backend->set_default_factory (_language, sf->get_uuid ());
2301         _panel_client.register_input_context (ic->id, sf->get_uuid ());
2302         set_ic_capabilities (ic);
2303         turn_on_ic (ic);
2304
2305         if (_shared_input_method) {
2306             _default_instance = ic->impl->si;
2307             ic->impl->shared_si = true;
2308         }
2309     }
2310 }
2311
2312 static void
2313 open_previous_factory (GtkIMContextSCIM *ic)
2314 {
2315     SCIM_DEBUG_FRONTEND(2) << "open_previous_factory context=" << ic->id << "\n";
2316     IMEngineFactoryPointer sf = _backend->get_previous_factory ("", "UTF-8", ic->impl->si->get_factory_uuid ());
2317
2318     if (!sf.null ()) {
2319         turn_off_ic (ic);
2320         ic->impl->si = sf->create_instance ("UTF-8", ic->impl->si->get_id ());
2321         ic->impl->si->set_frontend_data (static_cast <void*> (ic));
2322         ic->impl->preedit_string = WideString ();
2323         ic->impl->preedit_caret = 0;
2324         attach_instance (ic->impl->si);
2325         _backend->set_default_factory (_language, sf->get_uuid ());
2326         _panel_client.register_input_context (ic->id, sf->get_uuid ());
2327         set_ic_capabilities (ic);
2328         turn_on_ic (ic);
2329
2330         if (_shared_input_method) {
2331             _default_instance = ic->impl->si;
2332             ic->impl->shared_si = true;
2333         }
2334     }
2335 }
2336
2337 static void
2338 open_specific_factory (GtkIMContextSCIM *ic,
2339                        const String     &uuid)
2340 {
2341     if (!(ic && ic->impl))
2342         return;
2343
2344     SCIM_DEBUG_FRONTEND(2) << "open_specific_factory context=" << ic->id << "\n";
2345
2346     // The same input method is selected, just turn on the IC.
2347     if (ic->impl->si && ic->impl->si->get_factory_uuid () == uuid) {
2348         turn_on_ic (ic);
2349         return;
2350     }
2351
2352     IMEngineFactoryPointer sf = _backend->get_factory (uuid);
2353
2354     if (uuid.length () && !sf.null ()) {
2355         turn_off_ic (ic);
2356         ic->impl->si = sf->create_instance ("UTF-8", ic->impl->si->get_id ());
2357         ic->impl->si->set_frontend_data (static_cast <void*> (ic));
2358         ic->impl->preedit_string = WideString ();
2359         ic->impl->preedit_caret = 0;
2360         attach_instance (ic->impl->si);
2361         _backend->set_default_factory (_language, sf->get_uuid ());
2362         _panel_client.register_input_context (ic->id, sf->get_uuid ());
2363         set_ic_capabilities (ic);
2364         turn_on_ic (ic);
2365
2366         if (_shared_input_method) {
2367             _default_instance = ic->impl->si;
2368             ic->impl->shared_si = true;
2369         }
2370     } else {
2371         // turn_off_ic comment out panel_req_update_factory_info()
2372         // turn_off_ic (ic);
2373         if (ic && ic->impl && ic->impl->is_on) {
2374             ic->impl->is_on = false;
2375
2376             if (ic == _focused_ic) {
2377                 ic->impl->si->focus_out ();
2378
2379                 panel_req_update_factory_info (ic);
2380                 _panel_client.turn_off (ic->id);
2381             }
2382
2383             //Record the IC on/off status
2384             if (_shared_input_method)
2385                 _config->write (String (SCIM_CONFIG_FRONTEND_IM_OPENED_BY_DEFAULT), false);
2386
2387             if (ic->impl->use_preedit && ic->impl->preedit_string.length ()) {
2388                 g_signal_emit_by_name(ic, "preedit-changed");
2389                 g_signal_emit_by_name(ic, "preedit-end");
2390                 ic->impl->preedit_started = false;
2391             }
2392         }
2393     }
2394 }
2395
2396 static void
2397 attach_instance (const IMEngineInstancePointer &si)
2398 {
2399     si->signal_connect_show_preedit_string (
2400         slot (slot_show_preedit_string));
2401     si->signal_connect_show_aux_string (
2402         slot (slot_show_aux_string));
2403     si->signal_connect_show_lookup_table (
2404         slot (slot_show_lookup_table));
2405
2406     si->signal_connect_hide_preedit_string (
2407         slot (slot_hide_preedit_string));
2408     si->signal_connect_hide_aux_string (
2409         slot (slot_hide_aux_string));
2410     si->signal_connect_hide_lookup_table (
2411         slot (slot_hide_lookup_table));
2412
2413     si->signal_connect_update_preedit_caret (
2414         slot (slot_update_preedit_caret));
2415     si->signal_connect_update_preedit_string (
2416         slot (slot_update_preedit_string));
2417     si->signal_connect_update_aux_string (
2418         slot (slot_update_aux_string));
2419     si->signal_connect_update_lookup_table (
2420         slot (slot_update_lookup_table));
2421
2422     si->signal_connect_commit_string (
2423         slot (slot_commit_string));
2424
2425     si->signal_connect_forward_key_event (
2426         slot (slot_forward_key_event));
2427
2428     si->signal_connect_register_properties (
2429         slot (slot_register_properties));
2430
2431     si->signal_connect_update_property (
2432         slot (slot_update_property));
2433
2434     si->signal_connect_beep (
2435         slot (slot_beep));
2436
2437     si->signal_connect_start_helper (
2438         slot (slot_start_helper));
2439
2440     si->signal_connect_stop_helper (
2441         slot (slot_stop_helper));
2442
2443     si->signal_connect_send_helper_event (
2444         slot (slot_send_helper_event));
2445
2446     si->signal_connect_get_surrounding_text (
2447         slot (slot_get_surrounding_text));
2448
2449     si->signal_connect_delete_surrounding_text (
2450         slot (slot_delete_surrounding_text));
2451 }
2452
2453 // Implementation of slot functions
2454 /**
2455  * @brief Show the preedit string area.
2456  *
2457  * The preedit string should be updated by calling
2458  * update_preedit_string before or right after this call.
2459  *
2460  * @param si the IMEngineInstace pointer
2461  */
2462 static void
2463 slot_show_preedit_string (IMEngineInstanceBase *si)
2464 {
2465     SCIM_DEBUG_FRONTEND(1) << "slot_show_preedit_string...\n";
2466
2467     GtkIMContextSCIM *ic = static_cast<GtkIMContextSCIM *> (si->get_frontend_data ());
2468
2469     if (ic && ic->impl && _focused_ic == ic) {
2470         if (ic->impl->use_preedit) {
2471             if (!ic->impl->preedit_started) {
2472                 g_signal_emit_by_name (_focused_ic, "preedit-start");
2473                 ic->impl->preedit_started = true;
2474             }
2475             //if (ic->impl->preedit_string.length ())
2476             //    g_signal_emit_by_name (_focused_ic, "preedit-changed");
2477         } else {
2478             _panel_client.show_preedit_string (ic->id);
2479         }
2480     }
2481 }
2482
2483 /**
2484  * @brief Show the aux string area.
2485  *
2486  * The aux string should be updated by calling
2487  * update_aux_string before or right after this call.
2488  *
2489  * The aux string can contain any additional information whatever
2490  * the input method engine want.
2491  *
2492  * @param si the IMEngineInstace pointer
2493  */
2494 static void
2495 slot_show_aux_string (IMEngineInstanceBase *si)
2496 {
2497     SCIM_DEBUG_FRONTEND(1) << "slot_show_aux_string...\n";
2498
2499     GtkIMContextSCIM *ic = static_cast<GtkIMContextSCIM *> (si->get_frontend_data ());
2500
2501     if (ic && ic->impl && _focused_ic == ic)
2502         _panel_client.show_aux_string (ic->id);
2503 }
2504
2505 /**
2506  * @brief Show the lookup table area.
2507  *
2508  * The lookup table should be updated by calling
2509  * update_lookup_table before or right after this call.
2510  *
2511  * @param si the IMEngineInstace pointer
2512  */
2513 static void
2514 slot_show_lookup_table (IMEngineInstanceBase *si)
2515 {
2516     SCIM_DEBUG_FRONTEND(1) << "slot_show_lookup_table...\n";
2517
2518     GtkIMContextSCIM *ic = static_cast<GtkIMContextSCIM *> (si->get_frontend_data ());
2519
2520     if (ic && ic->impl && _focused_ic == ic)
2521         _panel_client.show_lookup_table (ic->id);
2522 }
2523
2524 /**
2525  * @brief Hide the preedit string area.
2526  *
2527  * @param si the IMEngineInstace pointer
2528  */
2529 static void
2530 slot_hide_preedit_string (IMEngineInstanceBase *si)
2531 {
2532     SCIM_DEBUG_FRONTEND(1) << "slot_hide_preedit_string...\n";
2533
2534     GtkIMContextSCIM *ic = static_cast<GtkIMContextSCIM *> (si->get_frontend_data ());
2535
2536     if (ic && ic->impl && _focused_ic == ic) {
2537         bool emit = false;
2538         if (ic->impl->preedit_string.length ()) {
2539             ic->impl->preedit_string = WideString ();
2540             ic->impl->preedit_caret = 0;
2541             ic->impl->preedit_attrlist.clear ();
2542             emit = true;
2543         }
2544         if (ic->impl->use_preedit) {
2545             if (emit) g_signal_emit_by_name (ic, "preedit-changed");
2546             if (ic->impl->preedit_started) {
2547                 g_signal_emit_by_name (ic, "preedit-end");
2548                 ic->impl->preedit_started = false;
2549             }
2550         } else {
2551             _panel_client.hide_preedit_string (ic->id);
2552         }
2553     }
2554 }
2555
2556 /**
2557  * @brief Hide the aux string area.
2558  *
2559  * @param si the IMEngineInstace pointer
2560  */
2561 static void
2562 slot_hide_aux_string (IMEngineInstanceBase *si)
2563 {
2564     SCIM_DEBUG_FRONTEND(1) << "slot_hide_aux_string...\n";
2565
2566     GtkIMContextSCIM *ic = static_cast<GtkIMContextSCIM *> (si->get_frontend_data ());
2567
2568     if (ic && ic->impl && _focused_ic == ic)
2569         _panel_client.hide_aux_string (ic->id);
2570 }
2571
2572 /**
2573  * @brief Hide the lookup table area.
2574  *
2575  * @param si the IMEngineInstace pointer
2576  */
2577 static void
2578 slot_hide_lookup_table (IMEngineInstanceBase *si)
2579 {
2580     SCIM_DEBUG_FRONTEND(1) << "slot_hide_lookup_table...\n";
2581
2582     GtkIMContextSCIM *ic = static_cast<GtkIMContextSCIM *> (si->get_frontend_data ());
2583
2584     if (ic && ic->impl && _focused_ic == ic)
2585         _panel_client.hide_lookup_table (ic->id);
2586 }
2587
2588 /**
2589  * @brief Update the preedit caret position in the preedit string.
2590  *
2591  * @param si the IMEngineInstace pointer
2592  * @param caret - the new position of the preedit caret.
2593  */
2594 static void
2595 slot_update_preedit_caret (IMEngineInstanceBase *si, int caret)
2596 {
2597     SCIM_DEBUG_FRONTEND(1) << "slot_update_preedit_caret...\n";
2598
2599     GtkIMContextSCIM *ic = static_cast<GtkIMContextSCIM *> (si->get_frontend_data ());
2600
2601     if (ic && ic->impl && _focused_ic == ic && ic->impl->preedit_caret != caret) {
2602         ic->impl->preedit_caret = caret;
2603         if (ic->impl->use_preedit) {
2604             if (!ic->impl->preedit_started) {
2605                 g_signal_emit_by_name(_focused_ic, "preedit-start");
2606                 ic->impl->preedit_started = true;
2607             }
2608             g_signal_emit_by_name(ic, "preedit-changed");
2609         } else {
2610             _panel_client.update_preedit_caret (ic->id, caret);
2611         }
2612     }
2613 }
2614
2615 /**
2616  * @brief Update the content of the preedit string,
2617  *
2618  * @param si the IMEngineInstace pointer
2619  * @param str - the string content
2620  * @param attrs - the string attributes
2621  */
2622 static void
2623 slot_update_preedit_string (IMEngineInstanceBase *si,
2624                             const WideString & str,
2625                             const AttributeList & attrs)
2626 {
2627     SCIM_DEBUG_FRONTEND(1) << "slot_update_preedit_string...\n";
2628
2629     GtkIMContextSCIM *ic = static_cast<GtkIMContextSCIM *> (si->get_frontend_data ());
2630
2631     if (ic && ic->impl && _focused_ic == ic && (ic->impl->preedit_string != str || str.length ())) {
2632         ic->impl->preedit_string   = str;
2633         ic->impl->preedit_attrlist = attrs;
2634         if (ic->impl->use_preedit) {
2635             if (!ic->impl->preedit_started) {
2636                 g_signal_emit_by_name(_focused_ic, "preedit-start");
2637                 ic->impl->preedit_started = true;
2638             }
2639             ic->impl->preedit_caret    = str.length ();
2640             ic->impl->preedit_updating = true;
2641             g_signal_emit_by_name(ic, "preedit-changed");
2642             ic->impl->preedit_updating = false;
2643         } else {
2644             _panel_client.update_preedit_string (ic->id, str, attrs);
2645         }
2646     }
2647 }
2648
2649 /**
2650  * @brief Update the content of the aux string,
2651  *
2652  * @param si the IMEngineInstace pointer
2653  * @param str - the string content
2654  * @param attrs - the string attribute
2655  */
2656 static void
2657 slot_update_aux_string (IMEngineInstanceBase *si,
2658                         const WideString & str,
2659                         const AttributeList & attrs)
2660 {
2661     SCIM_DEBUG_FRONTEND(1) << "slot_update_aux_string...\n";
2662
2663     GtkIMContextSCIM *ic = static_cast<GtkIMContextSCIM *> (si->get_frontend_data ());
2664
2665     if (ic && ic->impl && _focused_ic == ic)
2666         _panel_client.update_aux_string (ic->id, str, attrs);
2667 }
2668
2669 /**
2670  * @brief Commit a string to the client application.
2671  *
2672  * The preedit string should be hid before calling
2673  * this method. Otherwise the clients which use
2674  * OnTheSpot input mode will flicker annoyingly.
2675  *
2676  * @param si the IMEngineInstace pointer
2677  * @param str - the string to be committed.
2678  */
2679 static void
2680 slot_commit_string (IMEngineInstanceBase *si,
2681                     const WideString & str)
2682 {
2683     SCIM_DEBUG_FRONTEND(1) << "slot_commit_string...\n";
2684
2685     GtkIMContextSCIM *ic = static_cast<GtkIMContextSCIM *> (si->get_frontend_data ());
2686
2687     if (ic)
2688         g_signal_emit_by_name (ic, "commit", utf8_wcstombs (str).c_str ());
2689 }
2690
2691 /**
2692  * @brief Forward a key event to the client application.
2693  *
2694  * @param si the IMEngineInstace pointer
2695  * @param key - the key event to be forwarded.
2696  */
2697 static void
2698 slot_forward_key_event (IMEngineInstanceBase *si,
2699                         const KeyEvent & key)
2700 {
2701     SCIM_DEBUG_FRONTEND(1) << "slot_forward_key_event...\n";
2702
2703     GtkIMContextSCIM *ic = static_cast<GtkIMContextSCIM *> (si->get_frontend_data ());
2704
2705     if (ic && _focused_ic == ic) {
2706         GdkEventKey gdkevent = keyevent_scim_to_gdk (ic, key, TRUE);
2707         if (!_fallback_instance->process_key_event (key) &&
2708             !gtk_im_context_filter_keypress (GTK_IM_CONTEXT (ic->slave), &gdkevent)) {
2709
2710             // To avoid timing issue, we need emit the signal directly, rather than put the event into the queue.
2711             if (_focused_widget) {
2712                 gboolean result;
2713                 g_signal_emit_by_name(_focused_widget, key.is_key_press () ? "key-press-event" : "key-release-event", &gdkevent, &result);
2714             } else {
2715                 gdk_event_put ((GdkEvent *) &gdkevent);
2716             }
2717         }
2718     }
2719 }
2720
2721 /**
2722  * @brief Update the content of the lookup table,
2723  *
2724  * @param si the IMEngineInstace pointer
2725  * @param table - the new LookupTable
2726  */
2727 static void
2728 slot_update_lookup_table (IMEngineInstanceBase *si,
2729                           const LookupTable & table)
2730 {
2731     SCIM_DEBUG_FRONTEND(1) << "slot_update_lookup_table...\n";
2732
2733     GtkIMContextSCIM *ic = static_cast<GtkIMContextSCIM *> (si->get_frontend_data ());
2734
2735     if (ic && ic->impl && _focused_ic == ic)
2736         _panel_client.update_lookup_table (ic->id, table);
2737 }
2738
2739 /**
2740  * @brief Register all properties of this IMEngineInstance into the FrontEnd.
2741  *
2742  * The old properties previously registered by other IMEngineInstance will be discarded,
2743  * so for each time focus_in() is called, all properties should be registered
2744  * no matter whether they had been registered before.
2745  *
2746  * @param si the IMEngineInstace pointer
2747  * @param properties the PropertyList contains all of the properties.
2748  */
2749 static void
2750 slot_register_properties (IMEngineInstanceBase *si,
2751                           const PropertyList & properties)
2752 {
2753     SCIM_DEBUG_FRONTEND(1) << "slot_register_properties...\n";
2754
2755     GtkIMContextSCIM *ic = static_cast<GtkIMContextSCIM *> (si->get_frontend_data ());
2756
2757     if (ic && ic->impl && _focused_ic == ic)
2758         _panel_client.register_properties (ic->id, properties);
2759 }
2760
2761 /**
2762  * @brief Update a registered property.
2763  *
2764  * Update a property which already registered by register_properties () method.
2765  *
2766  * @param si the IMEngineInstace pointer
2767  * @param property the property to be updated.
2768  */
2769 static void
2770 slot_update_property (IMEngineInstanceBase *si,
2771                       const Property & property)
2772 {
2773     SCIM_DEBUG_FRONTEND(1) << "slot_update_property ...\n";
2774
2775     GtkIMContextSCIM *ic = static_cast<GtkIMContextSCIM *> (si->get_frontend_data ());
2776
2777     if (ic && ic->impl && _focused_ic == ic)
2778         _panel_client.update_property (ic->id, property);
2779 }
2780
2781 /**
2782  * @brief Generate a short beep.
2783  *
2784  * @param si the IMEngineInstace pointer
2785  */
2786 static void
2787 slot_beep (IMEngineInstanceBase *si)
2788 {
2789     SCIM_DEBUG_FRONTEND(1) << "slot_beep ...\n";
2790
2791     GtkIMContextSCIM *ic = static_cast<GtkIMContextSCIM *> (si->get_frontend_data ());
2792
2793     if (ic && ic->impl && _focused_ic == ic)
2794         gdk_beep ();
2795 }
2796
2797 /**
2798  * @brief Start a Client Helper process.
2799  *
2800  * @param si the IMEngineInstace pointer
2801  * @param helper_uuid The UUID of the Helper object.
2802  */
2803 static void
2804 slot_start_helper (IMEngineInstanceBase *si,
2805                    const String &helper_uuid)
2806 {
2807     GtkIMContextSCIM *ic = static_cast<GtkIMContextSCIM *> (si->get_frontend_data ());
2808
2809     SCIM_DEBUG_FRONTEND(1) << "slot_start_helper helper= " << helper_uuid << " context="
2810                            << (ic ? ic->id : -1) << " ic=" << ic
2811                            << " ic-uuid=" << ((ic && ic->impl) ? ic->impl->si->get_factory_uuid () : "") << "...\n";
2812
2813     if (ic && ic->impl)
2814         _panel_client.start_helper (ic->id, helper_uuid);
2815 }
2816
2817 /**
2818  * @brief Stop a Client Helper process which was started by start_helper.
2819  *
2820  * @param si the IMEngineInstace pointer
2821  * @param helper_uuid The UUID of the Helper object.
2822  */
2823 static void
2824 slot_stop_helper (IMEngineInstanceBase *si,
2825                   const String &helper_uuid)
2826 {
2827     GtkIMContextSCIM *ic = static_cast<GtkIMContextSCIM *> (si->get_frontend_data ());
2828
2829     SCIM_DEBUG_FRONTEND(1) << "slot_stop_helper helper= " << helper_uuid << " context=" << (ic ? ic->id : -1) << " ic=" << ic << "...\n";
2830
2831     if (ic && ic->impl)
2832         _panel_client.stop_helper (ic->id, helper_uuid);
2833 }
2834
2835 /**
2836  * @brief Send an events transaction to a client helper process.
2837  *
2838  * @param si the IMEngineInstace pointer
2839  * @param helper_uuid The UUID of the Helper object.
2840  * @param trans The transaction which contains events.
2841  */
2842 static void
2843 slot_send_helper_event (IMEngineInstanceBase *si,
2844                         const String      &helper_uuid,
2845                         const Transaction &trans)
2846 {
2847     GtkIMContextSCIM *ic = static_cast<GtkIMContextSCIM *> (si->get_frontend_data ());
2848
2849     SCIM_DEBUG_FRONTEND(1) << "slot_send_helper_event helper= " << helper_uuid << " context="
2850                            << (ic ? ic->id : -1) << " ic=" << ic
2851                            << " ic-uuid=" << ((ic && ic->impl) ? ic->impl->si->get_factory_uuid () : "") << "...\n";
2852
2853     if (ic && ic->impl)
2854         _panel_client.send_helper_event (ic->id, helper_uuid, trans);
2855 }
2856
2857 /**
2858  * @brief Retrieves context around the insertion point.
2859  *
2860  * @param si the IMEngineInstace pointer
2861  * @param text          location to store the context string around the insertion point.
2862  * @param cursor        location to store index of the insertion cursor within @text.
2863  * @param maxlen_before the maxmium length of context string to be retrieved
2864  *                      before the cursor; -1 means unlimited.
2865  * @param maxlen_after  the maxmium length of context string to be retrieved
2866  *                      after the cursor; -1 means unlimited.
2867  *
2868  * @return true if surrounding text was provided.
2869  */
2870 static bool
2871 slot_get_surrounding_text (IMEngineInstanceBase *si,
2872                            WideString            &text,
2873                            int                   &cursor,
2874                            int                    maxlen_before,
2875                            int                    maxlen_after)
2876 {
2877     SCIM_DEBUG_FRONTEND(1) << "slot_get_surrounding_text ...\n";
2878
2879     GtkIMContextSCIM *ic = static_cast<GtkIMContextSCIM *> (si->get_frontend_data ());
2880
2881     if (ic && ic->impl && _focused_ic == ic) {
2882         gchar *surrounding = NULL;
2883         gint   cursor_index;
2884         if (gtk_im_context_get_surrounding (GTK_IM_CONTEXT (_focused_ic), &surrounding, &cursor_index)) {
2885             SCIM_DEBUG_FRONTEND(2) << "Surrounding text: " << surrounding <<"\n";
2886             SCIM_DEBUG_FRONTEND(2) << "Cursor Index    : " << cursor_index <<"\n";
2887             WideString before (utf8_mbstowcs (String (surrounding, surrounding + cursor_index)));
2888             WideString after (utf8_mbstowcs (String (surrounding + cursor_index)));
2889             if (maxlen_before > 0 && ((unsigned int)maxlen_before) < before.length ())
2890                 before = WideString (before.begin () + (before.length () - maxlen_before), before.end ());
2891             else if (maxlen_before == 0) before = WideString ();
2892             if (maxlen_after > 0 && ((unsigned int)maxlen_after) < after.length ())
2893                 after = WideString (after.begin (), after.begin () + maxlen_after);
2894             else if (maxlen_after == 0) after = WideString ();
2895             text = before + after;
2896             cursor = before.length ();
2897             return true;
2898         }
2899     }
2900     return false;
2901 }
2902
2903 /**
2904  * @brief Ask the client to delete characters around the cursor position.
2905  *
2906  * @param si the IMEngineInstace pointer
2907  * @param offset offset from cursor position in chars;
2908  *               a negative value means start before the cursor.
2909  * @param len number of characters to delete.
2910  *
2911  * @return true if the signal was handled.
2912  */
2913 static bool
2914 slot_delete_surrounding_text (IMEngineInstanceBase *si,
2915                               int                    offset,
2916                               int                    len)
2917 {
2918     SCIM_DEBUG_FRONTEND(1) << "slot_delete_surrounding_text ...\n";
2919
2920     GtkIMContextSCIM *ic = static_cast<GtkIMContextSCIM *> (si->get_frontend_data ());
2921
2922     if (ic && ic->impl && _focused_ic == ic)
2923         return gtk_im_context_delete_surrounding (GTK_IM_CONTEXT (_focused_ic), offset, len);
2924     return false;
2925 }
2926
2927 static void
2928 reload_config_callback (const ConfigPointer &config)
2929 {
2930     SCIM_DEBUG_FRONTEND(1) << "reload_config_callback...\n";
2931
2932     _frontend_hotkey_matcher.load_hotkeys (config);
2933     _imengine_hotkey_matcher.load_hotkeys (config);
2934
2935     KeyEvent key;
2936
2937     scim_string_to_key (key,
2938                         config->read (String (SCIM_CONFIG_HOTKEYS_FRONTEND_VALID_KEY_MASK),
2939                                       String ("Shift+Control+Alt+Lock")));
2940
2941     _valid_key_mask = (key.mask > 0)?(key.mask):0xFFFF;
2942     _valid_key_mask |= SCIM_KEY_ReleaseMask;
2943     // Special treatment for two backslash keys on jp106 keyboard.
2944     _valid_key_mask |= SCIM_KEY_QuirkKanaRoMask;
2945
2946     _on_the_spot = config->read (String (SCIM_CONFIG_FRONTEND_ON_THE_SPOT), _on_the_spot);
2947     _shared_input_method = config->read (String (SCIM_CONFIG_FRONTEND_SHARED_INPUT_METHOD), _shared_input_method);
2948     _use_key_snooper = config->read (String (SCIM_CONFIG_FRONTEND_GTK_IMMODULE_USE_KEY_SNOOPER), _use_key_snooper);
2949
2950     // Get keyboard layout setting
2951     // Flush the global config first, in order to load the new configs from disk.
2952     scim_global_config_flush ();
2953
2954     _keyboard_layout = scim_get_default_keyboard_layout ();
2955 }
2956
2957 static void
2958 fallback_commit_string_cb (IMEngineInstanceBase  *si,
2959                            const WideString      &str)
2960 {
2961     if (_focused_ic && _focused_ic->impl)
2962         g_signal_emit_by_name (_focused_ic, "commit", utf8_wcstombs (str).c_str ());
2963 }
2964
2965 /*
2966 vi:ts=4:expandtab:nowrap
2967 */