Back to index

im-sdk  12.3.91
gtkimcontextiiim.c
Go to the documentation of this file.
00001 /* GTK - The GIMP Toolkit
00002  * Copyright (C) 2000 Red Hat Software
00003  * Copyright (C) 2003 Motonobu Ichimura
00004  *
00005  * This library is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU Lesser General Public
00007  * License as published by the Free Software Foundation; either
00008  * version 2 of the License, or (at your option) any later version.
00009  *
00010  * This library is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  * Lesser General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU Lesser General Public
00016  * License along with this library; if not, write to the
00017  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00018  * Boston, MA 02111-1307, USA.
00019  *
00020  * Authors: Motonobu Ichimura <famao@momonga-linux.org>
00021  *
00022  */
00023 
00024 #include <config.h>
00025 #include <locale.h>
00026 #include <libintl.h>
00027 #include <gdk/gdkkeysyms.h>
00028 #include <gdk/gdkx.h>
00029 #include <gtk/gtk.h>
00030 #include <gtk/gtkimmodule.h>
00031 #include <gtk/gtkinvisible.h>
00032 #include <gtk/gtkimcontextsimple.h>
00033 #include <gdk/gdkproperty.h>
00034 #include <gdk/gdkselection.h>
00035 #include <stdlib.h>
00036 #include <string.h>
00037 #include <X11/Xatom.h>
00038 #include "iiimcf.h"
00039 #include "gtkimcontextiiim.h"
00040 #include "imaux.h"
00041 #include "imswitcher.h"
00042 #include "IIIMGdkEventKey.h"
00043 
00044 #ifdef DEBUG
00045 #define DEBUG_DO(x) (x)
00046 #else
00047 #define DEBUG_DO(x)
00048 #endif
00049 
00050 #define _IS_DEAD_KEY(keyval,sym)   ((keyval) == GDK_dead_ ## sym)
00051 #if defined(GDK_dead_hook) && defined(GDK_dead_horn)
00052 #define IS_DEAD_KEY(keyval)               \
00053   (_IS_DEAD_KEY(keyval, grave) ||         \
00054    _IS_DEAD_KEY(keyval, acute) ||         \
00055    _IS_DEAD_KEY(keyval, circumflex) ||           \
00056    _IS_DEAD_KEY(keyval, tilde) ||         \
00057    _IS_DEAD_KEY(keyval, macron) ||        \
00058    _IS_DEAD_KEY(keyval, breve) ||         \
00059    _IS_DEAD_KEY(keyval, abovedot) ||             \
00060    _IS_DEAD_KEY(keyval, diaeresis) ||            \
00061    _IS_DEAD_KEY(keyval, abovering) ||            \
00062    _IS_DEAD_KEY(keyval, doubleacute) ||          \
00063    _IS_DEAD_KEY(keyval, caron) ||         \
00064    _IS_DEAD_KEY(keyval, cedilla) ||              \
00065    _IS_DEAD_KEY(keyval, ogonek) ||        \
00066    _IS_DEAD_KEY(keyval, iota) ||          \
00067    _IS_DEAD_KEY(keyval, voiced_sound) ||  \
00068    _IS_DEAD_KEY(keyval, semivoiced_sound) ||     \
00069    _IS_DEAD_KEY(keyval, belowdot) ||             \
00070    _IS_DEAD_KEY(keyval, hook) ||          \
00071    _IS_DEAD_KEY(keyval, horn))
00072 #else /* !GDK_dead_hook || !GDK_dead_horn */
00073 #define IS_DEAD_KEY(keyval)               \
00074   (_IS_DEAD_KEY(keyval, grave) ||         \
00075    _IS_DEAD_KEY(keyval, acute) ||         \
00076    _IS_DEAD_KEY(keyval, circumflex) ||           \
00077    _IS_DEAD_KEY(keyval, tilde) ||         \
00078    _IS_DEAD_KEY(keyval, macron) ||        \
00079    _IS_DEAD_KEY(keyval, breve) ||         \
00080    _IS_DEAD_KEY(keyval, abovedot) ||             \
00081    _IS_DEAD_KEY(keyval, diaeresis) ||            \
00082    _IS_DEAD_KEY(keyval, abovering) ||            \
00083    _IS_DEAD_KEY(keyval, doubleacute) ||          \
00084    _IS_DEAD_KEY(keyval, caron) ||         \
00085    _IS_DEAD_KEY(keyval, cedilla) ||              \
00086    _IS_DEAD_KEY(keyval, ogonek) ||        \
00087    _IS_DEAD_KEY(keyval, iota) ||          \
00088    _IS_DEAD_KEY(keyval, voiced_sound) ||  \
00089    _IS_DEAD_KEY(keyval, semivoiced_sound) ||     \
00090    _IS_DEAD_KEY(keyval, belowdot))
00091 #endif /* !GDK_dead_hook || !GDK_dead_horn */
00092 
00093 typedef struct _CandidateWindow CandidateWindow;
00094 
00095 /* Style for gtk input method preedit/status */
00096 struct _GtkIIIMInfo
00097 {
00098 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 2)
00099   GdkScreen *screen;
00100 #endif
00101   IIIMCF_handle iiim;
00102 
00103   /* locale is obtained from $IIIM_LOCALE, or LC_CTYPE seeting. */
00104   char *locale;
00105 
00106 #ifdef DIRTY_HACK_FOR_TEST_PURPOSE
00107   /* le is obtained from $IIIM_LE */
00108   char *le_name;
00109 #endif
00110 
00111   GSList *ics;
00112 
00113   SwitcherInfo *switcher_info;
00114 
00115   gchar *le_list;
00116   /*
00117     The following GtkSettings are used to configure appearance of
00118     input method status windows.
00119 
00120     status_style_setting:
00121        GTK_IM_STATUS_NOTHING
00122          IIIMGCF does not show the status window, but somebody else perhaps 
00123          GIMLET shows it on their UI.
00124        GTK_IM_STATUS_CALLBACK
00125          IIIMGCF shows the status window attached to application frame
00126          window.
00127        GTK_IM_STATUS_NONE
00128          no status window anywhere.
00129    */
00130   GtkSettings *settings;
00131   gulong status_set;
00132   GtkIMStatusStyle status_style_setting;
00133 };
00134 
00135 /* A candiate window */
00136 struct _CandidateWindow
00137 {
00138   GtkWidget *tree;
00139   GtkWidget *toplevel;
00140   GtkWidget *frame;
00141 
00142   /* Toplevel window to which the candiddate window corresponds */
00143   GtkWidget *app_toplevel;
00144   GtkListStore *store;
00145   gint choices_per_window;
00146   gint number_of_rows;
00147   gint number_of_columns;
00148   gint direction;
00149 
00150   gulong destroy_handler_id;
00151   gulong configure_handler_id;
00152   gulong button_press_handler_id;
00153 };
00154 
00155 /* A context status window; these are kept in the status_windows list. */
00156 struct _StatusWindow
00157 {
00158   GtkWidget *window;
00159 
00160   /* Toplevel window to which the status window corresponds */
00161   GtkWidget *toplevel;
00162 
00163   /* Currently focused GtkIMContextIIIM for the toplevel, if any */
00164   GtkIMContextIIIM *context;
00165 };
00166 
00167 typedef enum {
00168   ON_DESKTOP_PANEL,
00169   ATTACH_TO_APP_FRAME
00170 } IMStatusPlacement;
00171 
00172 typedef enum {
00173   CONV_OFF = 0,
00174   CONV_ON = 1
00175 } ICConversionMode;
00176 
00177 typedef enum {
00178   IM_OFF = 0,
00179   IM_ON
00180 } IMEnabled;
00181 
00182 typedef struct {
00183   IMEnabled im_enabled;
00184   IMEnabled status_enabled;
00185   IMEnabled lookup_enabled;
00186   IMStatusPlacement status_placement;
00187 } IMSettings;
00188 
00189 static IMSettings current_setting;
00190 static gboolean current_setting_enabled;
00191 static GdkAtom im_settings_atom = GDK_NONE;
00192 static const char *_IM_SETTINGS = "_IM_SETTINGS";
00193 
00194 static gboolean on_status_toplevel_configure (GtkWidget         *toplevel,
00195                                          GdkEventConfigure *event,
00196                                          StatusWindow      *status_window);
00197 static void im_context_iiim_class_init (GtkIMContextIIIMClass * class);
00198 static void im_context_iiim_init (GtkIMContextIIIM * im_context);
00199 static void im_context_iiim_finalize (GObject * obj);
00200 static void im_context_iiim_set_client_window (GtkIMContext * context,
00201                                           GdkWindow * client_window);
00202 static gboolean im_context_iiim_filter_keypress (GtkIMContext * context,
00203                                            GdkEventKey * key);
00204 static void im_context_iiim_reset (GtkIMContext * context);
00205 static void im_context_iiim_focus_in (GtkIMContext * context);
00206 static void im_context_iiim_focus_out (GtkIMContext * context);
00207 
00208 static void im_context_iiim_set_cursor_location (GtkIMContext * context,
00209                                            GdkRectangle * area);
00210 static void im_context_iiim_set_use_preedit (GtkIMContext * context,
00211                                         gboolean use_preedit);
00212 static void im_context_iiim_get_preedit_string (GtkIMContext * context,
00213                                           gchar ** str,
00214                                           PangoAttrList ** attrs,
00215                                           gint * cursor_pos);
00216 
00217 
00218 /* Session Context */
00219 static IIIMCF_context iiim_get_session_context (GtkIMContextIIIM *
00220                                           context_iiim);
00221 
00222 /* Candidate Window */
00223 static IIIMCF_lookup_choice iiim_get_lookup_choice (GtkIMContextIIIM *
00224                                               context_iiim);
00225 static GtkWidget *iiim_get_candidate_window (GtkIMContextIIIM * context_iiim);
00226 static void iiim_candidate_show (GtkIMContextIIIM * context_iiim);
00227 static void iiim_destroy_candidate_window (GtkIMContextIIIM * context_iiim);
00228 static gboolean iiim_candidate_window_configure (GtkWidget * toplevel,
00229                                            GdkEventConfigure * event,
00230                                            GtkIMContextIIIM *context_iiim);
00231 static void iiim_candidate_window_button_press (GtkWidget *widget,
00232                                           GdkEventButton *event,
00233                                           GtkIMContextIIIM *context_iiim);
00234 
00235 /* Key Event */
00236 static void iiim_keylist_free (GtkIMContextIIIM * context_iiim);
00237 static gint check_stored_keyevent (GtkIMContext * context,
00238                                GdkEventKey * event);
00239 
00240 /* Event */
00241 static gboolean iiim_event_dispatch (GtkIMContextIIIM * context_iiim);
00242 static gboolean forward_event (GtkIMContextIIIM * context_iiim,
00243                             IIIMCF_event ev, IIIMF_status *st_ret);
00244 
00245 static gchar *utf16_to_utf8 (IIIMCF_text text);
00246 
00247 static void
00248 set_sc_client_window (GtkIMContextIIIM * context_iiim,
00249                     GdkWindow * client_window, gboolean send_signal);
00250 
00251 /* Status */
00252 static void update_client_widget   (GtkIMContextIIIM *context_iiim);
00253 static void update_status_window   (GtkIMContextIIIM*context_iiim);
00254 
00255 static StatusWindow *status_window_get      (GtkWidget    *toplevel);
00256 static void          status_window_free     (StatusWindow *status_window);
00257 static void          status_window_set_text (StatusWindow *status_window,
00258                                         const gchar  *text);
00259 
00260 static void reinitialize_sc (GtkIMContextIIIM * context_iiim,
00261                           gboolean send_signal);
00262 
00263 
00264 static GObjectClass *parent_class;
00265 
00266 GType gtk_type_im_context_iiim = 0;
00267 
00268 static GSList *open_iiims = NULL;
00269 
00270 static gboolean iiim_is_initialized = FALSE;
00271 static IIIMCF_handle iiim = NULL;
00272 
00273 /* List of status windows for different toplevels */
00274 static GSList *status_windows = NULL;
00275 
00276 void
00277 im_context_iiim_register_type (GTypeModule * type_module)
00278 {
00279   static const GTypeInfo im_context_iiim_info = {
00280     sizeof (GtkIMContextIIIMClass),
00281     (GBaseInitFunc) NULL,
00282     (GBaseFinalizeFunc) NULL,
00283     (GClassInitFunc) im_context_iiim_class_init,
00284     NULL,
00285     NULL,
00286     sizeof (GtkIMContextIIIM),
00287     0,
00288     (GInstanceInitFunc) im_context_iiim_init,
00289   };
00290 
00291   gtk_type_im_context_iiim =
00292     g_type_module_register_type (type_module,
00293                              GTK_TYPE_IM_CONTEXT,
00294                              "GtkIMContextIIIM",
00295                              &im_context_iiim_info, 0);
00296 }
00297 
00298 
00299 static void
00300 im_context_iiim_class_init (GtkIMContextIIIMClass * class)
00301 {
00302   GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS (class);
00303   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
00304 
00305   parent_class = g_type_class_peek_parent (class);
00306 
00307 
00308   im_context_class->set_client_window = im_context_iiim_set_client_window;
00309   im_context_class->filter_keypress = im_context_iiim_filter_keypress;
00310   im_context_class->reset = im_context_iiim_reset;
00311   im_context_class->get_preedit_string = im_context_iiim_get_preedit_string;
00312   im_context_class->focus_in = im_context_iiim_focus_in;
00313   im_context_class->focus_out = im_context_iiim_focus_out;
00314   im_context_class->set_cursor_location = im_context_iiim_set_cursor_location;
00315   im_context_class->set_use_preedit = im_context_iiim_set_use_preedit;
00316   gobject_class->finalize = im_context_iiim_finalize;
00317   return;
00318 }
00319 
00320 /* 
00321  * Update Status Window Message 
00322  */
00323 static void
00324 status_callback (GtkIMContextIIIM * context_iiim)
00325 {
00326   gchar *utf8;
00327   IIIMCF_context c = iiim_get_session_context (context_iiim);
00328   IIIMCF_text text;
00329   IIIMF_status st;
00330 
00331   if (!context_iiim->status_window)
00332     {
00333       if (!context_iiim->has_focus)
00334        {
00335          context_iiim->has_focus = TRUE;
00336          /* tell switcher that I'm the current client */
00337          im_context_switcher_set_focus (context_iiim);
00338        }
00339       update_status_window (context_iiim);
00340     }
00341   if (!context_iiim->status_window)
00342     return;
00343 
00344   st = iiimcf_get_status_text (c, &text);
00345   if (st == IIIMF_STATUS_NO_STATUS_TEXT || st != IIIMF_STATUS_SUCCESS)
00346     {
00347       status_window_set_text (context_iiim->status_window, "");
00348       im_context_switcher_set_status_text (context_iiim, " ");
00349       return;
00350     }
00351   utf8 = utf16_to_utf8 (text);
00352 
00353   if (current_setting_enabled &&
00354       (current_setting.im_enabled == IM_OFF ||
00355        current_setting.status_enabled == IM_OFF))
00356     status_window_set_text (context_iiim->status_window, "");
00357   else if ((current_setting_enabled &&
00358            current_setting.status_placement == ATTACH_TO_APP_FRAME) ||
00359           !current_setting_enabled)
00360     {
00361       status_window_set_text (context_iiim->status_window, utf8);
00362       im_context_switcher_set_status_text (context_iiim, utf8);
00363     }
00364   else
00365     {
00366       status_window_set_text (context_iiim->status_window, "");
00367       im_context_switcher_set_status_text (context_iiim, utf8);
00368     }
00369 
00370   g_free (utf8);
00371 }
00372 
00373 /* get Lang List */
00374 IIIMCF_language*
00375 iiim_get_languages (GtkIIIMInfo *info, int *n_lang)
00376 {
00377   IIIMF_status st;
00378   IIIMCF_handle iiim = info->iiim;
00379   IIIMCF_language *lang = NULL;
00380 
00381   if (!iiim)
00382     return NULL;
00383 
00384   st = iiimcf_get_supported_languages (iiim, n_lang, &lang);
00385   if (st != IIIMF_STATUS_SUCCESS)
00386     return NULL;
00387 
00388   return lang;
00389 }
00390 
00391 static char*
00392 format_iiimcf_string (const IIIMP_card16 *u16str)
00393 {
00394   return (u16str != NULL ?
00395          g_utf16_to_utf8 ((const gunichar2 *) u16str, -1, NULL, NULL, NULL) :
00396          NULL);
00397 }
00398 
00399 static IIIMP_card16 *
00400 format_utf8_string (char *utf8str)
00401 {
00402   return (utf8str != NULL ?
00403           g_utf8_to_utf16 (utf8str, -1, NULL, NULL, NULL) :
00404          NULL);
00405 }
00406 
00407 static char *
00408 get_imelist (int nimeinfos, IIIMCF_imeinfo_rec **ppimeinfos)
00409 {
00410   int i;
00411   char *imelist = NULL, *p;
00412 
00413   imelist = g_new0 (char, 1024);
00414 
00415   p = imelist;
00416   for (i = 0; i < nimeinfos; ++i)
00417      {
00418        char *imename = NULL;
00419        char ime_seperator = ',';
00420 
00421        if (!ppimeinfos[i]->enable) continue;
00422        
00423        if (i) *p++ = ime_seperator;
00424 
00425        imename = format_iiimcf_string (ppimeinfos[i]->imename);
00426 
00427        strcpy (p, imename); /* imename shouldn't be NULL */
00428        p += strlen (imename);
00429 
00430        g_free (imename);
00431      }
00432 
00433   if (*imelist == 0)
00434     {
00435        g_free (imelist);
00436        imelist = NULL;
00437     }
00438 
00439   return imelist;
00440 }
00441 
00442 /*
00443  * Get IM List
00444  */
00445 static void
00446 iiim_get_im_list (GtkIIIMInfo * info)
00447 {
00448   const IIIMP_card16 *u16idname, *u16hrn, *u16domain;
00449   int input_methods_n;
00450   gint i;
00451   gsize len = 0;
00452   IIIMCF_handle handle = info->iiim;
00453   IIIMCF_input_method *input_methods;
00454   IIIMF_status st;
00455   const char *separator = ";";
00456   const char *ime_delimiter = "-";
00457   gsize separator_len;
00458   gchar *le_list, *ptr;
00459   char *lename = NULL;
00460   char *imelist;
00461   IIIMCF_imeinfo_rec **ppimeinfos;
00462   int nimeinfos;
00463 
00464   separator_len = strlen (separator);
00465 
00466   if (!handle)
00467     return;
00468 
00469   st = iiimcf_get_supported_input_methods (handle,
00470                                       &input_methods_n, &input_methods);
00471   if (st != IIIMF_STATUS_SUCCESS)
00472     return;
00473 
00474   /* First part, getting length */
00475   for (i = 0; i < input_methods_n; i++)
00476     {
00477       gint nlangs;
00478       gint j;
00479       IIIMCF_language *plangs;
00480       char *langid;
00481 
00482       st = iiimcf_get_input_method_desc (input_methods[i], &u16idname,
00483                                     &u16hrn, &u16domain);
00484       if (st != IIIMF_STATUS_SUCCESS)
00485        goto error;
00486 
00487       lename = format_iiimcf_string (u16hrn);
00488 
00489       st = iiimcf_get_input_method_imeinfos (input_methods[i],
00490                                         &nimeinfos, &ppimeinfos);
00491       if (st != IIIMF_STATUS_SUCCESS)
00492        goto error;
00493 
00494       /* append the list of IME name to lename */
00495       if (nimeinfos && ppimeinfos && (imelist = get_imelist (nimeinfos, ppimeinfos)))
00496        {
00497           char *tmp_lename = g_new0 (char, strlen (lename) + strlen (imelist) + 2);
00498 
00499           sprintf (tmp_lename, "%s%s%s", lename, ime_delimiter, imelist);
00500           g_free (lename);
00501           lename = tmp_lename;
00502           g_free (imelist);
00503         }
00504 
00505       st = iiimcf_get_input_method_languages (input_methods[i],
00506                                          &nlangs, &plangs);
00507       if (st != IIIMF_STATUS_SUCCESS)
00508        goto error;
00509 
00510       for (j = 0; j < nlangs; j++)
00511        {
00512          st = iiimcf_get_language_id (plangs[j],
00513                                    (const char **)&langid);
00514          len += (lename != NULL ? strlen (lename) : 0) + 1 + strlen (langid);
00515          len += separator_len;
00516        }
00517       if (lename != NULL)
00518        {
00519          g_free (lename);
00520          lename = NULL;
00521        }
00522     }
00523 
00524   /* Second part, building string */
00525   le_list = g_new (gchar, len + 1);
00526 
00527   ptr = le_list;
00528   for (i = 0; i < input_methods_n; i++)
00529     {
00530       gint nlangs;
00531       gint j;
00532       IIIMCF_language *plangs;
00533       char *langid;
00534 
00535       st = iiimcf_get_input_method_desc (input_methods[i], &u16idname,
00536                                     &u16hrn, &u16domain);
00537       lename = format_iiimcf_string (u16hrn);
00538 
00539       st = iiimcf_get_input_method_imeinfos (input_methods[i],
00540                                               &nimeinfos, &ppimeinfos);
00541       if (st != IIIMF_STATUS_SUCCESS)
00542         goto error;
00543 
00544       /* append the list of IME name to lename */
00545       if (nimeinfos && ppimeinfos && (imelist = get_imelist (nimeinfos, ppimeinfos)))
00546         {
00547            char *tmp_lename = g_new0 (char, strlen (lename) + strlen (imelist) + 2);
00548 
00549            sprintf (tmp_lename, "%s%s%s", lename, ime_delimiter, imelist);
00550            g_free (lename);
00551            lename = tmp_lename;
00552            g_free (imelist);
00553         }
00554 
00555       st = iiimcf_get_input_method_languages (input_methods[i],
00556                                          &nlangs, &plangs);
00557       if (st != IIIMF_STATUS_SUCCESS)
00558        goto error;
00559 
00560       st = iiimcf_get_language_id (plangs[0],
00561                                (const char **)&langid);
00562       for (j = 0; j < nlangs; j++)
00563        {
00564          st = iiimcf_get_language_id (plangs[j],
00565                                    (const char **)&langid);
00566          ptr = g_stpcpy (ptr, langid);
00567          ptr = g_stpcpy (ptr, ":");
00568          ptr = g_stpcpy (ptr, lename);
00569          ptr = g_stpcpy (ptr, separator);
00570        }
00571       if (lename != NULL)
00572        {
00573          g_free (lename);
00574          lename = NULL;
00575        }
00576     }
00577   le_list[len - 1] = 0;
00578 
00579   info->le_list = le_list;
00580  error:
00581   if (lename != NULL)
00582     g_free (lename);
00583 
00584   return;
00585 }
00586 
00587 /*
00588  * Event Dispatch
00589  */
00590 static gboolean
00591 iiim_event_dispatch (GtkIMContextIIIM * context_iiim)
00592 {
00593   IIIMCF_context c;
00594   IIIMF_status st;
00595   IIIMCF_event ev;
00596   IIIMCF_event_type et;
00597   gboolean result = TRUE;
00598 
00599   c = iiim_get_session_context (context_iiim);
00600   if (!c)
00601     return FALSE;
00602 
00603   while ((st = iiimcf_get_next_event (c, &ev)) == IIIMF_STATUS_SUCCESS)
00604     {
00605       st = iiimcf_get_event_type (ev, &et);
00606       if (st != IIIMF_STATUS_SUCCESS)
00607        continue;
00608       DEBUG_DO (g_message ("event type %d", et));
00609       switch (et)
00610        {
00611        case IIIMCF_EVENT_TYPE_KEYEVENT:
00612          {
00613            /* We need to send GdkEventKey to a client */
00614 
00615            /* GTK+ is in ASYNC mode, key event will be always sent to server by GTK+,
00616             * if the key event isn't consumed by server, it is necessary for client framework
00617             * to send the key press event and fake one release event to client.
00618             * It's required that LE shouldn't try to send back the key release event,
00619             * otherwise, duplicate key event would be wrongly created and sent to client.
00620             */
00621 
00622            GdkEventKey *event;
00623            GdkEventKey release_event;
00624            IIIMCF_keyevent kev;
00625 
00626            st = iiimcf_get_keyevent_value (ev, &kev);
00627            if (st != IIIMF_STATUS_SUCCESS)
00628              {
00629               result = FALSE;
00630               break;
00631              }
00632            event = (GdkEventKey *) gdk_event_new (GDK_KEY_PRESS);
00633            st = convert_IIIMCF_keyevent_to_GdkEventKey (&kev, event);
00634            DEBUG_DO (g_message ("event created %s %d %d %d",
00635                              gdk_keyval_name (event->keyval),
00636                              event->hardware_keycode, event->length,
00637                              event->group));
00638            if (st != IIIMF_STATUS_SUCCESS)
00639              {
00640               gdk_event_free ((GdkEvent *) event);
00641               result = FALSE;
00642               break;
00643              }
00644            event->window = context_iiim->client_window;
00645 
00646            /* 
00647             * increment client_window's ref count.
00648             * gdk_event_free decrements it.
00649             */
00650 
00651            g_object_ref (event->window);
00652            gdk_event_put ((GdkEvent *) event);
00653 
00654            /* Store GdkEventKey */
00655            context_iiim->keylist =
00656              g_slist_append (context_iiim->keylist, event);
00657 
00658            /* create Fake Key Release Event */
00659            memset (&release_event, 0, sizeof (GdkEventKey));
00660            release_event.type = GDK_KEY_RELEASE;
00661            release_event.window = context_iiim->client_window;
00662            release_event.state = event->state;
00663            release_event.keyval = event->keyval;
00664            release_event.hardware_keycode = event->hardware_keycode;
00665            gdk_event_put ((GdkEvent *) & release_event);
00666          }
00667          break;
00668        case IIIMCF_EVENT_TYPE_TRIGGER_NOTIFY:
00669          im_context_switcher_set_conversion_mode (context_iiim);
00670          break;
00671        case IIIMCF_EVENT_TYPE_UI_PREEDIT_START:
00672          DEBUG_DO (g_message ("preedit start"));
00673          if (!context_iiim->finalizing)
00674            g_signal_emit_by_name (context_iiim, "preedit_start");
00675          break;
00676        case IIIMCF_EVENT_TYPE_UI_PREEDIT_CHANGE:
00677          DEBUG_DO (g_message ("preedit changed"));
00678          if (!context_iiim->finalizing)
00679            g_signal_emit_by_name (context_iiim, "preedit_changed");
00680          break;
00681        case IIIMCF_EVENT_TYPE_UI_PREEDIT_DONE:
00682          DEBUG_DO (g_message ("preedit end"));
00683          /*
00684            call preedit_changed here, otherwise
00685            preedit text on gtkview and gtkentry remains when 
00686            conversion is turned off.
00687          */
00688          if (!context_iiim->finalizing)
00689            {
00690              g_signal_emit_by_name (context_iiim, "preedit_changed");
00691              g_signal_emit_by_name (context_iiim, "preedit_end");
00692            }
00693          break;
00694        case IIIMCF_EVENT_TYPE_UI_LOOKUP_CHOICE_START:
00695          DEBUG_DO (g_message ("lookup_choice start"));
00696          context_iiim->candidate_start = TRUE;
00697          iiim_candidate_show (context_iiim);
00698          break;
00699        case IIIMCF_EVENT_TYPE_UI_LOOKUP_CHOICE_CHANGE:
00700          iiim_candidate_show (context_iiim);
00701          DEBUG_DO (g_message ("lookup_choice change"));
00702          break;
00703        case IIIMCF_EVENT_TYPE_UI_LOOKUP_CHOICE_DONE:
00704          context_iiim->candidate_start = FALSE;
00705          iiim_destroy_candidate_window (context_iiim);
00706          DEBUG_DO (g_message ("lookup_choice_done"));
00707          break;
00708        case IIIMCF_EVENT_TYPE_UI_STATUS_START:
00709          DEBUG_DO (g_message ("ui_status_start"));
00710          status_callback (context_iiim);
00711          break;
00712        case IIIMCF_EVENT_TYPE_UI_STATUS_CHANGE:
00713          DEBUG_DO (g_message ("ui_status_change"));
00714          status_callback (context_iiim);
00715          break;
00716        case IIIMCF_EVENT_TYPE_UI_STATUS_END:
00717          DEBUG_DO (g_message ("ui_status_end"));
00718          status_callback (context_iiim);
00719          break;
00720        case IIIMCF_EVENT_TYPE_AUX_START:
00721          DEBUG_DO (g_message ("aux_start"));
00722          iiim_aux_start (context_iiim, ev);
00723          break;
00724        case IIIMCF_EVENT_TYPE_AUX_DRAW:
00725          DEBUG_DO (g_message ("aux_draw"));
00726          iiim_aux_draw (context_iiim, ev);
00727          break;
00728        case IIIMCF_EVENT_TYPE_AUX_DONE:
00729          DEBUG_DO (g_message ("aux_done"));
00730          iiim_aux_done (context_iiim, ev);
00731          break;
00732        case IIIMCF_EVENT_TYPE_AUX_GETVALUES:
00733          DEBUG_DO (g_message ("aux_getvalues_reply"));
00734          iiim_aux_getvalues_reply (context_iiim, ev);
00735          break;
00736        case IIIMCF_EVENT_TYPE_UI_COMMIT:
00737          {
00738            IIIMCF_text text;
00739            gchar *utf8 = NULL;
00740            st = iiimcf_get_committed_text (c, &text);
00741            utf8 = utf16_to_utf8 (text);
00742            g_signal_emit_by_name (context_iiim, "commit", utf8);
00743            g_free (utf8);
00744          }
00745          break;
00746        default:
00747          break;
00748        }
00749       iiimcf_dispatch_event (c, ev);
00750       iiimcf_ignore_event (ev);
00751     }
00752 
00753   return result;
00754 }
00755 
00756 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >=2)
00757 static void
00758 iiim_info_display_closed (GdkDisplay * display,
00759                        gboolean is_error, GtkIIIMInfo * info)
00760 {
00761   GSList *ics, *tmp_list;
00762 
00763   open_iiims = g_slist_remove (open_iiims, info);
00764 
00765   ics = info->ics;
00766   info->ics = NULL;
00767 
00768   for (tmp_list = ics; tmp_list; tmp_list = g_slist_next (tmp_list))
00769     {
00770       set_sc_client_window (tmp_list->data, NULL, TRUE);
00771     }
00772 
00773   g_slist_free (tmp_list);
00774 
00775   g_free (info->locale);
00776 
00777 #ifdef DIRTY_HACK_FOR_TEST_PURPOSE
00778   if (info->le_name)
00779     g_free (info->le_name);
00780 #endif
00781 
00782   g_free (info->le_list);
00783   /* TODO */
00784   g_free (info);
00785 }
00786 #endif
00787 
00788 static void
00789 status_style_change (GtkIIIMInfo *info)
00790 {
00791   GtkIMStatusStyle status_style;
00792   
00793   g_object_get (info->settings,
00794               "gtk-im-status-style", &status_style,
00795               NULL);
00796 
00797   info->status_style_setting = status_style;
00798 }
00799 
00800 static void
00801 get_im_settings_property ()
00802 {
00803   GdkWindow *root_window = gdk_get_default_root_window ();
00804   GdkAtom  type;
00805   guchar   *data = NULL;
00806   gint     format;
00807   gint     length = 0;
00808   if (im_settings_atom == GDK_NONE)
00809     im_settings_atom = gdk_atom_intern (_IM_SETTINGS, FALSE);
00810   if (im_settings_atom == GDK_NONE)
00811     return;
00812 
00813   gdk_property_get (root_window,
00814                   im_settings_atom, im_settings_atom,
00815                   0, INT_MAX, FALSE,
00816                   &type, &format, &length, &data);
00817   if (data)
00818     {
00819       g_memmove (&current_setting, data, sizeof (IMSettings));
00820       g_free (data);
00821       current_setting_enabled = TRUE;
00822     }
00823   else
00824     current_setting_enabled = FALSE;
00825 }
00826 
00827 static GtkIIIMInfo *
00828 get_iiim (GdkWindow * client_window)
00829 {
00830   GtkIIIMInfo *info = NULL;
00831   IIIMCF_attr attr;
00832   IIIMF_status st;
00833 #if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 2
00834   GSList *tmp_list;
00835   GdkScreen *screen = gdk_drawable_get_screen (client_window);
00836   GdkDisplay *display = gdk_screen_get_display (screen);
00837 #endif
00838 
00839 #if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 2
00840   tmp_list = open_iiims;
00841   while (tmp_list)
00842     {
00843       info = tmp_list->data;
00844       if (info->screen == screen)
00845        return info;
00846       tmp_list = tmp_list->next;
00847     }
00848 #else
00849   if (open_iiims)
00850     return open_iiims->data;
00851 #endif
00852   info = NULL;
00853 
00854   if (!iiim_is_initialized)
00855     {
00856       Atom    iiimd;
00857       Window  iiimx;
00858       Display *      display_x11;
00859 
00860       st = iiimcf_initialize (IIIMCF_ATTR_NULL);
00861       if (st != IIIMF_STATUS_SUCCESS)
00862        goto Error;
00863       st = iiimcf_create_attr (&attr);
00864       if (st != IIIMF_STATUS_SUCCESS)
00865        goto Error;
00866       st = iiimcf_attr_put_string_value (attr,
00867                                     IIIMCF_ATTR_CLIENT_TYPE,
00868                                     "Gtk IIIMCF Module");
00869       if (st != IIIMF_STATUS_SUCCESS)
00870        goto Error;
00871       
00872       st = iiimcf_attr_put_string_value (attr,
00873                                     IIIMCF_ATTR_CLIENT_X_DISPLAY_NAME,
00874                                     gdk_screen_make_display_name(screen));
00875       if (st != IIIMF_STATUS_SUCCESS)
00876        goto Error;
00877 
00878       display_x11 = GDK_DISPLAY_XDISPLAY (display);
00879       iiimd = XInternAtom (display_x11, "IIIM_SERVER", True);
00880       if (None != iiimd) {
00881        iiimx = XGetSelectionOwner (display_x11, iiimd);
00882        if (None != iiimx) {
00883          Atom          type;
00884          int           format;
00885          unsigned long        length;
00886          unsigned long        nitem;
00887          unsigned char * data;
00888          data = NULL;
00889          XGetWindowProperty(display_x11, iiimx, iiimd,
00890                           0, INT_MAX, False, XA_STRING,
00891                           &type, &format, &nitem, &length, &data);
00892          if (NULL == data) {
00893          } else if (0 == strncmp("uds:", (char *)data, 4)) {
00894            st = iiimcf_attr_put_string_value (attr,
00895                                           IIIMCF_ATTR_SERVER_ADDRESS,
00896                                           data + 4);
00897            XFree(data);
00898            if (st != IIIMF_STATUS_SUCCESS)
00899              goto Error;
00900            st = iiimcf_attr_put_string_value (attr,
00901                                           IIIMCF_ATTR_SERVER_SERVICE,
00902                                           "");
00903            if (st != IIIMF_STATUS_SUCCESS)
00904              goto Error;
00905          } else {
00906            XFree (data);
00907          }
00908        }
00909       }
00910 
00911       st = iiimcf_create_handle (attr, &iiim);
00912       if (st != IIIMF_STATUS_SUCCESS)
00913        goto Error;
00914       st = iiimcf_destroy_attr (attr);
00915       if (iiim)
00916        {
00917          st = iiim_setup_aux_object (iiim);
00918          if (st == IIIMF_STATUS_SUCCESS)
00919            iiim_is_initialized = TRUE;
00920        }
00921     }
00922 
00923 Error:
00924 
00925   if (!iiim)
00926     return NULL;
00927 
00928   info = g_new0 (GtkIIIMInfo, 1);
00929   open_iiims = g_slist_prepend (open_iiims, info);
00930 #if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 2
00931   info->screen = screen;
00932 #endif
00933 
00934   info->iiim = iiim;
00935   info->ics = NULL;
00936 
00937 #ifdef DIRTY_HACK_FOR_TEST_PURPOSE
00938   /* Dirty Hack for Test Purpose */
00939   {
00940     gchar *locale = NULL;
00941     gchar *le_name = NULL;
00942 
00943     locale = getenv ("IIIM_LOCALE");
00944     le_name = getenv ("IIIM_LE");
00945 
00946     if (locale && *locale)
00947       info->locale = g_strdup (locale);
00948     else
00949       info->locale = g_strdup (setlocale (LC_CTYPE, NULL));
00950 
00951     if (le_name && *le_name)
00952       info->le_name = g_strdup (le_name);
00953   }
00954 #else
00955   info->locale = g_strdup (setlocale (LC_CTYPE, NULL));
00956 #endif
00957 
00958   info->settings = gtk_settings_get_for_screen (info->screen);
00959 
00960   if (!g_object_class_find_property (G_OBJECT_GET_CLASS (info->settings),
00961                                  "gtk-im-status-style"))
00962     gtk_settings_install_property (g_param_spec_enum ("gtk-im-status-style",
00963                                                 _("IM Status Style"),
00964                                                 _("Where to show the input method statusbar"),
00965                                                 GTK_TYPE_IM_STATUS_STYLE,
00966                                                 GTK_IM_STATUS_CALLBACK,
00967                                                 G_PARAM_READWRITE));
00968 
00969   info->status_set = g_signal_connect_swapped (info->settings,
00970                                           "notify::gtk-im-status-style",
00971                                           G_CALLBACK (status_style_change),
00972                                           info);
00973   status_style_change (info);
00974 
00975   iiim_get_im_list (info);
00976 
00977   if (!info->iiim)
00978     g_warning ("Unable to Connect IIIM input method");
00979 
00980 #if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 2
00981   g_signal_connect (display, "closed",
00982                   G_CALLBACK (iiim_info_display_closed), info);
00983 #endif
00984   return info;
00985 }
00986 
00987 /* 
00988  * Candidate Window Handling
00989  */
00990 
00991 static void
00992 iiim_candidate_move (GtkIMContextIIIM *context_iiim, GtkWidget *candwin)
00993 {
00994   gint x, y;
00995   GdkRectangle rect;
00996   GtkRequisition requisition;
00997   gint width, height;
00998 
00999   height = gdk_screen_get_height (gtk_widget_get_screen (candwin));
01000   width = gdk_screen_get_width (gtk_widget_get_screen (candwin));
01001   gdk_window_get_frame_extents (candwin->window, &rect);
01002   gtk_widget_size_request (candwin, &requisition);
01003 
01004   gdk_window_get_origin (context_iiim->client_window, &x, &y);
01005 
01006   x = x + context_iiim->cursor.x;
01007   y = y + context_iiim->cursor.y + context_iiim->cursor.height;
01008 
01009   if (y + requisition.height > height)
01010     y = height - requisition.height;
01011   else if (y < 0)           /* may not happen */
01012     y = 0;
01013 
01014   if (x + requisition.width > width)
01015     x = width - requisition.width;
01016   else if (x < 0)
01017     x = 0;
01018 
01019   if (requisition.width < rect.width || requisition.height < rect.height)
01020     gtk_window_resize (GTK_WINDOW (candwin), requisition.width, requisition.height);
01021   gtk_window_move (GTK_WINDOW (candwin), x, y);
01022 }
01023 
01024 static void
01025 iiim_candidate_show (GtkIMContextIIIM * context_iiim)
01026 {
01027   GtkWidget *w = iiim_get_candidate_window (context_iiim);
01028   IIIMF_status st;
01029   IIIMCF_lookup_choice luc;
01030   CandidateWindow *cw;
01031   GtkTreeIter iter;
01032   GtkTreeIter riter;
01033   int size;
01034   int first_candidate;
01035   int last_candidate;
01036   int current_candidate;
01037   int i, j;
01038   IIIMCF_text title, candidate, label;
01039   gchar *title_u8 = NULL;
01040   int flag;
01041 
01042   DEBUG_DO (g_message ("candidate show"));
01043   if (!w)
01044     {
01045       DEBUG_DO (g_message ("w not found"));
01046       return;
01047     }
01048   if (!context_iiim->candidate_start)
01049     {
01050       DEBUG_DO (g_message ("candidate not start"));
01051       return;
01052     }
01053 
01054   if (current_setting_enabled &&
01055       current_setting.lookup_enabled == IM_OFF)
01056     return;
01057 
01058   luc = iiim_get_lookup_choice (context_iiim);
01059   if (!luc)
01060     {
01061       DEBUG_DO (g_message ("fail to obtain IIIMCF_lookup_choice"));
01062       return;
01063     }
01064 
01065   title = NULL;
01066 
01067   st = iiimcf_get_lookup_choice_title (luc, &title);
01068   if (st != IIIMF_STATUS_SUCCESS)
01069     {
01070       DEBUG_DO (g_message ("Failed to get lookup choice title"));
01071       return;
01072     }
01073 
01074   if (title)
01075     title_u8 = utf16_to_utf8 (title);
01076 
01077   cw = g_object_get_data (G_OBJECT (w), "iiim-candidate-win");
01078   if (!cw)
01079     {
01080       DEBUG_DO (g_message ("candidate window not found"));
01081       return;
01082     }
01083 
01084   gtk_frame_set_label(GTK_FRAME (cw->frame), title_u8);
01085 
01086   if (title_u8)
01087     g_free (title_u8);
01088 
01089   /* get candidates's amount from IIIMSF */
01090   size = 0;
01091   first_candidate = 0;
01092   last_candidate = 0;
01093   current_candidate = 0;
01094   st = iiimcf_get_lookup_choice_size (luc, &size, &first_candidate,
01095                                   &last_candidate, &current_candidate);
01096   if (st != IIIMF_STATUS_SUCCESS)
01097     return;
01098   DEBUG_DO (g_message ("size %d first %d last %d current %d",
01099                      size, first_candidate, last_candidate,
01100                      current_candidate));
01101   /* some LE sends negative value :-< */
01102   if (current_candidate < 0) current_candidate = 0;
01103 
01104   /* clear */
01105   gtk_list_store_clear (cw->store);
01106   /* set iter */
01107   gtk_list_store_append (cw->store, &iter);
01108   /* adding candidate to treeview */
01109   for (i = first_candidate, j = 0;
01110        i < (last_candidate + 1); i++, j++)
01111     {
01112       gchar *candidate_u8, *label_u8 = NULL, *result = NULL;
01113       /* get candidates from IIIMSF */
01114       candidate = NULL;
01115       label = NULL;
01116       flag = 0;
01117       st = iiimcf_get_lookup_choice_item (luc, i, &candidate, &label, &flag);
01118       if (st != IIIMF_STATUS_SUCCESS)
01119        break;
01120       if (label)
01121        label_u8 = utf16_to_utf8 (label);
01122       candidate_u8 = utf16_to_utf8 (candidate);
01123       DEBUG_DO (g_message ("candidate %s", candidate_u8));
01124       if (label_u8)
01125        {
01126          result = g_strconcat (label_u8, " ", candidate_u8, NULL);
01127        }
01128       /* max columns */
01129       if (j == cw->number_of_columns)
01130        {
01131          /* set next row */
01132          gtk_list_store_insert_after (cw->store, &riter, &iter);
01133          iter = riter;
01134          j = 0;
01135        }
01136       gtk_list_store_set (cw->store, &iter, j,
01137                        result ? result : candidate_u8, -1);
01138       /* current candidate */
01139       if (i == current_candidate)
01140        {
01141          GtkTreeSelection *selection =
01142            gtk_tree_view_get_selection (GTK_TREE_VIEW (cw->tree));
01143          gtk_tree_selection_select_iter (selection, &iter);
01144        }
01145       if (result)
01146        {
01147          g_free (result);
01148          g_free (label_u8);
01149        }
01150       g_free (candidate_u8);
01151     }
01152   gtk_widget_realize (w);
01153   iiim_candidate_move (context_iiim, w);
01154   gtk_widget_show_all (w);
01155 }
01156 
01157 static GtkListStore *
01158 iiim_create_candidate_model (int number_of_columns)
01159 {
01160   GtkListStore *ret;
01161   GType *types;
01162   int i;
01163 
01164   DEBUG_DO (g_message ("create_candidate_model"));
01165   types = g_new0 (GType, number_of_columns);
01166   for (i = 0; i < number_of_columns; i++)
01167     {
01168       types[i] = G_TYPE_STRING;
01169     }
01170   ret = gtk_list_store_newv (number_of_columns, types);
01171   g_free (types);
01172   return ret;
01173 }
01174 
01175 static void
01176 iiim_candidate_window_button_press (GtkWidget *widget, GdkEventButton *event,
01177                                 GtkIMContextIIIM *context_iiim)
01178 {
01179   GtkTreeView *tree_view;
01180   GtkTreePath *path;
01181   GtkTreeIter iter;
01182   GtkTreeModel *model;
01183   GtkTreeSelection *selection;
01184 
01185   tree_view = GTK_TREE_VIEW (widget);
01186 
01187   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
01188   gtk_tree_view_get_path_at_pos (tree_view, event->x, event->y,
01189                              &path, NULL, NULL, NULL);
01190   if (!path)  /* click event occured on no candidate */
01191     return;
01192   gtk_tree_selection_select_path (selection, path);
01193 
01194   if (gtk_tree_selection_get_selected (selection, &model, &iter))
01195     {
01196       GdkEventKey *e;
01197       IIIMF_status st;
01198       IIIMCF_keyevent kev;
01199       IIIMCF_event ev;
01200       gchar *buffer = NULL;
01201 
01202       gtk_tree_model_get (model, &iter, 0, &buffer, -1);
01203 
01204       /* assemble the GdkEventKey */
01205       e = (GdkEventKey *) gdk_event_new (GDK_KEY_PRESS);
01206   
01207       e->time = gdk_event_get_time ((GdkEvent *)event);
01208       gdk_event_get_state ((GdkEvent *)event, &e->state);
01209 
01210       /* buffer[0] always holds a candidate index character. */
01211       e->keyval = buffer[0];
01212 
01213       g_free (buffer);
01214     
01215       st = convert_GdkEventKey_to_IIIMCF_keyevent (e, &kev);
01216       gdk_event_free ((GdkEvent *)e);
01217   
01218       if (st != IIIMF_STATUS_SUCCESS)
01219        return;
01220 
01221       st = iiimcf_create_keyevent (&kev, &ev);
01222       if (st != IIIMF_STATUS_SUCCESS)
01223        return;
01224 
01225       /* Send Message to IIIMSF */
01226       if (forward_event (context_iiim, ev, &st))
01227        iiim_event_dispatch (context_iiim);
01228     }
01229   return;
01230 }
01231 
01232 static GtkWidget *
01233 iiim_create_candidate_window (GtkIMContextIIIM * context_iiim)
01234 {
01235   CandidateWindow *w;
01236   GtkListStore *store;
01237   GtkCellRenderer *renderer;
01238   GtkTreeViewColumn *column;
01239   IIIMCF_lookup_choice luc = iiim_get_lookup_choice (context_iiim);
01240   IIIMF_status st;
01241   IIIMCF_text title;
01242   GdkWindow *toplevel_gdk;
01243   GtkWidget *toplevel;
01244   gpointer ptoplevel;
01245   gchar *title_u8 = NULL;
01246   int choices_per_window;
01247   int number_of_rows;
01248   int number_of_columns;
01249   int direction;
01250   int i;
01251 
01252 #if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 2
01253   GdkScreen *screen;
01254   GdkWindow *root_window;
01255 #endif
01256 
01257   if (!context_iiim->client_window)
01258     return NULL;
01259 
01260   toplevel_gdk = context_iiim->client_window;
01261 
01262 #if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 2
01263   screen = gdk_drawable_get_screen (toplevel_gdk);
01264   root_window = gdk_screen_get_root_window (screen);
01265 #endif
01266 
01267   while (TRUE)
01268     {
01269       GdkWindow *parent = gdk_window_get_parent (toplevel_gdk);
01270 #if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 2
01271       if (parent == root_window)
01272 #else
01273       if (parent == gdk_get_default_root_window ())
01274 #endif
01275        break;
01276       else
01277        toplevel_gdk = parent;
01278     }
01279 
01280   gdk_window_get_user_data (toplevel_gdk, &ptoplevel);
01281   toplevel = GTK_WIDGET(ptoplevel);
01282   if (!toplevel)
01283     return NULL;
01284 
01285   DEBUG_DO (g_message ("create candidate window"));
01286 
01287   if (!luc)
01288     {
01289       DEBUG_DO (g_message ("lookup choice not found"));
01290       return NULL;
01291     }
01292 
01293   choices_per_window = 0;
01294   number_of_rows = 0;
01295   number_of_columns = 0;
01296   direction = IIIMCF_LOOKUP_CHOICE_HORIZONTAL_DIRECTION; /* default */
01297   st = iiimcf_get_lookup_choice_configuration (luc,
01298                                           &choices_per_window,
01299                                           &number_of_rows,
01300                                           &number_of_columns,
01301                                           &direction);
01302   if (st != IIIMF_STATUS_SUCCESS)
01303     {
01304       DEBUG_DO (g_message ("config failed"));
01305       return NULL;
01306     }
01307 
01308   title = NULL;
01309   st = iiimcf_get_lookup_choice_title (luc, &title);
01310   if (st != IIIMF_STATUS_SUCCESS)
01311     {
01312       DEBUG_DO (g_message ("Failed to get lookup choice title"));
01313       return NULL;
01314     }
01315 
01316   if (title)
01317     title_u8 = utf16_to_utf8 (title);
01318 
01319   if ((number_of_columns < 0) || (number_of_rows < 0))
01320     {
01321       DEBUG_DO (g_message
01322               ("column %d %d", number_of_columns, number_of_rows));
01323       return NULL;
01324     }
01325   store = iiim_create_candidate_model (number_of_columns);
01326   if (!store)
01327     {
01328       DEBUG_DO (g_message ("create model failed"));
01329       return NULL;
01330     }
01331   w = g_new0 (CandidateWindow, 1);
01332   w->toplevel = gtk_window_new (GTK_WINDOW_POPUP);
01333   gtk_container_set_border_width (GTK_CONTAINER (w->toplevel), 2);
01334 
01335   w->frame = gtk_frame_new (title_u8);
01336   gtk_frame_set_shadow_type (GTK_FRAME (w->frame), GTK_SHADOW_ETCHED_OUT);
01337 
01338   w->tree = gtk_tree_view_new ();
01339   gtk_tree_view_set_model (GTK_TREE_VIEW (w->tree), GTK_TREE_MODEL (store));
01340   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (w->tree), FALSE);
01341   gtk_tree_view_columns_autosize (GTK_TREE_VIEW (w->tree));
01342 
01343   for (i = 0; i < number_of_columns; i++)
01344     {
01345       renderer = gtk_cell_renderer_text_new ();
01346       column = gtk_tree_view_column_new_with_attributes ("",
01347                                                   renderer, "text", i,
01348                                                   NULL);
01349       gtk_tree_view_column_set_resizable (column, TRUE);
01350       gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
01351       gtk_tree_view_append_column (GTK_TREE_VIEW (w->tree), column);
01352     }
01353   gtk_tree_selection_set_mode (gtk_tree_view_get_selection
01354                             (GTK_TREE_VIEW (w->tree)),
01355                             GTK_SELECTION_SINGLE);
01356   w->store = store;
01357   w->app_toplevel = toplevel;
01358   w->choices_per_window = choices_per_window;
01359   w->number_of_rows = number_of_rows;
01360   w->number_of_columns = number_of_columns;
01361   w->direction = direction;
01362   
01363   w->button_press_handler_id = g_signal_connect (G_OBJECT (w->tree),
01364                                            "button-press-event",
01365                                            G_CALLBACK (iiim_candidate_window_button_press),
01366                                            context_iiim);
01367 
01368   gtk_container_add (GTK_CONTAINER (w->toplevel), w->frame);
01369   gtk_container_add (GTK_CONTAINER (w->frame), w->tree);
01370 
01371   g_object_set_data (G_OBJECT (w->toplevel), "iiim-candidate-win",
01372                    (gpointer) w);
01373   DEBUG_DO (g_message ("create_candidate_window"));
01374 
01375   if (title_u8)
01376     g_free (title_u8);
01377 
01378 
01379   w->configure_handler_id = g_signal_connect (toplevel, "configure_event",
01380                                          G_CALLBACK
01381                                          (iiim_candidate_window_configure),
01382                                          context_iiim);
01383   w->destroy_handler_id =
01384     g_signal_connect_swapped (toplevel, "destroy",
01385                            G_CALLBACK (iiim_destroy_candidate_window),
01386                            context_iiim);
01387   return w->toplevel;
01388 }
01389 
01390 static void
01391 iiim_destroy_candidate_window (GtkIMContextIIIM * context_iiim)
01392 {
01393   GtkWidget *w = context_iiim->candidate;
01394   CandidateWindow *cw;
01395   if (!w)
01396     return;
01397   cw = gtk_object_get_data (GTK_OBJECT (w), "iiim-candidate-win");
01398   g_signal_handler_disconnect (cw->app_toplevel, cw->destroy_handler_id);
01399   g_signal_handler_disconnect (cw->app_toplevel, cw->configure_handler_id);
01400   g_signal_handler_disconnect (cw->tree, cw->button_press_handler_id);
01401 
01402   gtk_widget_destroy (cw->toplevel);
01403   g_free (cw);
01404   context_iiim->candidate = NULL;
01405   return;
01406 }
01407 
01408 static gboolean
01409 iiim_candidate_window_configure (GtkWidget * toplevel,
01410                              GdkEventConfigure * event,
01411                              GtkIMContextIIIM * context_iiim)
01412 {
01413   GtkWidget *candwin = iiim_get_candidate_window (context_iiim);
01414 
01415   if (!candwin)
01416     return FALSE;
01417 
01418   iiim_candidate_move (context_iiim, candwin);
01419   return FALSE;
01420 }
01421 
01422 static GtkWidget *
01423 iiim_get_candidate_window (GtkIMContextIIIM * context_iiim)
01424 {
01425   GtkWidget *candidate = NULL;
01426 
01427   if (!context_iiim->candidate_start)
01428     {
01429       DEBUG_DO (g_message ("candidate not started yet"));
01430       return NULL;
01431     }
01432   if (context_iiim->candidate)
01433     {
01434       return context_iiim->candidate;
01435     }
01436   else
01437     {
01438       candidate = iiim_create_candidate_window (context_iiim);
01439       DEBUG_DO (g_message ("candidate %p", candidate));
01440       context_iiim->candidate = candidate;
01441     }
01442   return candidate;
01443 }
01444 
01445 static IIIMCF_lookup_choice
01446 iiim_get_lookup_choice (GtkIMContextIIIM * context_iiim)
01447 {
01448   IIIMCF_context context = iiim_get_session_context (context_iiim);
01449   IIIMCF_lookup_choice luc;
01450   IIIMF_status st;
01451 
01452   if (context && context_iiim->candidate_start)
01453     {
01454       if (context_iiim->lookup_choice)
01455        return context_iiim->lookup_choice;
01456       luc = NULL;
01457       st = iiimcf_get_lookup_choice (context, &luc);
01458       if (st != IIIMF_STATUS_SUCCESS)
01459        return NULL;
01460       context_iiim->lookup_choice = luc;
01461       return context_iiim->lookup_choice;
01462     }
01463 
01464   return NULL;
01465 }
01466 
01467 static IIIMCF_language
01468 get_input_language (GtkIMContextIIIM *context_iiim, gchar *input_language,
01469                   gboolean exact_match)
01470 {
01471   int i;
01472   GtkIIIMInfo *info = context_iiim->iiim_info;
01473   IIIMF_status st;
01474   char *langid, *lang, *p;
01475   int n_lang;
01476   IIIMCF_language *lang_list;
01477 
01478   if (input_language == NULL)
01479     return NULL;
01480   else
01481     lang = g_strdup(input_language);
01482 
01483   lang_list = iiim_get_languages (info, &n_lang);
01484   if (lang_list)
01485     {
01486       for (i = 0; i < n_lang; i++)
01487        {
01488          st = iiimcf_get_language_id(lang_list[i], (const char **)&langid);
01489          if (st != IIIMF_STATUS_SUCCESS)
01490            continue;
01491          if (!strcmp(lang, langid))
01492            goto found_language;
01493        }
01494       /* try to find the supported language again without charset locale */
01495       p = strchr(lang, '@');
01496       if (p)
01497        *p = 0;
01498       p = strchr(lang, '.');
01499       if (p)
01500        *p = 0;
01501 
01502       for (i = 0; i < n_lang; i++)
01503        {
01504          st = iiimcf_get_language_id(lang_list[i], (const char **)&langid);
01505          if (st != IIIMF_STATUS_SUCCESS)
01506            continue;
01507          if (!strcmp(lang, langid))
01508            goto found_language;
01509        }
01510       /* finally try to take care of the locale, such as en */
01511       p = strchr(lang, '_');
01512       if (p)
01513        *p = 0;
01514 
01515       for (i = 0; i < n_lang; i++)
01516        {
01517          st = iiimcf_get_language_id(lang_list[i], (const char **)&langid);
01518          if (st != IIIMF_STATUS_SUCCESS)
01519            continue;
01520          if (!strcmp(lang, langid))
01521            goto found_language;
01522        }
01523       g_free(lang);
01524 
01525       /*
01526        still cannot find a match. Just take one for "en" as
01527        a default.
01528       */
01529       for (i = 0; i < n_lang; i++)
01530        {
01531          st = iiimcf_get_language_id (lang_list[i],
01532                                    (const char **)&langid);
01533          if (st != IIIMF_STATUS_SUCCESS)
01534            continue;
01535          if (!strncmp (langid, "en", 2))
01536            goto found_language;
01537        }
01538     }
01539 
01540   return NULL;
01541 
01542  found_language:;
01543   g_free(context_iiim->current_language);
01544   context_iiim->current_language = g_strdup(langid);
01545 
01546   return lang_list[i];
01547 }
01548 
01549 #ifdef DEBUG
01550 static void
01551 next_input_language (GtkIMContextIIIM *context_iiim)
01552 {
01553   int i;
01554   GtkIIIMInfo *info = context_iiim->iiim_info;
01555   IIIMF_status st;
01556   char *langid;
01557   int n_lang;
01558   IIIMCF_language *lang_list;
01559 
01560   lang_list = iiim_get_languages (info, &n_lang);
01561   if (lang_list)
01562     {
01563       for (i = 0; i < n_lang; i++)
01564        {
01565          st = iiimcf_get_language_id (lang_list[i],
01566                                    (const char **)&langid);
01567          if (st != IIIMF_STATUS_SUCCESS)
01568            continue;
01569          if (!strncmp (langid, context_iiim->current_language,
01570                      strlen (langid)))
01571            break;
01572        }
01573       if (i == n_lang - 1)
01574        i = 0;
01575       else
01576        i++;
01577 
01578       st = iiimcf_get_language_id (lang_list[i],
01579                                (const char **)&langid);
01580       if (st != IIIMF_STATUS_SUCCESS)
01581        return;
01582       g_free (context_iiim->current_language);
01583       context_iiim->current_language = g_strdup (langid);
01584     }
01585   return;
01586 }
01587 static void
01588 prev_input_language (GtkIMContextIIIM *context_iiim)
01589 {
01590   int i;
01591   GtkIIIMInfo *info = context_iiim->iiim_info;
01592   IIIMF_status st;
01593   char *langid;
01594   int n_lang;
01595   IIIMCF_language *lang_list;
01596 
01597   lang_list = iiim_get_languages (info, &n_lang);
01598   if (lang_list)
01599     {
01600       for (i = n_lang - 1; i > 0; i--)
01601        {
01602          st = iiimcf_get_language_id (lang_list[i],
01603                                    (const char **)&langid);
01604          if (st != IIIMF_STATUS_SUCCESS)
01605            continue;
01606          if (!strncmp (langid, context_iiim->current_language,
01607                      strlen (langid)))
01608            break;
01609        }
01610       if (i == 0)
01611        i = n_lang - 1;
01612       else
01613        i--;
01614 
01615       st = iiimcf_get_language_id (lang_list[i],
01616                                (const char **)&langid);
01617       if (st != IIIMF_STATUS_SUCCESS)
01618        return;
01619       g_free (context_iiim->current_language);
01620       context_iiim->current_language = g_strdup (langid);
01621     }
01622   return;
01623 }
01624 #endif
01625 
01626 static IIIMCF_context
01627 iiim_get_session_context (GtkIMContextIIIM * context_iiim)
01628 {
01629   IIIMF_status st;
01630   IIIMCF_attr attr;
01631 
01632   if (!context_iiim->iiim_info)
01633     {
01634       if (context_iiim->client_window)
01635        context_iiim->iiim_info = get_iiim (context_iiim->client_window);
01636       if (context_iiim->iiim_info)
01637        context_iiim->iiim_info->ics =
01638          g_slist_prepend (context_iiim->iiim_info->ics, context_iiim);
01639     }
01640   if (!context_iiim->iiim_info)
01641     {
01642       DEBUG_DO (g_message ("iiim_info->iiim not found"));
01643       return NULL;
01644     }
01645   if (!context_iiim->context && context_iiim->iiim_info)
01646     {
01647       IIIMCF_language iiim_lang = NULL;
01648 
01649       iiim_lang = get_input_language (context_iiim,
01650                                   context_iiim->iiim_info->locale,
01651                                   FALSE);
01652       st = iiimcf_create_attr (&attr);
01653       if (st != IIIMF_STATUS_SUCCESS)
01654        return NULL;
01655 
01656       if (iiim_lang)
01657        iiimcf_attr_put_ptr_value (attr, IIIMCF_ATTR_INPUT_LANGUAGE,
01658                                iiim_lang);
01659       st = iiimcf_create_context (context_iiim->iiim_info->iiim,
01660                               attr, &(context_iiim->context));
01661       iiimcf_destroy_attr (attr);
01662       if (st != IIIMF_STATUS_SUCCESS)
01663        return NULL;
01664     }
01665 
01666   DEBUG_DO (g_message ("%p", context_iiim->context));
01667   return context_iiim->context;
01668 }
01669 
01670 static void
01671 im_context_iiim_commit_cb(GtkIMContext *context,
01672                        const gchar  *string,
01673                        GtkIMContextIIIM *context_iiim)
01674 {
01675   g_return_if_fail(string != NULL);
01676 
01677   g_signal_emit_by_name(context_iiim, "commit", string);
01678 }
01679 
01680 static void
01681 im_context_iiim_init (GtkIMContextIIIM * im_context_iiim)
01682 {
01683   im_context_iiim->context = NULL;
01684   im_context_iiim->candidate = NULL;
01685   im_context_iiim->keylist = NULL;
01686   im_context_iiim->candidate_start = FALSE;
01687   im_context_iiim->use_preedit = FALSE;
01688   im_context_iiim->finalizing = FALSE;
01689   im_context_iiim->has_focus = FALSE;
01690   im_context_iiim->in_toplevel = FALSE;
01691 
01692   /* for the dead key */
01693   im_context_iiim->slave = g_object_new(GTK_TYPE_IM_CONTEXT_SIMPLE, NULL);
01694   g_signal_connect(G_OBJECT(im_context_iiim->slave), "commit",
01695                  G_CALLBACK(im_context_iiim_commit_cb), im_context_iiim);
01696 
01697   memset(&im_context_iiim->saved_key, 0, sizeof(im_context_iiim->saved_key));
01698 
01699   bindtextdomain (GETTEXT_PACKAGE, IIIMGCFLOCALEDIR);
01700   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
01701 }
01702 
01703 static gchar *
01704 utf16_to_utf8 (IIIMCF_text text)
01705 {
01706   IIIMF_status st;
01707   IIIMP_card16 *u16str = NULL;
01708   st = iiimcf_get_text_utf16string (text, (const IIIMP_card16 **) &u16str);
01709   return (u16str != NULL ?
01710          g_utf16_to_utf8 ((const gunichar2 *) u16str, -1, NULL, NULL, NULL) :
01711          NULL);
01712 }
01713 
01714 static void
01715 iiim_keylist_free (GtkIMContextIIIM * context_iiim)
01716 {
01717   while (context_iiim->keylist != NULL)
01718     {
01719       GdkEventKey *ev = (GdkEventKey *)context_iiim->keylist->data;
01720 
01721       context_iiim->keylist = g_slist_remove (context_iiim->keylist, ev);
01722       gdk_event_free ((GdkEvent *) ev);
01723     }
01724 }
01725 
01726 static gint
01727 check_stored_keyevent (GtkIMContext * context, GdkEventKey * event)
01728 {
01729   int same;
01730   GtkIMContextIIIM *context_iiim = GTK_IM_CONTEXT_IIIM (context);
01731   GSList *tmp;
01732   GdkEventKey *saved_event = &(context_iiim->saved_key);
01733 
01734   /*
01735      More detail see bug 6226542: gedit hangs with Click + ESC/CapsLock/NumLock/ScrollLock/F2/F8/F12.
01736      gdk_grad_add() will cause two same key_event be sent for grabbed window and focus window.
01737      so need first check this event.
01738   */
01739   same = (event->time == saved_event->time && event->keyval == saved_event->keyval);
01740   context_iiim->saved_key = *event;
01741 
01742   for (tmp = context_iiim->keylist; tmp; tmp = g_slist_next (tmp))
01743     {
01744       GdkEventKey *ev = (GdkEventKey *) tmp->data;
01745       if (ev->time == event->time && ev->keyval == event->keyval)
01746        {
01747          /* found */
01748          guint32 unicode;
01749          context_iiim->keylist = g_slist_remove (context_iiim->keylist, ev);
01750          unicode = gdk_keyval_to_unicode (event->keyval);
01751          DEBUG_DO (g_message
01752                   ("keyevent found %s %d %d", gdk_keyval_name (ev->keyval),
01753                    g_unichar_isprint (unicode), unicode));
01754          if (g_unichar_isprint (unicode)
01755              && (event->state == 0 || event->state == GDK_SHIFT_MASK))
01756            {
01757              gchar utf8[7];
01758              gint len;
01759              len = g_unichar_to_utf8 (unicode, utf8);
01760              utf8[len] = 0;
01761              g_signal_emit_by_name (context, "commit", utf8);
01762              gdk_event_free ((GdkEvent *) ev);
01763              return 1;
01764            }
01765          gdk_event_free ((GdkEvent *) ev);
01766          return 0;
01767        }
01768     }
01769 
01770   if (same)
01771      return 0;
01772 
01773   return -1;
01774 }
01775 
01776 static void
01777 set_error_message (GtkIMContextIIIM * context_iiim)
01778 {
01779 #ifdef DEBUG
01780   /* set error messages to status window */
01781   char *error_message = _("Can't communicate with IIIM server");
01782 
01783   if (!context_iiim->status_window)
01784     {
01785       if (!context_iiim->has_focus)
01786        {
01787          context_iiim->has_focus = TRUE;
01788          /* tell switcher that I'm the current client */
01789          im_context_switcher_set_focus (context_iiim);
01790        }
01791       update_status_window (context_iiim);
01792     }
01793   status_window_set_text (context_iiim->status_window, error_message);
01794   im_context_switcher_set_status_text (context_iiim, error_message);
01795 #endif
01796 }
01797 
01798 static gboolean
01799 forward_event (GtkIMContextIIIM * context_iiim, IIIMCF_event ev,
01800               IIIMF_status *st_ret)
01801 {
01802   IIIMCF_context c;
01803   IIIMF_status st;
01804 
01805   c = iiim_get_session_context (context_iiim);
01806   if (!c)
01807     {
01808       set_error_message (context_iiim);
01809       if (st_ret)
01810        *st_ret = IIIMF_STATUS_EVENT_NOT_FORWARDED;
01811       return FALSE;
01812     }
01813   /* Send Message to IIIMSF */
01814   st = iiimcf_forward_event (c, ev);
01815   if (st_ret)
01816     *st_ret = st;
01817 
01818   DEBUG_DO (g_message ("iiimcf_forward_event %d", st));
01819   switch (st)
01820     {
01821     case IIIMF_STATUS_SUCCESS:
01822       break;
01823     case IIIMF_STATUS_IC_INVALID:
01824     case IIIMF_STATUS_EVENT_NOT_FORWARDED:
01825       break;
01826     case IIIMF_STATUS_STREAM_SEND:
01827     case IIIMF_STATUS_STREAM_RECEIVE:
01828     case IIIMF_STATUS_CONNECTION_CLOSED:
01829       set_error_message (context_iiim);
01830       break;
01831     default:
01832       status_window_set_text (context_iiim->status_window, "");
01833       break;
01834     }
01835   return (st == IIIMF_STATUS_SUCCESS);
01836 }
01837 
01838 static gboolean
01839 im_context_iiim_filter_keypress (GtkIMContext * context, GdkEventKey * event)
01840 {
01841   GtkIMContextIIIM *context_iiim = GTK_IM_CONTEXT_IIIM (context);
01842   IIIMF_status st;
01843   IIIMCF_keyevent kev;
01844   IIIMCF_event ev;
01845   gint store_key = 0;
01846   static gboolean was_dead_key = FALSE;
01847 
01848   /* IIIMF doesn't recognize */
01849   if ((GDK_KEY_RELEASE == event->type) &&
01850       (IIIMF_STATUS_SUCCESS !=
01851        iiimcf_is_capability_supported(context_iiim->iiim_info->iiim,
01852                                   IIIMP_CAPABILITY_KEY_RELEASE))) {
01853     return FALSE;
01854   }
01855 
01856   if (current_setting_enabled &&
01857       current_setting.im_enabled == IM_OFF)
01858     goto commit_this_event;
01859 
01860   DEBUG_DO (g_message
01861            ("event in %s %d %d %d", gdk_keyval_name (event->keyval),
01862             event->hardware_keycode, event->length, event->group));
01863 
01864   store_key = check_stored_keyevent (context, event);
01865   if (!store_key)
01866     return FALSE;
01867   else if (store_key > 0)
01868     return TRUE;
01869 
01870 #ifdef DEBUG
01871   if (event->state == GDK_CONTROL_MASK) {
01872     if (event->keyval == GDK_Down)
01873       {
01874        next_input_language (context_iiim);
01875        im_context_initialize_with_input_language (context_iiim, NULL);
01876        im_context_switcher_set_input_language (context_iiim, NULL);
01877        return FALSE;
01878       }
01879     else if (event->keyval == GDK_Up)
01880       {
01881        prev_input_language (context_iiim);
01882        im_context_initialize_with_input_language (context_iiim, NULL);
01883        im_context_switcher_set_input_language (context_iiim, NULL);
01884        return FALSE;
01885       }
01886   }
01887 #endif
01888 
01889   st = convert_GdkEventKey_to_IIIMCF_keyevent (event, &kev);
01890   if (st != IIIMF_STATUS_SUCCESS)
01891     goto commit_this_event;
01892 
01893   st = iiimcf_create_keyevent (&kev, &ev);
01894   if (st != IIIMF_STATUS_SUCCESS)
01895     goto commit_this_event;
01896 
01897   /* Send Message to IIIMSF */
01898   if (forward_event (context_iiim, ev, &st))
01899     return iiim_event_dispatch (context_iiim);
01900 
01901   if (st != IIIMF_STATUS_EVENT_NOT_FORWARDED &&
01902       st != IIIMF_STATUS_IC_INVALID)
01903     return FALSE;
01904 
01905  commit_this_event:
01906   if (IS_DEAD_KEY(event->keyval)) {
01907     /* don't send the dead key with the commit event! */
01908     was_dead_key = TRUE;
01909     return gtk_im_context_filter_keypress(context_iiim->slave, event);
01910   } else if (was_dead_key == TRUE) {
01911     if (event->type == GDK_KEY_RELEASE)
01912       was_dead_key = FALSE;
01913     return gtk_im_context_filter_keypress(context_iiim->slave, event);
01914   }
01915   was_dead_key = FALSE;
01916   if (event->state &
01917       (gtk_accelerator_get_default_mod_mask () & ~GDK_SHIFT_MASK))
01918     return FALSE;
01919   else
01920     {
01921       /* commit this event */
01922       guint32 unicode;
01923 
01924       /* don't need to commit this event twice */
01925       if (event->type == GDK_KEY_RELEASE)
01926        return FALSE;
01927 
01928       unicode = gdk_keyval_to_unicode (event->keyval);
01929       if (g_unichar_isprint (unicode))
01930        {
01931          gchar utf8[7];
01932          gint len;
01933          len = g_unichar_to_utf8 (unicode, utf8);
01934          utf8[len] = 0;
01935          g_signal_emit_by_name (context, "commit", utf8);
01936          return TRUE;
01937        }
01938     }
01939   return FALSE;
01940 }
01941 
01942 static void
01943 set_sc_client_window (GtkIMContextIIIM * context_iiim,
01944                     GdkWindow * client_window, gboolean send_signal)
01945 {
01946   DEBUG_DO (g_message ("set_sc_client_window"));
01947   reinitialize_sc (context_iiim, TRUE);
01948   if (context_iiim->client_window)
01949     {
01950       DEBUG_DO (g_message ("set_sc_client_window 1"));
01951       if (context_iiim->iiim_info)
01952        context_iiim->iiim_info->ics =
01953          g_slist_remove (context_iiim->iiim_info->ics, context_iiim);
01954       context_iiim->iiim_info = NULL;
01955     }
01956   context_iiim->client_window = client_window;
01957 
01958   if (context_iiim->client_window)
01959     {
01960       DEBUG_DO (g_message ("set_sc_client_window 2"));
01961       context_iiim->iiim_info =
01962        get_iiim (context_iiim->client_window);
01963       if (context_iiim->iiim_info)
01964        context_iiim->iiim_info->ics =
01965          g_slist_prepend (context_iiim->iiim_info->ics, context_iiim);
01966     }
01967 
01968   update_client_widget (context_iiim);
01969 }
01970 
01971 static void
01972 im_context_iiim_set_client_window (GtkIMContext * context,
01973                                GdkWindow * client_window)
01974 {
01975   GtkIMContextIIIM *context_iiim = GTK_IM_CONTEXT_IIIM (context);
01976 
01977   DEBUG_DO (g_message ("iiim_set_client_window"));
01978   set_sc_client_window (context_iiim, client_window, TRUE);
01979   if (!context_iiim->context)
01980     return;
01981 
01982   DEBUG_DO (g_message ("set client window"));
01983 }
01984 
01985 static void
01986 im_context_iiim_finalize (GObject * obj)
01987 {
01988   GtkIMContextIIIM *context_iiim = GTK_IM_CONTEXT_IIIM (obj);
01989 
01990   context_iiim->finalizing = TRUE;
01991 
01992   IIim_aux_destrory_ic (context_iiim);
01993 
01994   set_sc_client_window (context_iiim, NULL, FALSE);
01995 
01996   if (context_iiim->candidate)
01997     iiim_destroy_candidate_window (context_iiim);
01998 
01999   im_context_switcher_finalize (context_iiim);
02000 
02001   g_free (context_iiim->current_language);
02002 
02003   iiim_keylist_free (context_iiim);
02004 
02005   g_signal_handlers_disconnect_by_func(context_iiim->slave,
02006                                    (gpointer)im_context_iiim_commit_cb,
02007                                    context_iiim);
02008 
02009   DEBUG_DO (g_message ("im_context_iiim_finalize"));
02010 
02011   G_OBJECT_CLASS(parent_class)->finalize(obj);
02012 }
02013 
02014 static void
02015 reinitialize_sc (GtkIMContextIIIM * context_iiim, gboolean send_signal)
02016 {
02017   IIIMF_status st;
02018   if (context_iiim->context)
02019     {
02020       st = iiimcf_destroy_context (context_iiim->context);
02021       context_iiim->context = NULL;
02022       update_status_window (context_iiim);
02023       context_iiim->lookup_choice = NULL;
02024       g_free (context_iiim->current_language);
02025       context_iiim->current_language = NULL;
02026     }
02027 }
02028 
02029 static void
02030 im_context_iiim_focus_in (GtkIMContext * context)
02031 {
02032   IIIMF_status st;
02033   IIIMCF_event ev;
02034   IIIMCF_context c;
02035   GtkIMContextIIIM *context_iiim = GTK_IM_CONTEXT_IIIM (context);
02036   GtkWidget *cand_win = iiim_get_candidate_window (context_iiim);
02037   guint has_focus;
02038 
02039   DEBUG_DO (g_message ("im_context_iiim_focus_in"));
02040 
02041   get_im_settings_property ();
02042 
02043   has_focus = context_iiim->has_focus;
02044 
02045   if (!has_focus)
02046     {
02047       c = iiim_get_session_context (context_iiim);
02048       if (!c)
02049        return;
02050 
02051       context_iiim->has_focus = TRUE;
02052       update_status_window (context_iiim);
02053       if (context_iiim->status_window != NULL)
02054        {
02055          /* enforce to update the status window position */
02056          on_status_toplevel_configure(context_iiim->status_window->toplevel,
02057                                    NULL,
02058                                    context_iiim->status_window);
02059        }
02060       im_context_switcher_set_focus (context_iiim);
02061       /* tell switcher that I'm the current client */
02062       im_context_switcher_set_focus (context_iiim);
02063     }
02064 
02065   st = iiimcf_create_seticfocus_event (&ev);
02066   if (st != IIIMF_STATUS_SUCCESS)
02067     return;
02068 
02069   if (forward_event (context_iiim, ev, NULL))
02070     iiim_event_dispatch (context_iiim);
02071 
02072   if (cand_win && !GTK_WIDGET_VISIBLE (cand_win))
02073     {
02074       gtk_widget_show (cand_win);
02075     }
02076 
02077 //#ifdef CONFIG_HOTKEY
02078 #if 1
02079     {
02080       if (im_info_switcher_active (context_iiim->iiim_info))
02081         {
02082          char *hotkey;
02083 
02084          /*
02085           * server sends the hotjey when client's context is constructed, client
02086           * tries to get the hotkey info with iiimcf_register_hotkeys by sending
02087           * one HOTKEY_NOTIFY message. Usually it will happen at the first time
02088           * when client sends set icfocus event to server within im_context_iiim_focus_in.
02089           * The hotkey wouldn't be changed at the client's life cycle unless another
02090           * hotkey profile is used insteads.
02091           *
02092           * To communicate with gimlet, current implementation is as followings:
02093           *   once client get focus, it checks whether hotkey info exists with
02094           *   _IIIM_SWITCHER_SET_HOTKEY atom, if it does exists, check further
02095           *   whether the conversion keys get changed with the atom by gimlet,
02096           *   if yes, change the conversion keys. Otherwise, get the hotkey info
02097           *   from server and notify to gimlet with the above atom.
02098           *
02099           *  FIXME one better way to handle the communication is that each client
02100           *        can be notified asynchronously with the atom change from gimlet window. 
02101           */
02102          if ((hotkey = im_context_switcher_get_hotkey_with_atom (context_iiim)) == NULL)
02103            {
02104              if (hotkey = im_context_switcher_get_hotkey (context_iiim))
02105               im_context_switcher_set_hotkey (context_iiim, hotkey);
02106            }
02107          else
02108            {
02109              char *conversion_keys;
02110 
02111              /*
02112               * change the conversion keys if nesssary
02113               * FIXME how can we tell whether it is modified or not?
02114               */
02115              conversion_keys = im_context_switcher_get_hotkey_with_type (hotkey,
02116                                                           TRIGGER_KEY_LABEL);
02117              im_context_switcher_change_hotkey_with_type (context_iiim,
02118                                                     TRIGGER_KEY_LABEL,
02119                                                     conversion_keys);
02120              g_free (conversion_keys);
02121            }
02122 
02123          if (hotkey) g_free (hotkey);
02124        }
02125     }
02126 #endif
02127 
02128   if (!has_focus)
02129     {
02130       if (im_info_switcher_active (context_iiim->iiim_info))
02131        {
02132          IIIMCF_language *lang_list;
02133          int n_lang;
02134 
02135          lang_list = iiim_get_languages (context_iiim->iiim_info, &n_lang);
02136          im_context_switcher_set_language_list (context_iiim, lang_list,
02137                                            n_lang);
02138          im_context_switcher_set_language_engine_list (context_iiim,
02139                                                  context_iiim->iiim_info->le_list);
02140          im_context_switcher_set_input_language (context_iiim, NULL);
02141        }
02142     }
02143   return;
02144 }
02145 
02146 static void
02147 im_context_iiim_focus_out (GtkIMContext * context)
02148 {
02149   GtkIMContextIIIM *context_iiim = GTK_IM_CONTEXT_IIIM (context);
02150   IIIMF_status st;
02151   IIIMCF_event ev;
02152   IIIMCF_context c;
02153   GtkWidget *cand_win = iiim_get_candidate_window (context_iiim);
02154 
02155   DEBUG_DO (g_message ("im_context_iiim_focus_out"));
02156 
02157   if (context_iiim->has_focus)
02158     {
02159       c = iiim_get_session_context (context_iiim);
02160       if (!c)
02161        return;
02162 
02163       context_iiim->has_focus = FALSE;
02164       update_status_window (context_iiim);
02165       IIim_aux_unset_icfocus (context_iiim);
02166 
02167       DEBUG_DO (g_message ("candidate_window %p", cand_win));
02168       if (cand_win && GTK_WIDGET_VISIBLE (cand_win))
02169        {
02170          gtk_widget_hide (cand_win);
02171        }
02172     }
02173 
02174   st = iiimcf_create_unseticfocus_event (&ev);
02175   if (st != IIIMF_STATUS_SUCCESS)
02176     return;
02177 
02178   if (forward_event (context_iiim, ev, NULL))
02179     iiim_event_dispatch (context_iiim);
02180   return;
02181 }
02182 
02183 static void
02184 im_context_iiim_set_cursor_location (GtkIMContext * context,
02185                                  GdkRectangle * area)
02186 {
02187   GtkIMContextIIIM *context_iiim = GTK_IM_CONTEXT_IIIM (context);
02188   DEBUG_DO (g_message ("im_context_iiim_set_cursor_location"));
02189   if (!context_iiim)
02190     return;
02191   context_iiim->cursor.x = area->x;
02192   context_iiim->cursor.y = area->y;
02193   context_iiim->cursor.height = area->height;
02194   context_iiim->cursor.width = area->width;
02195   return;
02196 }
02197 
02198 static void
02199 im_context_iiim_set_use_preedit (GtkIMContext * context, gboolean use_preedit)
02200 {
02201   GtkIMContextIIIM *context_iiim = GTK_IM_CONTEXT_IIIM (context);
02202   DEBUG_DO (g_message ("im_context_iiim_set_use_preedit"));
02203 
02204   use_preedit = use_preedit != FALSE;
02205 
02206   if (context_iiim->use_preedit != use_preedit)
02207     {
02208       context_iiim->use_preedit = use_preedit;
02209       reinitialize_sc (context_iiim, TRUE);
02210     }
02211   return;
02212 }
02213 
02214 static void
02215 im_context_iiim_reset (GtkIMContext * context)
02216 {
02217   GtkIMContextIIIM *context_iiim = GTK_IM_CONTEXT_IIIM(context);
02218   IIIMCF_context ctxt;
02219   IIIMF_status st;
02220   IIIMCF_text text;
02221   IIIMCF_event event;
02222   gint caret_position, preedit_len = 0;
02223   gchar *utf8;
02224 
02225   DEBUG_DO (g_message ("im_context_iiim_reset"));
02226 
02227   ctxt = iiim_get_session_context(context_iiim);
02228   if (!ctxt)
02229     return;
02230 
02231   /* commit the preedit strings first */
02232   st = iiimcf_get_preedit_text(ctxt, &text, &caret_position);
02233   if (st != IIIMF_STATUS_SUCCESS)
02234     return;
02235   st = iiimcf_get_text_length(text, &preedit_len);
02236   if (st != IIIMF_STATUS_SUCCESS)
02237     return;
02238 
02239   if (preedit_len == 0)
02240     return;
02241 
02242   utf8 = utf16_to_utf8(text);
02243   if (utf8 != NULL)
02244     {
02245       /* reset IC */
02246       st = iiimcf_reset_context(ctxt);
02247       if (st != IIIMF_STATUS_SUCCESS)
02248        return;
02249 
02250       /* FIXME: workaround to get it working after calling iiimcf_reset_context */
02251       st = iiimcf_create_trigger_notify_event(0, &event);
02252       if (st != IIIMF_STATUS_SUCCESS)
02253        return;
02254       st = iiimcf_forward_event(ctxt, event);
02255       st = iiimcf_create_trigger_notify_event(1, &event);
02256       if (st != IIIMF_STATUS_SUCCESS)
02257        return;
02258       st = iiimcf_forward_event(ctxt, event);
02259 
02260       /* emit 'commit' signal after resetting IC */
02261       g_signal_emit_by_name(context_iiim, "commit", utf8);
02262       g_free(utf8);
02263 
02264       if (preedit_len > 0)
02265        g_signal_emit_by_name(context_iiim, "preedit_changed");
02266     }
02267 }
02268 
02269 static void
02270 add_feedback_attr (PangoAttrList * attrs,
02271                  const gchar * str,
02272                  const IIIMP_card32 feedback,
02273                  gint start_pos, gint end_pos)
02274 {
02275   PangoAttribute *attr;
02276   gint start_index = g_utf8_offset_to_pointer (str, start_pos) - str;
02277   gint end_index = g_utf8_offset_to_pointer (str, end_pos) - str;
02278 
02279   DEBUG_DO (g_message ("feedback %d", feedback));
02280 
02281   if (feedback == 2 /* Underline */ )
02282     {
02283       attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
02284       attr->start_index = start_index;
02285       attr->end_index = end_index;
02286 
02287       pango_attr_list_change (attrs, attr);
02288     }
02289 
02290   if (feedback == 1 /* Reverse */ )
02291     {
02292       attr = pango_attr_foreground_new (0xffff, 0xffff, 0xffff);
02293       attr->start_index = start_index;
02294       attr->end_index = end_index;
02295 
02296       pango_attr_list_change (attrs, attr);
02297 
02298       attr = pango_attr_background_new (0, 0, 0);
02299       attr->start_index = start_index;
02300       attr->end_index = end_index;
02301 
02302       pango_attr_list_change (attrs, attr);
02303     }
02304 }
02305 
02306 static gboolean
02307 iiim_check_feedback (const IIIMP_card32 * feedback, gint nfb,
02308                    const IIIMP_card32 * feedback2, gint nfb2)
02309 {
02310   gint i;
02311   gboolean result = TRUE;
02312   if (!feedback)
02313     return FALSE;
02314   if (nfb != nfb2)
02315     return FALSE;
02316   for (i = 0; i < nfb; i++)
02317     {
02318       if (feedback[i] != feedback2[i])
02319        result = FALSE;
02320     }
02321   return result;
02322 }
02323 
02324 static void
02325 im_context_iiim_get_preedit_string (GtkIMContext * context,
02326                                 gchar ** str,
02327                                 PangoAttrList ** attrs, gint * cursor_pos)
02328 {
02329   GtkIMContextIIIM *im_context_iiim = GTK_IM_CONTEXT_IIIM (context);
02330   IIIMF_status st;
02331   IIIMCF_text text;
02332   gint caret_position;
02333   gint preedit_len;
02334   gchar *utf8;
02335 
02336   if (attrs)
02337     *attrs = pango_attr_list_new ();
02338 
02339   if (!im_context_iiim->context)
02340     {
02341       DEBUG_DO (g_message ("preedit_string context is not initialized"));
02342       goto Error;
02343     }
02344   st =
02345     iiimcf_get_preedit_text (im_context_iiim->context, &text,
02346                           &caret_position);
02347   if (st != IIIMF_STATUS_SUCCESS)
02348     goto Error;
02349   utf8 = utf16_to_utf8 (text);
02350 
02351   if (attrs)
02352     {
02353       gint i;
02354       gint j;
02355       IIIMP_card32 last_visual_feedback = 0;
02356       gint start = -1;
02357 
02358       st = iiimcf_get_text_length (text, &preedit_len);
02359       if (st != IIIMF_STATUS_SUCCESS)
02360        goto Error;
02361 
02362       for (i = 0; i < preedit_len; i++)
02363        {
02364          IIIMP_card16 ch;
02365          const IIIMP_card32 *feedback_ids, *feedbacks;
02366          int nfb;
02367 
02368          st =
02369            iiimcf_get_char_with_feedback (text, i, &ch, &nfb, &feedback_ids,
02370                                       &feedbacks);
02371          if (st != IIIMF_STATUS_SUCCESS)
02372            goto Error;
02373          for (j = 0; j < nfb; j++)
02374            {
02375              IIIMP_card32 new_visual_feedback;
02376 
02377              switch (feedback_ids[j])
02378               {
02379               case 0:              /* VISUAL ATTRIBUTES */
02380                 new_visual_feedback = feedbacks[j];
02381                 if (new_visual_feedback != last_visual_feedback)
02382                   {
02383                     if (start >= 0)
02384                      add_feedback_attr (*attrs, utf8, last_visual_feedback,
02385                                       start, i);
02386                     last_visual_feedback = new_visual_feedback;
02387                     start = i;
02388                   }
02389               default:
02390                 break;
02391               }
02392            }
02393        }
02394       if (start >= 0)
02395        add_feedback_attr (*attrs, utf8, last_visual_feedback, start, i);
02396     }
02397 
02398   if (str)
02399     *str = utf8;
02400   else
02401     g_free (utf8);
02402 
02403   DEBUG_DO (g_message ("cursor %d", caret_position));
02404   if (cursor_pos)
02405     *cursor_pos = caret_position;
02406   return;
02407 
02408 Error:
02409   if (str)
02410     {
02411       *str = g_strdup ("");
02412     }
02413   if (cursor_pos)
02414     {
02415       *cursor_pos = 0;
02416     }
02417 
02418 }
02419 
02420 GtkIMContext *
02421 im_context_iiim_new (void)
02422 {
02423   GtkIMContextIIIM *result;
02424 
02425   result =
02426     GTK_IM_CONTEXT_IIIM (g_object_new (GTK_TYPE_IM_CONTEXT_IIIM, NULL));
02427 
02428   return GTK_IM_CONTEXT (result);
02429 }
02430 
02431 /*****************************************************************
02432  * Status Window handling
02433  *
02434  * A status window is a small window attached to the toplevel
02435  * that is used to display information to the user about the
02436  * current input operation.
02437  *
02438  * We claim the toplevel's status window for an input context if:
02439  *
02440  * A) The input context has a toplevel
02441  * B) The input context has the focus
02442  * C) The input context has an XIC associated with it
02443  *
02444  * Tracking A) and C) is pretty reliable since we
02445  * compute A) and create the XIC for C) ourselves.
02446  * For B) we basically have to depend on our callers
02447  * calling ::focus-in and ::focus-out at the right time.
02448  *
02449  * The toplevel is computed by walking up the GdkWindow
02450  * hierarchy from context->client_window until we find a
02451  * window that is owned by some widget, and then calling
02452  * gtk_widget_get_toplevel() on that widget. This should
02453  * handle both cases where we might have GdkWindows without widgets,
02454  * and cases where GtkWidgets have strange window hierarchies
02455  * (like a torn off GtkHandleBox.)
02456  *
02457  * The status window is visible if and only if there is text
02458  * for it; whenever a new GtkIMContextIIIM claims the status
02459  * window, we blank out any existing text. We actually only
02460  * create a GtkWindow for the status window the first time
02461  * it is shown; this is an important optimization when we are
02462  * using IIIM with something like a simple compose-key input
02463  * method that never needs a status window.
02464  *****************************************************************/
02465 
02466 /* Called when we no longer need a status window
02467 */
02468 static void
02469 disclaim_status_window (GtkIMContextIIIM *context_iiim)
02470 {
02471   if (context_iiim->status_window)
02472     {
02473       g_assert (context_iiim->status_window->context == context_iiim);
02474 
02475       status_window_set_text (context_iiim->status_window, "");
02476       
02477       context_iiim->status_window->context = NULL;
02478       context_iiim->status_window = NULL;
02479     }
02480 }
02481 
02482 /* Called when we need a status window
02483  */
02484 static void
02485 claim_status_window (GtkIMContextIIIM *context_iiim)
02486 {
02487   if (!context_iiim->status_window && context_iiim->client_widget)
02488     {
02489       GtkWidget *toplevel = gtk_widget_get_toplevel (context_iiim->client_widget);
02490       if (toplevel && GTK_WIDGET_TOPLEVEL (toplevel))
02491        {
02492          StatusWindow *status_window = status_window_get (toplevel);
02493 
02494          if (status_window->context)
02495            disclaim_status_window (status_window->context);
02496 
02497          status_window->context = context_iiim;
02498          context_iiim->status_window = status_window;
02499        }
02500     }
02501 }
02502 
02503 /* Basic call made whenever something changed that might cause
02504  * us to need, or not to need a status window.
02505  */
02506 static void
02507 update_status_window (GtkIMContextIIIM *context_iiim)
02508 {
02509   if (context_iiim->in_toplevel && context_iiim->has_focus)
02510     claim_status_window (context_iiim);
02511   else
02512     disclaim_status_window (context_iiim);
02513 }
02514 
02515 /* Updates the in_toplevel flag for @context_iiim
02516  */
02517 static void
02518 update_in_toplevel (GtkIMContextIIIM *context_iiim)
02519 {
02520   if (context_iiim->client_widget)
02521     {
02522       GtkWidget *toplevel = gtk_widget_get_toplevel (context_iiim->client_widget);
02523       
02524       context_iiim->in_toplevel = (toplevel && GTK_WIDGET_TOPLEVEL (toplevel));
02525     }
02526   else
02527     context_iiim->in_toplevel = FALSE;
02528 
02529   /* Some paranoia, in case we don't get a focus out */
02530   if (!context_iiim->in_toplevel)
02531     context_iiim->has_focus = FALSE;
02532   
02533   update_status_window (context_iiim);
02534 }
02535 
02536 /* Callback when @widget's toplevel changes. It will always
02537  * change from NULL to a window, or a window to NULL;
02538  * we use that intermediate NULL state to make sure
02539  * that we disclaim the toplevel status window for the old
02540  * window.
02541  */
02542 static void
02543 on_client_widget_hierarchy_changed (GtkWidget       *widget,
02544                                 GtkWidget       *old_toplevel,
02545                                 GtkIMContextIIIM *context_iiim)
02546 {
02547   update_in_toplevel (context_iiim);
02548 }
02549 
02550 /* Finds the GtkWidget that owns the window, or if none, the
02551  * widget owning the nearest parent that has a widget.
02552  */
02553 static GtkWidget *
02554 widget_for_window (GdkWindow *window)
02555 {
02556   while (window)
02557     {
02558       gpointer user_data;
02559       gdk_window_get_user_data (window, &user_data);
02560       if (user_data)
02561        return user_data;
02562 
02563       window = gdk_window_get_parent (window);
02564     }
02565 
02566   return NULL;
02567 }
02568 
02569 /* Called when context_iiim->client_window changes; takes care of
02570  * removing and/or setting up our watches for the toplevel
02571  */
02572 static void
02573 update_client_widget (GtkIMContextIIIM *context_iiim)
02574 {
02575   GtkWidget *new_client_widget = widget_for_window (context_iiim->client_window);
02576 
02577   if (new_client_widget != context_iiim->client_widget)
02578     {
02579       if (context_iiim->client_widget)
02580        {
02581          g_signal_handlers_disconnect_by_func (context_iiim->client_widget,
02582                                           G_CALLBACK (on_client_widget_hierarchy_changed),
02583                                           context_iiim);
02584        }
02585       context_iiim->client_widget = new_client_widget;
02586       if (context_iiim->client_widget)
02587        {
02588          g_signal_connect (context_iiim->client_widget, "hierarchy-changed",
02589                          G_CALLBACK (on_client_widget_hierarchy_changed),
02590                          context_iiim);
02591        }
02592 
02593       update_in_toplevel (context_iiim);
02594     }
02595 }
02596 
02597 /* Called when the toplevel is destroyed; frees the status window
02598  */
02599 static void
02600 on_status_toplevel_destroy (GtkWidget    *toplevel,
02601                          StatusWindow *status_window)
02602 {
02603   status_window_free (status_window);
02604 }
02605 
02606 /* Called when the screen for the toplevel changes; updates the
02607  * screen for the status window to match.
02608  */
02609 static void
02610 on_status_toplevel_notify_screen (GtkWindow    *toplevel,
02611                               GParamSpec   *pspec,
02612                               StatusWindow *status_window)
02613 {
02614   if (status_window->window)
02615     gtk_window_set_screen (GTK_WINDOW (status_window->window),
02616                         gtk_widget_get_screen (GTK_WIDGET (toplevel)));
02617 }
02618 
02619 /* Called when the toplevel window is moved; updates the position of
02620  * the status window to follow it.
02621  */
02622 static gboolean
02623 on_status_toplevel_configure (GtkWidget         *toplevel,
02624                            GdkEventConfigure *event,
02625                            StatusWindow      *status_window)
02626 {
02627   GdkRectangle rect;
02628   GtkRequisition requisition;
02629   gint y;
02630   gint height;
02631 
02632   if (status_window->window)
02633     {
02634       height = gdk_screen_get_height (gtk_widget_get_screen (toplevel));
02635   
02636       gdk_window_get_frame_extents (toplevel->window, &rect);
02637       gtk_widget_size_request (status_window->window, &requisition);
02638       
02639       if (rect.y + rect.height + requisition.height < height)
02640        y = rect.y + rect.height;
02641       else
02642        y = height - requisition.height;
02643       
02644       gtk_window_move (GTK_WINDOW (status_window->window), rect.x, y);
02645     }
02646 
02647   return FALSE;
02648 }
02649 
02650 /* Frees a status window and removes its link from the status_windows list
02651  */
02652 static void
02653 status_window_free (StatusWindow *status_window)
02654 {
02655   status_windows = g_slist_remove (status_windows, status_window);
02656 
02657   if (status_window->context)
02658     status_window->context->status_window = NULL;
02659  
02660   g_signal_handlers_disconnect_by_func (status_window->toplevel,
02661                                    G_CALLBACK (on_status_toplevel_destroy),
02662                                    status_window);
02663   g_signal_handlers_disconnect_by_func (status_window->toplevel,
02664                                    G_CALLBACK (on_status_toplevel_notify_screen),
02665                                    status_window);
02666   g_signal_handlers_disconnect_by_func (status_window->toplevel,
02667                                    G_CALLBACK (on_status_toplevel_configure),
02668                                    status_window);
02669 
02670   if (status_window->window)
02671     gtk_widget_destroy (status_window->window);
02672   
02673   g_object_set_data (G_OBJECT (status_window->toplevel), "gtk-im-iiim-status-window", NULL);
02674  
02675   g_free (status_window);
02676 }
02677 
02678 /* Finds the status window object for a toplevel, creating it if necessary.
02679  */
02680 static StatusWindow *
02681 status_window_get (GtkWidget *toplevel)
02682 {
02683   StatusWindow *status_window;
02684 
02685   status_window = g_object_get_data (G_OBJECT (toplevel), "gtk-im-iiim-status-window");
02686   if (status_window)
02687     return status_window;
02688   
02689   status_window = g_new0 (StatusWindow, 1);
02690   status_window->toplevel = toplevel;
02691 
02692   status_windows = g_slist_prepend (status_windows, status_window);
02693 
02694   g_signal_connect (toplevel, "destroy",
02695                   G_CALLBACK (on_status_toplevel_destroy),
02696                   status_window);
02697   g_signal_connect (toplevel, "configure_event",
02698                   G_CALLBACK (on_status_toplevel_configure),
02699                   status_window);
02700   g_signal_connect (toplevel, "notify::screen",
02701                   G_CALLBACK (on_status_toplevel_notify_screen),
02702                   status_window);
02703   
02704   g_object_set_data (G_OBJECT (toplevel), "gtk-im-iiim-status-window", status_window);
02705 
02706   return status_window;
02707 }
02708 
02709 /* Draw the background (normally white) and border for the status window
02710  */
02711 static gboolean
02712 on_status_window_expose_event (GtkWidget      *widget,
02713                             GdkEventExpose *event)
02714 {
02715   gdk_draw_rectangle (widget->window,
02716                     widget->style->base_gc [GTK_STATE_NORMAL],
02717                     TRUE,
02718                     0, 0,
02719                     widget->allocation.width, widget->allocation.height);
02720   gdk_draw_rectangle (widget->window,
02721                     widget->style->text_gc [GTK_STATE_NORMAL],
02722                     FALSE,
02723                     0, 0,
02724                     widget->allocation.width - 1, widget->allocation.height - 1);
02725 
02726   return FALSE;
02727 }
02728 
02729 /* We watch the ::style-set signal for our label widget
02730  * and use that to change it's foreground color to match
02731  * the 'text' color of the toplevel window. The text/base
02732  * pair of colors might be reversed from the fg/bg pair
02733  * that are normally used for labels.
02734  */
02735 static void
02736 on_status_window_style_set (GtkWidget *toplevel,
02737                          GtkStyle  *previous_style,
02738                          GtkWidget *label)
02739 {
02740   gint i;
02741   
02742   for (i = 0; i < 5; i++)
02743     gtk_widget_modify_fg (label, i, &toplevel->style->text[i]);
02744 }
02745 
02746 /* Creates the widgets for the status window; called when we
02747  * first need to show text for the status window.
02748  */
02749 static void
02750 status_window_make_window (StatusWindow *status_window)
02751 {
02752   GtkWidget *window;
02753   GtkWidget *status_label;
02754   
02755   status_window->window = gtk_window_new (GTK_WINDOW_POPUP);
02756   window = status_window->window;
02757 
02758   gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
02759   gtk_widget_set_app_paintable (window, TRUE);
02760 
02761   status_label = gtk_label_new ("");
02762   gtk_misc_set_padding (GTK_MISC (status_label), 1, 1);
02763   gtk_widget_show (status_label);
02764   
02765   g_signal_connect (window, "style_set",
02766                   G_CALLBACK (on_status_window_style_set), status_label);
02767   gtk_container_add (GTK_CONTAINER (window), status_label);
02768   
02769   g_signal_connect (window, "expose_event",
02770                   G_CALLBACK (on_status_window_expose_event), NULL);
02771   
02772   gtk_window_set_screen (GTK_WINDOW (status_window->window),
02773                       gtk_widget_get_screen (status_window->toplevel));
02774 
02775   on_status_toplevel_configure (status_window->toplevel, NULL, status_window);
02776 }
02777 
02778 /* Updates the text in the status window, hiding or
02779  * showing the window as necessary.
02780  */
02781 static void
02782 status_window_set_text (StatusWindow *status_window,
02783                      const gchar  *text)
02784 {
02785   if (status_window == NULL)
02786     /* status window is disclaimed */
02787     return;
02788 
02789   if (text[0])
02790     {
02791       GtkWidget *label;
02792       
02793       if (!status_window->window)
02794        status_window_make_window (status_window);
02795       
02796       label = GTK_BIN (status_window->window)->child;
02797       gtk_label_set_text (GTK_LABEL (label), text);
02798   
02799       gtk_widget_show (status_window->window);
02800     }
02801   else
02802     {
02803       if (status_window->window)
02804        gtk_widget_hide (status_window->window);
02805     }
02806 }
02807 
02814 void
02815 im_context_iiim_shutdown (void)
02816 {
02817   GSList *tmp_list;
02818   GtkIIIMInfo *info = NULL;
02819 
02820   DEBUG_DO (g_message ("shutdown\n"));
02821   if (iiim)
02822     iiimcf_destroy_handle (iiim);
02823   iiimcf_finalize ();
02824   iiim_is_initialized = FALSE;
02825   while (status_windows)
02826     status_window_free (status_windows->data);
02827 
02828   tmp_list = open_iiims;
02829   while (tmp_list)
02830     {
02831       info = tmp_list->data;
02832       g_signal_handler_disconnect (info->settings, info->status_set);
02833       im_info_switcher_shutdown (info);
02834       tmp_list = tmp_list->next;
02835     }
02836 }
02837 
02838 void
02839 im_context_initialize_with_input_language (GtkIMContextIIIM *context_iiim,
02840                                       gchar *new_lang)
02841 
02842 {
02843   IIIMCF_language iiim_lang = NULL;
02844   IIIMF_status st;
02845   IIIMCF_attr attr;
02846   IIIMCF_event ev;
02847   gboolean conversion_mode = FALSE;
02848   gchar **names;
02849   gchar *le_name = NULL;
02850   gint i = 0, n;
02851   const IIIMP_card16 *u16idname, *u16hrn, *u16domain;
02852   gboolean found_le = FALSE;
02853   IIIMCF_input_method *pims;
02854 
02855   if (context_iiim == NULL || context_iiim->context == NULL)
02856     return;
02857 
02858   if (new_lang)
02859     {
02860       names = g_strsplit(new_lang, ":", -1);
02861       new_lang = names[0];
02862       le_name = names[1];
02863 
02864       iiim_lang = get_input_language (context_iiim,
02865                                   new_lang,
02866                                   TRUE);
02867       if (iiim_lang != NULL)
02868        {
02869          g_free (context_iiim->current_language);
02870          context_iiim->current_language = g_strdup (new_lang);
02871        }
02872       else
02873        /* invalid new lang */
02874        return;
02875     }
02876   else
02877     iiim_lang = get_input_language (context_iiim,
02878                                 context_iiim->current_language,
02879                                 FALSE);
02880 
02881   if (le_name != NULL)
02882     {
02883       char *idname = NULL;
02884       /* probably le_name consists of 'lename' and 'imename' which are separated by '-' */
02885       const char *ime_delimiter = "-";
02886       char **le_ime = g_strsplit (le_name, ime_delimiter, -1);
02887       char *lename = le_ime[0], *imename = le_ime[1];
02888 
02889       st = iiimcf_get_supported_input_methods (iiim, &n, &pims);
02890       if (st != IIIMF_STATUS_SUCCESS)
02891        return;
02892 
02893       for (i = 0; i < n; i++)
02894        {
02895          st = iiimcf_get_input_method_desc (pims[i], &u16idname, &u16hrn, &u16domain);
02896          if (st != IIIMF_STATUS_SUCCESS)
02897            return;
02898 
02899          idname = format_iiimcf_string (u16hrn);
02900           if (idname != NULL && strcmp (lename, idname) == 0)
02901            {
02902              /* update the pims[i]->imname with the new imname that contains imename */
02903              if (imename)
02904                {
02905                IIIMP_card16 *imname = format_utf8_string (le_name);
02906                iiimcf_set_input_method_name (pims[i], imname);
02907               }
02908 
02909              g_free (idname);
02910              found_le = TRUE;
02911              break;
02912            }
02913          if (idname != NULL) {
02914            g_free (idname);
02915            idname = NULL;
02916          }
02917        }
02918        g_free (le_ime);
02919     }
02920   /* save conversion mode to restore this after switching */
02921   st = iiimcf_get_current_conversion_mode (context_iiim->context,
02922                                       &conversion_mode);
02923 
02924   /* to cancel any remaining preedit text */
02925   if (!context_iiim->finalizing)
02926     g_signal_emit_by_name (context_iiim, "preedit_changed");
02927 
02928   /* clear candidate if any */
02929   if (context_iiim->candidate_start == TRUE)
02930     {
02931       iiim_destroy_candidate_window (context_iiim);
02932       context_iiim->candidate_start = FALSE;
02933     }
02934 
02935   context_iiim->lookup_choice = NULL;
02936 
02937   st = iiimcf_create_attr (&attr);
02938   if (st != IIIMF_STATUS_SUCCESS)
02939     return;
02940   if (found_le)
02941       iiimcf_attr_put_ptr_value (attr, IIIMCF_ATTR_INPUT_METHOD,
02942                              pims[i]);
02943   if (iiim_lang)
02944     iiimcf_attr_put_ptr_value (attr, IIIMCF_ATTR_INPUT_LANGUAGE,
02945                             iiim_lang);
02946   st = iiimcf_context_set_attr(context_iiim->context, attr);
02947   iiimcf_destroy_attr (attr);
02948   if (st != IIIMF_STATUS_SUCCESS)
02949     return;
02950 
02951   st = iiimcf_create_seticfocus_event (&ev);
02952 
02953   if (st != IIIMF_STATUS_SUCCESS)
02954     return;
02955 
02956   forward_event(context_iiim, ev, NULL);
02957   if (conversion_mode)
02958     {
02959       st = iiimcf_create_trigger_notify_event (CONV_ON, &ev);
02960       if (st != IIIMF_STATUS_SUCCESS)
02961        return;
02962       forward_event(context_iiim, ev, NULL);
02963     }
02964   iiim_event_dispatch (context_iiim);
02965 }
02966 
02967 void
02968 im_context_change_conversion_mode (GtkIMContextIIIM *context_iiim,
02969                                gchar *conv_mode)
02970 {
02971   IIIMCF_event event;
02972 
02973   if (conv_mode && !strcmp ((gchar*)conv_mode, "on"))
02974     {
02975       IIIMF_status st;
02976       st = iiimcf_create_trigger_notify_event (CONV_ON, &event);
02977       if (st != IIIMF_STATUS_SUCCESS)
02978        return;
02979     }
02980   else if (conv_mode && !strcmp ((gchar*)conv_mode, "off"))
02981     {
02982       IIIMF_status st;
02983       st = iiimcf_create_trigger_notify_event (CONV_OFF, &event);
02984       if (st != IIIMF_STATUS_SUCCESS)
02985        return;
02986     }
02987   if (forward_event(context_iiim, event, NULL))
02988     iiim_event_dispatch (context_iiim);
02989 }
02990 
02991 GdkScreen*
02992 im_info_get_screen (GtkIIIMInfo *info)
02993 {
02994   if (info == NULL)
02995     return NULL;
02996   return info->screen;
02997 }
02998 
02999 IIIMCF_handle
03000 im_info_get_handle (GtkIIIMInfo *info)
03001 {
03002   if (info == NULL)
03003     return NULL;
03004   return info->iiim;
03005 }
03006 
03007 SwitcherInfo*
03008 im_info_get_switcher_info (GtkIIIMInfo *info)
03009 {
03010   if (info == NULL)
03011     return NULL;
03012   return info->switcher_info;
03013 }
03014 
03015 void
03016 im_info_set_switcher_info (GtkIIIMInfo *info, SwitcherInfo *sw_info)
03017 {
03018   info->switcher_info = sw_info;
03019 }
03020 
03021 /* Aux */
03022 void
03023 im_context_aux_set_values (GtkIMContextIIIM *context_iiim,
03024                         IIIMCF_event ev)
03025 {
03026   forward_event(context_iiim, ev, NULL);
03027   iiim_event_dispatch (context_iiim);
03028 }
03029 
03030 void
03031 im_context_aux_get_values (GtkIMContextIIIM *context_iiim,
03032                         IIIMCF_event ev)
03033 {
03034   forward_event(context_iiim, ev, NULL);
03035   iiim_event_dispatch (context_iiim);
03036 }
03037 
03038 /* Local Variables: */
03039 /* c-file-style: "gnu" */
03040 /* End: */