Back to index

im-sdk  12.3.91
imswitcher.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2000 Red Hat Software
00003  * Copyright (C) 2003 Motonobu Ichimura
00004  * Copyright 2003 Sun Microsystems Inc.
00005  *
00006  * This is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Lesser General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2 of the License, or (at your option) any later version.
00010  *
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with this library; if not, write to the
00018  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00019  * Boston, MA 02111-1307, USA.
00020  *
00021  * Authors: Hidetoshi Tajima <hidetoshi.tajima@sun.com>
00022  */
00023 
00024 #include <gdk/gdkx.h>
00025 #include <gtk/gtk.h>
00026 #include <stdlib.h>
00027 #include <string.h>
00028 #include "iiimcf.h"
00029 #include "gtkimcontextiiim.h"
00030 #include "imswitcher.h"
00031 #include "IIIMGdkEventKey.h"
00032 
00033 #include <gtk/gtkinvisible.h>
00034 #include <gdk/gdkproperty.h>
00035 #include <gdk/gdkselection.h>
00036 
00037 /* #define DEBUG */
00038 #ifdef DEBUG
00039 #define DEBUG_DO(x) (x)
00040 #else
00041 #define DEBUG_DO(x)
00042 #endif
00043 
00044 struct _SwitcherInfo
00045 {
00046   GdkWindow *switcher;
00047   GdkAtom  selection_atom;
00048   GdkAtom  set_current_input_language_atom;
00049   GdkAtom  set_current_client_atom;
00050   GdkAtom  set_status_text_atom;
00051   GdkAtom  set_input_language_list_atom;
00052   GdkAtom  set_language_engine_list_atom;
00053   GdkAtom  set_conversion_mode_atom;
00054   GdkAtom  set_hotkey_atom;
00055 
00056   /*
00057     When switcher is NULL while switcher_x_window isn't and works, it is due
00058     to gdk_selection_owner_get()'s bug. See bugzilla #126375.
00059   */
00060   Window switcher_x_window;
00061 };
00062 
00063 /* A listener window for input method switcher */
00064 struct _SwitcherContext
00065 {
00066   GtkWidget *invisible;
00067 
00068   gulong destroy_handler_id;
00069   gulong property_handler_id;
00070 };
00071 
00072 
00073 static GdkFilterReturn switcher_owner_filter (GdkXEvent *xev, GdkEvent *event,
00074                                          gpointer data);
00075 static gboolean filter_destroy_event (Display *d, Window w, XEvent *ev,
00076                                   gpointer data);
00077 
00078 gboolean
00079 im_info_switcher_new (GtkIIIMInfo *info)
00080 {
00081   GdkAtom selection = GDK_NONE;
00082   SwitcherInfo *sw_info;
00083 
00084   if (info == NULL)
00085     return FALSE;
00086 
00087   selection = gdk_atom_intern ("_IIIM_SWITCHER", FALSE);
00088 
00089   sw_info = im_info_get_switcher_info (info);
00090   if (sw_info == NULL)
00091     {
00092       sw_info = g_new0 (SwitcherInfo, 1);
00093       im_info_set_switcher_info (info, sw_info);
00094     }
00095 
00096   if (selection != GDK_NONE)
00097     sw_info->switcher = gdk_selection_owner_get (selection);
00098 
00099   sw_info->selection_atom = selection;
00100 
00101   if (sw_info->switcher)
00102     gdk_window_add_filter (sw_info->switcher,
00103                         switcher_owner_filter, info);
00104   else
00105     {
00106       /*
00107        this could be due to bugzilla 126375, hence try to
00108        do Xlib directly.
00109       */
00110       GdkScreen *screen;
00111       GdkDisplay *display;
00112       Atom x_atom;
00113       Window xwindow;
00114 
00115       screen = im_info_get_screen (info);
00116       if (screen == None)
00117          return FALSE;
00118 
00119       display = gdk_screen_get_display (screen);
00120       x_atom = gdk_x11_atom_to_xatom_for_display (display, 
00121                                             selection);
00122       xwindow = XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display),
00123                                 x_atom);
00124       if (xwindow == None)
00125        {
00126          DEBUG_DO (g_message ("Unable to find input method switcher"));
00127          return FALSE;
00128        }
00129       sw_info->switcher_x_window = xwindow;
00130 
00131       _XRegisterFilterByType (GDK_DISPLAY_XDISPLAY (display), xwindow,
00132                            DestroyNotify, DestroyNotify,
00133                            filter_destroy_event, info);
00134       XSelectInput (GDK_DISPLAY_XDISPLAY (display), xwindow, StructureNotifyMask);
00135     }
00136 
00137   sw_info->set_current_input_language_atom
00138     = gdk_atom_intern ("_IIIM_SWITCHER_CURRENT_INPUT_LANGUAGE", FALSE);
00139 
00140   sw_info->set_current_client_atom =
00141     gdk_atom_intern ("_IIIM_SWITCHER_CURRENT_CLIENT", FALSE);
00142 
00143   sw_info->set_status_text_atom
00144     = gdk_atom_intern ("_IIIM_SWITCHER_STATUS_TEXT", FALSE);
00145 
00146   sw_info->set_input_language_list_atom =
00147     gdk_atom_intern ("_IIIM_SWITCHER_INPUT_LANGUAGE_LIST", FALSE);
00148 
00149   sw_info->set_language_engine_list_atom
00150     = gdk_atom_intern ("_IIIM_SWITCHER_LANGUAGE_ENGINE_LIST", FALSE);
00151 
00152   sw_info->set_conversion_mode_atom = 
00153     gdk_atom_intern ("_IIIM_SWITCHER_SET_CONVERSION_MODE", FALSE);
00154 
00155   sw_info->set_hotkey_atom =
00156     gdk_atom_intern ("_IIIM_SWITCHER_SET_HOTKEY", FALSE);
00157 
00158   return TRUE;
00159 }
00160 
00161 static void
00162 destroy_switcher_window (GtkWidget *widget, GtkIMContextIIIM *context_iiim)
00163 {
00164   SwitcherContext *w = context_iiim->switcher_context;
00165   if (!w)
00166     return;
00167   gtk_widget_destroy (w->invisible);
00168   g_free (w);
00169   context_iiim->switcher_context = NULL;
00170   return;
00171 }
00172 
00173 static void
00174 property_notify_switcher_window (GtkWidget *widget, GdkEventProperty *ev,
00175                              GtkIMContextIIIM *context_iiim)
00176 {
00177   GdkAtom  type;
00178   guchar   *data = NULL;
00179   gint     format;
00180   gint     length;
00181   SwitcherInfo *sw_info = im_info_get_switcher_info (context_iiim->iiim_info);
00182 
00183   if (context_iiim->context == NULL)
00184     return;
00185 
00186   if (ev->atom == sw_info->set_current_input_language_atom)
00187     {
00188       gdk_property_get (widget->window, ev->atom, ev->atom,
00189                      0, INT_MAX, FALSE,
00190                      &type, &format, &length, &data);
00191       im_context_initialize_with_input_language (context_iiim,
00192                                            data);
00193       g_free (data);
00194     }
00195   if (ev->atom == sw_info->set_conversion_mode_atom)
00196     {
00197       gdk_property_get (widget->window, ev->atom, ev->atom,
00198                      0, INT_MAX, FALSE,
00199                      &type, &format, &length, &data);
00200       im_context_change_conversion_mode (context_iiim,
00201                                     data);
00202       g_free (data);
00203     }
00204   return;
00205 }
00206 
00207 void
00208 im_context_switcher_set_status_text (GtkIMContextIIIM *context_iiim, 
00209                                  gchar *utf8)
00210 {
00211   GtkIIIMInfo *info = context_iiim->iiim_info;
00212   SwitcherInfo *sw_info = im_info_get_switcher_info (info);
00213 
00214   if (sw_info == NULL)
00215     return;
00216 
00217   if (sw_info->switcher)
00218     {
00219       gdk_property_change (sw_info->switcher, 
00220                         sw_info->set_status_text_atom,
00221                         sw_info->set_status_text_atom,
00222                         8,
00223                         GDK_PROP_MODE_REPLACE,
00224                         (unsigned char*)utf8,
00225                         strlen (utf8));
00226     }
00227   else if (sw_info->switcher_x_window)
00228     {
00229       GdkScreen *screen = im_info_get_screen (info);
00230       GdkDisplay *display = gdk_screen_get_display (screen);
00231       Atom x_atom = gdk_x11_atom_to_xatom_for_display (display,
00232                                sw_info->set_status_text_atom);
00233 
00234       XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
00235                      sw_info->switcher_x_window,
00236                      x_atom,
00237                      x_atom,
00238                      8,
00239                      PropModeReplace,
00240                      (guchar *)utf8,
00241                      strlen (utf8));
00242     }
00243 }
00244 
00245 void
00246 im_context_switcher_set_conversion_mode (GtkIMContextIIIM *context_iiim)
00247 {
00248   GtkIIIMInfo *info = context_iiim->iiim_info;
00249   SwitcherInfo *sw_info = im_info_get_switcher_info (info);
00250   IIIMF_status st;
00251   gint conversion_mode = FALSE;
00252 
00253   if (sw_info == NULL)
00254     return;
00255 
00256   st = iiimcf_get_current_conversion_mode (context_iiim->context,
00257                                       &conversion_mode);
00258 
00259   if (sw_info->switcher)
00260     {
00261       gdk_property_change (sw_info->switcher, 
00262                         sw_info->set_conversion_mode_atom,
00263                         sw_info->set_conversion_mode_atom,
00264                         32,
00265                         GDK_PROP_MODE_REPLACE,
00266                         (unsigned char*)&conversion_mode,
00267                         1);
00268     }
00269   else if (sw_info->switcher_x_window)
00270     {
00271       GdkScreen *screen = im_info_get_screen (info);
00272       GdkDisplay *display = gdk_screen_get_display (screen);
00273       Atom x_atom = gdk_x11_atom_to_xatom_for_display (display,
00274                             sw_info->set_conversion_mode_atom);
00275 
00276       XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
00277                      sw_info->switcher_x_window,
00278                      x_atom,
00279                      x_atom,
00280                      32,
00281                      PropModeReplace,
00282                      (guchar *)&conversion_mode,
00283                      1);
00284     }
00285 }
00286 
00287 /* change the hotkey property for the gimlet window */
00288 void
00289 im_context_switcher_set_hotkey (GtkIMContextIIIM *context_iiim,
00290                                 char *hotkey)
00291 {
00292   GtkIIIMInfo *info = context_iiim->iiim_info;
00293   SwitcherInfo *sw_info = im_info_get_switcher_info (info);
00294 
00295   if (sw_info == NULL || hotkey == NULL)
00296     return;
00297 
00298   if (sw_info->switcher)
00299     {
00300       gdk_property_change (sw_info->switcher,
00301                            sw_info->set_hotkey_atom,
00302                            sw_info->set_hotkey_atom,
00303                            8,
00304                            GDK_PROP_MODE_REPLACE,
00305                            (unsigned char*)hotkey,
00306                            strlen (hotkey));
00307     }
00308   else if (sw_info->switcher_x_window)
00309     {
00310       GdkScreen *screen = im_info_get_screen (info);
00311       GdkDisplay *display = gdk_screen_get_display (screen);
00312       Atom x_atom = gdk_x11_atom_to_xatom_for_display (display,
00313                                sw_info->set_hotkey_atom);
00314 
00315       XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
00316                        sw_info->switcher_x_window,
00317                        x_atom,
00318                        x_atom,
00319                        8,
00320                        PropModeReplace,
00321                        (guchar *)hotkey,
00322                        strlen (hotkey));
00323     }
00324 }
00325 
00326 void
00327 im_context_switcher_set_input_language (GtkIMContextIIIM *context_iiim,
00328                                    gchar *input_lang)
00329 {
00330   GtkIIIMInfo *info = context_iiim->iiim_info;
00331   SwitcherInfo *sw_info = im_info_get_switcher_info (info);
00332 
00333   if (sw_info == NULL)
00334     return;
00335 
00336   if (input_lang == NULL)
00337     input_lang = context_iiim->current_language;
00338   if (sw_info->switcher && input_lang)
00339     {
00340       gdk_property_change (sw_info->switcher, 
00341                         sw_info->set_current_input_language_atom,
00342                         sw_info->set_current_input_language_atom,
00343                         8,
00344                         GDK_PROP_MODE_REPLACE,
00345                         (unsigned char*)input_lang,
00346                         strlen (input_lang));
00347     }
00348   else if (sw_info->switcher_x_window && input_lang)
00349     {
00350       GdkScreen *screen = im_info_get_screen (info);
00351       GdkDisplay *display = gdk_screen_get_display (screen);
00352       Atom x_atom = gdk_x11_atom_to_xatom_for_display (display,
00353                             sw_info->set_current_input_language_atom);
00354 
00355       XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
00356                      sw_info->switcher_x_window,
00357                      x_atom,
00358                      x_atom,
00359                      8,
00360                      PropModeReplace,
00361                      (guchar *)input_lang,
00362                      strlen (input_lang));
00363     }
00364 }
00365 
00366 void
00367 im_context_switcher_set_language_engine_list (GtkIMContextIIIM *context_iiim,
00368                                          gchar *le_list)
00369 {
00370   GtkIIIMInfo *info = context_iiim->iiim_info;
00371   SwitcherInfo *sw_info = im_info_get_switcher_info (info);
00372   gsize len;
00373 
00374   if (sw_info == NULL)
00375     return;
00376 
00377   len = strlen (le_list);
00378 
00379   if (len == 0)
00380     return;
00381 
00382   if (sw_info->switcher)
00383     gdk_property_change (sw_info->switcher, 
00384                       sw_info->set_language_engine_list_atom,
00385                       sw_info->set_language_engine_list_atom,
00386                       8,
00387                       GDK_PROP_MODE_REPLACE,
00388                       (guchar*)le_list, len);
00389   else if (sw_info->switcher_x_window)
00390     {
00391       GdkScreen *screen = im_info_get_screen (info);
00392       GdkDisplay *display = gdk_screen_get_display (screen);
00393       Atom x_atom = gdk_x11_atom_to_xatom_for_display (display,
00394                                                  sw_info->set_language_engine_list_atom);
00395       XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
00396                      sw_info->switcher_x_window,
00397                      x_atom,
00398                      x_atom,
00399                      8,
00400                      PropModeReplace,
00401                      (guchar *)le_list, len);
00402     }
00403 }
00404 
00405 void
00406 im_context_switcher_set_language_list (GtkIMContextIIIM *context_iiim,
00407                                    IIIMCF_language *lang_list,
00408                                    int n_lang)
00409 {
00410   GtkIIIMInfo *info = context_iiim->iiim_info;
00411   SwitcherInfo *sw_info = im_info_get_switcher_info (info);
00412   gchar *languages;
00413   gchar *ptr;
00414   IIIMF_status st;
00415   char *langid;
00416   gint i;
00417   gsize len;
00418   const char *separator = ";";
00419   gsize separator_len;
00420 
00421   if (sw_info == NULL)
00422     return;
00423 
00424   if (lang_list == NULL || n_lang == 0)
00425     return;
00426 
00427   /* First part, getting length */
00428   st = iiimcf_get_language_id (lang_list[0],
00429                             (const char **) &langid);
00430   if (st != IIIMF_STATUS_SUCCESS)
00431     return;
00432 
00433   separator_len = strlen (separator);
00434   len = strlen (langid);
00435   for (i = 1; i < n_lang; i++)
00436     {
00437       st = iiimcf_get_language_id (lang_list[i],
00438                                (const char **) &langid);
00439       if (st != IIIMF_STATUS_SUCCESS)
00440        continue;
00441       len += strlen (langid);
00442     }
00443   len += separator_len * (i - 1);
00444 
00445   /* Second part, building string */
00446   languages = g_new (gchar, len + 1);
00447 
00448   st = iiimcf_get_language_id (lang_list[0],
00449                             (const char **) &langid);
00450   ptr = g_stpcpy (languages, langid);
00451   for (i = 1; i < n_lang; i++)
00452     {
00453       ptr = g_stpcpy (ptr, separator);
00454       st = iiimcf_get_language_id (lang_list[i],
00455                                (const char **) &langid);
00456       if (st != IIIMF_STATUS_SUCCESS)
00457        continue;
00458       ptr = g_stpcpy (ptr, langid);
00459     }
00460 
00461   if (sw_info->switcher)
00462     gdk_property_change (sw_info->switcher, 
00463                       sw_info->set_input_language_list_atom,
00464                       sw_info->set_input_language_list_atom,
00465                       8,
00466                       GDK_PROP_MODE_REPLACE,
00467                       (guchar*)languages, len);
00468   else if (sw_info->switcher_x_window)
00469     {
00470       GdkScreen *screen = im_info_get_screen (info);
00471       GdkDisplay *display = gdk_screen_get_display (screen);
00472       Atom x_atom = gdk_x11_atom_to_xatom_for_display (display,
00473                                                  sw_info->set_input_language_list_atom);
00474       XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
00475                      sw_info->switcher_x_window,
00476                      x_atom,
00477                      x_atom,
00478                      8,
00479                      PropModeReplace,
00480                      (guchar *)languages, len);
00481     }
00482   g_free (languages);
00483 }
00484 
00485 void
00486 im_context_switcher_new (GtkIMContextIIIM *context_iiim)
00487 {
00488   SwitcherContext *w = g_new0 (SwitcherContext, 1);
00489 
00490   g_return_if_fail (context_iiim != NULL);
00491   g_return_if_fail (context_iiim->iiim_info != NULL);
00492 
00493   w->invisible = gtk_invisible_new ();
00494   gtk_widget_realize (w->invisible);
00495 
00496   gtk_widget_add_events (w->invisible,
00497                       GDK_PROPERTY_CHANGE_MASK | GDK_STRUCTURE_MASK);
00498 
00499   w->destroy_handler_id =
00500     g_signal_connect (G_OBJECT (w->invisible), "destroy",      
00501                     G_CALLBACK (destroy_switcher_window),
00502                     context_iiim);
00503 
00504   w->property_handler_id = 
00505     g_signal_connect (G_OBJECT (w->invisible), "property-notify-event",
00506                     G_CALLBACK (property_notify_switcher_window),
00507                     context_iiim);
00508   context_iiim->switcher_context = w;
00509 }
00510 
00511 gboolean
00512 im_info_switcher_active (GtkIIIMInfo *info)
00513 {
00514   SwitcherInfo *sw_info = im_info_get_switcher_info (info);
00515   return (sw_info && (sw_info->switcher || sw_info->switcher_x_window));
00516 }
00517 
00518 void
00519 im_context_switcher_set_focus (GtkIMContextIIIM *context_iiim)
00520 {
00521   SwitcherContext *w;
00522   GtkIIIMInfo *info = context_iiim->iiim_info;
00523   SwitcherInfo *sw_info;
00524 
00525   if (!im_info_switcher_active (info))
00526     {
00527       im_info_switcher_new (info);
00528       if (!im_info_switcher_active (info))
00529        return;
00530     }
00531 
00532   if (context_iiim->switcher_context == NULL)
00533     im_context_switcher_new (context_iiim);
00534 
00535   w = context_iiim->switcher_context;
00536   sw_info = im_info_get_switcher_info (info);
00537   if (w && w->invisible)
00538     gdk_selection_convert (w->invisible->window,
00539                         sw_info->selection_atom,
00540                         sw_info->set_current_client_atom,
00541                         gtk_get_current_event_time ());
00542 }
00543 
00544 
00545 /* input method switcher */
00546 static
00547 GdkFilterReturn
00548 switcher_owner_filter (GdkXEvent *xev, GdkEvent *event, gpointer data)
00549 {
00550   XEvent *xevent = (GdkXEvent *)xev;
00551   GtkIIIMInfo *info = data;
00552   SwitcherInfo *sw_info = im_info_get_switcher_info (info);
00553 
00554   if (sw_info != NULL)
00555     {
00556       switch (event->type)
00557        {
00558        case SelectionClear:
00559          sw_info->switcher = NULL;
00560          sw_info->switcher_x_window = None;
00561          g_free (sw_info);
00562          im_info_set_switcher_info (info, NULL);
00563          break;
00564        default:
00565          break;
00566        }
00567     }
00568   return GDK_FILTER_CONTINUE;
00569 }
00570 
00571 static gboolean
00572 filter_destroy_event (Display *d, Window w, XEvent *ev, gpointer data)
00573 {
00574   GtkIIIMInfo *info = data;
00575   SwitcherInfo *sw_info = im_info_get_switcher_info (info);
00576 
00577   if (sw_info && sw_info->switcher_x_window == w)
00578     {
00579       sw_info->switcher = NULL;
00580       sw_info->switcher_x_window = None;
00581       g_free (sw_info);
00582       im_info_set_switcher_info (info, NULL);
00583       return TRUE;
00584     }
00585   return FALSE;
00586 }
00587 
00588 void
00589 im_info_switcher_shutdown (GtkIIIMInfo *info)
00590 {
00591   SwitcherInfo *sw_info = im_info_get_switcher_info (info);
00592   if (sw_info)
00593     {
00594       if (sw_info->switcher)
00595        gdk_window_remove_filter (sw_info->switcher,
00596                               switcher_owner_filter, info);
00597       else if (sw_info->switcher_x_window)
00598        {
00599          GdkScreen *screen = im_info_get_screen (info);
00600          GdkDisplay *display = gdk_screen_get_display (screen);
00601 
00602          _XUnregisterFilter (GDK_DISPLAY_XDISPLAY (display), 
00603                            sw_info->switcher_x_window,
00604                            filter_destroy_event, info);
00605        }
00606     }
00607 }
00608 
00609 void
00610 im_context_switcher_finalize (GtkIMContextIIIM *context_iiim)
00611 {
00612   SwitcherContext *w = context_iiim->switcher_context;
00613 
00614   if (w == NULL)
00615     return;
00616   g_signal_handler_disconnect (G_OBJECT (w->invisible), w->destroy_handler_id);
00617   g_signal_handler_disconnect (G_OBJECT (w->invisible), w->property_handler_id);
00618   gtk_widget_destroy (w->invisible);
00619   g_free (w);
00620   context_iiim->switcher_context = NULL;
00621   return;
00622 }
00623 
00624 gchar *
00625 im_context_switcher_get_hotkey_with_type (char *string,
00626                                      char *type)
00627 {
00628   char *label_delimiter = ":";
00629   char **hotkeys = g_strsplit (string, label_delimiter, -1);
00630   int num_hotkeys = 0, i;
00631   char *keys = NULL;
00632 
00633   // num_hotkeys = g_strv_length (hotkeys) / 2;
00634   while (hotkeys [num_hotkeys]) ++num_hotkeys;
00635   num_hotkeys /= 2;
00636 
00637   for (i = 0; i < num_hotkeys; ++i)
00638     {
00639       if (!strcasecmp (hotkeys[i * 2], type))
00640        {
00641          keys = strdup (hotkeys[i * 2 + 1]);
00642          break;
00643        }
00644     }
00645 
00646   g_strfreev (hotkeys);
00647 
00648   return keys;
00649 }
00650 
00651 gchar *
00652 im_context_switcher_get_hotkey (GtkIMContextIIIM *context_iiim)
00653 {
00654   int num_hotkey = 0, i;
00655   IIIMCF_hotkey *hotkeys;
00656   char s[512], *p = s;
00657 
00658   memset (s, 0, 512);
00659 
00660   iiimcf_get_hotkeys (context_iiim->context, &num_hotkey, &hotkeys);
00661 
00662   if (num_hotkey == 0) return NULL;
00663 
00664   for (i = 0; i < num_hotkey; ++i)
00665     {
00666       char label_delimiter = ':';
00667       char key_delimiter = ',';
00668       int k;
00669 
00670       strcpy (p, hotkeys[i].hotkey_label);
00671       p += strlen (hotkeys[i].hotkey_label);
00672       *p++ = label_delimiter;
00673 
00674       for (k = 0; k < hotkeys[i].nkeys; ++k)
00675        {
00676          IIIMCF_keyevent *key = hotkeys[i].keys + k;
00677          GdkEventKey event;
00678          char *keyname;
00679 
00680          if (k) *p++ = key_delimiter;
00681 
00682          if (key->modifier & IIIMF_CONTROL_MODIFIER)
00683            {
00684              strcpy (p, "Ctrl+");
00685              p += strlen ("Ctrl+");
00686            }
00687          if (key->modifier & IIIMF_SHIFT_MODIFIER)
00688            {
00689              strcpy (p, "Shift+");
00690              p += strlen ("Shift+");
00691            }
00692          if (key->modifier & IIIMF_ALT_MODIFIER)
00693            {
00694              strcpy (p, "Alt+");
00695              p += strlen ("Alt+");
00696            }
00697 
00698          convert_IIIMCF_keyevent_to_GdkEventKey (key, &event);
00699          keyname = gdk_keyval_name (event.keyval);
00700 
00701          if (keyname)
00702            {
00703              strcpy (p, keyname);
00704              p += strlen (keyname);
00705            }
00706        }
00707 
00708       if (i < num_hotkey - 1) *p++ = label_delimiter;
00709     }
00710 
00711   return strdup (s);
00712 }
00713 
00714 static void
00715 change_hotkey_with_type (GtkIMContextIIIM *context_iiim,
00716                       char *type, int num_keys,
00717                       IIIMCF_keyevent *keys)
00718 {
00719   int num_hotkey = 0, i;
00720   IIIMCF_hotkey *hotkeys = NULL;
00721   IIIMCF_handle handle = im_info_get_handle (context_iiim->iiim_info);
00722 
00723   iiimcf_get_hotkeys (context_iiim->context, &num_hotkey, &hotkeys);
00724 
00725   if (num_hotkey == 0) return;
00726 
00727   for (i = 0; i < num_hotkey; ++i)
00728     {
00729       if (!strcasecmp (type, hotkeys[i].hotkey_label))
00730        {
00731          if (hotkeys[i].nkeys < num_keys)
00732            {
00733              g_free (hotkeys[i].keys);
00734              hotkeys[i].keys = g_new0 (IIIMCF_keyevent, num_keys);
00735            }
00736 
00737          hotkeys[i].nkeys = num_keys;
00738          memcpy (hotkeys[i].keys, keys, sizeof (IIIMCF_keyevent) * num_keys);
00739        }
00740     }
00741 
00742   /* change the onkeys and offkeys if the target type is TRIGGER_KEY_LABEL */
00743   if ( handle && !strcasecmp (type, TRIGGER_KEY_LABEL))
00744     {
00745       int num_on_keys = 0, num_off_keys = 0;
00746       static int initial_num_on_keys = 0, initial_num_off_keys = 0;
00747       IIIMCF_keyevent *onkeys, *offkeys;
00748 
00749       iiimcf_get_trigger_keys (handle, &num_on_keys, &onkeys,
00750                             &num_off_keys, &offkeys);
00751 
00752       if (!initial_num_on_keys && !initial_num_off_keys) 
00753        {
00754          initial_num_on_keys = num_on_keys;
00755          initial_num_off_keys = num_off_keys;
00756        }
00757       
00758       num_keys = initial_num_on_keys < num_keys ? initial_num_on_keys : num_keys;
00759       memcpy (onkeys, keys, sizeof (IIIMCF_keyevent) * num_keys);
00760       memset (onkeys + num_keys, 0, sizeof (IIIMCF_keyevent) * (initial_num_on_keys - num_keys));
00761 
00762       num_keys = initial_num_off_keys < num_keys ? initial_num_off_keys : num_keys;
00763       memcpy (offkeys, keys, sizeof (IIIMCF_keyevent) * num_keys);
00764       memset (offkeys + num_keys, 0, sizeof (IIIMCF_keyevent) * (initial_num_off_keys - num_keys));
00765     }
00766 }
00767 
00768 /* 
00769  * The triggerkey information is set by gimlet with set_hotkey_atom, it is gimlet's responsibility
00770  * to validate its content before change the atom's content.
00771  * Usually the format of hotkey would take "Ctrl+space,Kanji,Shift+Kanji".
00772  */
00773 static void
00774 convert_hotkey_to_IIIMCF_keyevent (gchar *triggerkey,
00775                                int *num_keys, 
00776                                IIIMCF_keyevent **keys)
00777 {
00778   char *key_delimiter = ",";
00779   char **hotkeys = g_strsplit (triggerkey, key_delimiter, -1);
00780   int i;
00781 
00782   if (hotkeys == NULL) return;
00783 
00784   // *num_keys = g_strv_length (hotkeys);
00785   *num_keys = 0;
00786   while (hotkeys[*num_keys]) ++ *num_keys;
00787 
00788   *keys = g_new0 (IIIMCF_keyevent, *num_keys);
00789 
00790   for (i=0; i < *num_keys; ++i)
00791     {
00792       char *key_separator = "+";
00793       gchar **k, **keys_text = g_strsplit (hotkeys[i], key_separator, -1);
00794 
00795       k = keys_text;
00796       for (; *k; ++k)
00797        {
00798          if (!strcasecmp (*k, "Ctrl"))
00799            (*keys)[i].modifier |= IIIMF_CONTROL_MODIFIER;
00800          else if (!strcasecmp (*k, "Shift"))
00801            (*keys)[i].modifier |= IIIMF_SHIFT_MODIFIER;
00802          else if (!strcasecmp (*k, "Alt"))
00803            (*keys)[i].modifier |= IIIMF_ALT_MODIFIER;
00804          else
00805            {
00806              guint keyval = gdk_keyval_from_name (*k);
00807              gint keycode = g2icode (keyval);
00808 
00809              if (keycode < 0)
00810               {
00811                 (*keys)[i].keychar = 0;
00812                 (*keys)[i].keycode = -keycode;
00813               }
00814              else
00815               {
00816                 (*keys)[i].keychar = gdk_keyval_to_unicode (keyval);
00817                 (*keys)[i].keycode = keycode;
00818               }
00819            }
00820        }
00821 
00822       g_strfreev (keys_text);
00823     }
00824 
00825   g_strfreev (hotkeys);
00826 }
00827 
00828 void
00829 im_context_switcher_change_hotkey_with_type (GtkIMContextIIIM *context_iiim,
00830                                         gchar *type,
00831                                         gchar *string)
00832 {
00833   int num_keys;
00834   IIIMCF_keyevent *keys;
00835 
00836   convert_hotkey_to_IIIMCF_keyevent (string, &num_keys, &keys);
00837   
00838   change_hotkey_with_type (context_iiim, type, num_keys, keys);
00839 
00840   g_free (keys);
00841 }
00842 
00843 char *
00844 im_context_switcher_get_hotkey_with_atom (GtkIMContextIIIM *context_iiim)
00845 {
00846   SwitcherInfo *sw_info = im_info_get_switcher_info (context_iiim->iiim_info);
00847   GdkScreen *screen;
00848   GdkDisplay *display;
00849   Atom x_atom;
00850   int format;
00851   unsigned long length, nitem;
00852   unsigned char *data = NULL;
00853   Atom type;
00854 
00855   if (!im_info_switcher_active (context_iiim->iiim_info))
00856     return NULL;
00857 
00858   screen = im_info_get_screen (context_iiim->iiim_info);
00859   display = gdk_screen_get_display (screen);
00860   x_atom = gdk_x11_atom_to_xatom_for_display (display, sw_info->set_hotkey_atom);
00861 
00862   XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
00863                     sw_info->switcher_x_window,
00864                     x_atom,
00865                     0, INT_MAX, False, x_atom,
00866                     &type, &format, &nitem, &length, &data);
00867 
00868   return data;
00869 }