[openi18n-im:01432] patch to provide IME switching capability via gimlet

Federic Zhang <[email protected]> Fri, 24 Jun 2005 00:33:09 +0800
Newsgroups gmane.comp.internationalization.input-methods
Message-ID <[email protected]>
Hi,

Please review and give comment.

Making use of the exported IME info with the previous patch, client
gathers the list of IMEs which are enabled and sends to gimlet with 
the existing mechanism by modifying the le_list, the modified le_list 
would be 'lang:lename-imename1,imename2;lang2:lename2'. For better
reference, the original one is 'lang:lename;lang2:lename2'.

The IME list is appended to the lename, which is separated by ','.
gimlet interprets the list and create corresponding ime menuitems under
language submenu. Upon user's selection on specific IME, the lename
client receives is 'lang:lename-imename', then it will make new global
API, iiimcf_set_input_method_name, to update the input method name to 
'lename-imename', as the content of IIIMP_ICATTRIBUTE_INPUT_METHOD_NAME
protocol, as a result, server can get the IME name without modifying the
underline protocol. In this way, the IME switching by LE can be achieved
with the minor effort.

Please note that there still have some problems in gimlet side if more
than LE exists for one language, i will correct them later.

-federic
iiim-gimlet-imelist.diff (text/x-patch, 13.3 KB)
Index: iiimsf/src/LE.cpp
===================================================================
--- iiimsf/src/LE.cpp	(revision 2714)
+++ iiimsf/src/LE.cpp	(working copy)
@@ -507,8 +507,15 @@
 )
 {
     IMArg arg;
+    const ICAttribute &attr = pic->get_icattr();
+    const u16string *preq_imename = &attr.get_inputengine();
+
     bind_imlexec(pimlex);
 
+    /* let LE to switch to current input method engine */
+    IMSetArg (arg, SC_CLIENT_INPUT_METHOD_ENGINE, preq_imename->get_string()->c_str ()); /* FIXME */
+    set_values (&arg, 1);
+
     if (flag)
 	IMSetArg(arg, SC_TRIGGER_ON_NOTIFY, 0);
     else
Index: iiimsf/src/IIIMPUtil.cpp
===================================================================
--- iiimsf/src/IIIMPUtil.cpp	(revision 2714)
+++ iiimsf/src/IIIMPUtil.cpp	(working copy)
@@ -154,7 +154,32 @@
 	  }
 	  break;
 	  case IIIMP_ICATTRIBUTE_INPUT_METHOD_NAME:
-	   icattr.set_inputmethod(CONV_IIIMP_STR(pattr->value.input_method_name));
+	  {
+	    if (pattr->value.input_method_name)
+	      {
+		IIIMP_card16 *imename = pattr->value.input_method_name->ptr;
+		IIIMP_card16 *lename, *p;
+
+		/* check whether the name of input method engine is included,
+		 * set it to inputengine if yes.
+		 */
+		p = lename = imename;
+		while (*p && *p != '-') ++p;
+		if (*p == '-')
+		  {
+		    *p++ = 0x0;
+		    imename = p;
+		  }
+		else
+		    imename = NULL;
+
+	   	if (imename) icattr.set_inputengine (u16string (imename));
+
+		icattr.set_inputmethod (u16string (lename));
+	      }
+	     else
+	       icattr.set_inputmethod(CONV_IIIMP_STR(pattr->value.input_method_name));
+	   }
 	   break;
 #if 0
 	  case IIIMP_ICATTRIBUTE_INPUT_METHOD:
Index: iiimsf/src/ICHandler.hh
===================================================================
--- iiimsf/src/ICHandler.hh	(revision 2714)
+++ iiimsf/src/ICHandler.hh	(working copy)
@@ -22,6 +22,7 @@
   private:
     u16string inputmethod;
     u16string inputlanguage;
+    u16string inputengine;
     IMCharSubsets charsubsets;
 
   public:
@@ -29,6 +30,8 @@
     { inputmethod = imname; }
     void set_inputlanguage(const u16string& langname)
     { inputlanguage = langname; }
+    void set_inputengine(const u16string& imename)
+    { inputengine = imename; }
     void set_charsubsets(const IMCharSubsets &subsets)
     { charsubsets = subsets; }
 
@@ -36,6 +39,7 @@
 
     const u16string& get_inputmethod() const { return inputmethod; }
     const u16string& get_inputlanguage() const { return inputlanguage; }
+    const u16string& get_inputengine() const { return inputengine; }
     IMCharSubsets& get_character_subsets() { return charsubsets; }
 };
 
Index: include/iml/SunIMMthd.h
===================================================================
--- include/iml/SunIMMthd.h	(revision 2714)
+++ include/iml/SunIMMthd.h	(working copy)
@@ -425,7 +425,8 @@
 
 	SC_CLIENT_LOCALE = 200,
 	SC_CLIENT_CHARACTER_SUBSETS,
-	SC_CLIENT_INPUT_METHOD_NAME
+	SC_CLIENT_INPUT_METHOD_NAME,
+	SC_CLIENT_INPUT_METHOD_ENGINE
 }   SC_Attribute;
 
 typedef enum {
Index: include/iiimcf.h
===================================================================
--- include/iiimcf.h	(revision 2714)
+++ include/iiimcf.h	(working copy)
@@ -401,6 +401,11 @@
     int *pinput_method_language_size,
     IIIMCF_language **pplanguages
 );
+extern IIIMF_status iiimcf_set_input_method_name(
+    IIIMCF_input_method input_method,
+    const IIIMP_card16 *input_method_name
+);
+
 extern IIIMF_status iiimcf_get_input_method_imeinfos(
     IIIMCF_input_method input_method,
     int *pinput_method_imeinfo_size,
Index: gnome-im-switcher/src/quick-access-menu.c
===================================================================
--- gnome-im-switcher/src/quick-access-menu.c	(revision 2714)
+++ gnome-im-switcher/src/quick-access-menu.c	(working copy)
@@ -118,13 +118,20 @@
   if (GTK_MENU_ITEM (menuitem))
     {
       gchar *iiim_lang;
-      gchar *lename, *lang_le;
+      gchar *lename, *lang_le, *imename;
       gchar *conversion_mode;
 
       iiim_lang = g_strdup(g_object_get_data (G_OBJECT (menuitem), "iiim-lang-name"));
-      lename = g_strdup(gtk_label_get_text (GTK_LABEL (GTK_BIN (menuitem)->child)));
-      lang_le = g_strconcat(iiim_lang, ":", lename, NULL);
+      /* the actual name of language engine */
+      lename = g_strdup(g_object_get_data (G_OBJECT (menuitem), "iiim-le-name"));
+      /* the name of input method engine in UTF8 */
+      imename = g_strdup(gtk_label_get_text (GTK_LABEL (GTK_BIN (menuitem)->child)));
 
+      if (imename)
+	lang_le = g_strconcat (iiim_lang, ":", lename, "-", imename, NULL);
+      else
+	lang_le = g_strdup (iiim_lang);
+
       gimlet_iiim_language_set (gimlet, lang_le);
 
       free(lang_le);
@@ -157,8 +164,9 @@
   return g_strcasecmp (a, b);
 }
 
+/* FIXME */
 GSList*
-get_iiimf_lang_le_list (GimletWindow *gimlet, gchar *iiim_lang_name)
+get_iiimf_lang_le_list (GimletWindow *gimlet, gchar *iiim_lang_name, gchar **imelist)
 {
   const gchar *im_delimiter = ";";
   gchar **input_method_list = NULL;
@@ -167,6 +175,8 @@
   if (gimlet->le_list == NULL)
     return NULL;
 
+  *imelist = NULL;
+
   input_method_list = g_strsplit (gimlet->le_list, im_delimiter, -1);
 
   if (input_method_list)
@@ -177,10 +187,21 @@
 
       for (p = input_method_list; *p; p++)
 	{
+	  char *pime;
+
 	  le = g_strsplit (*p, le_delimiter, -1);
+
+	  /* probably 'le' consists of lename and imename which are separated by '-' */
+	  pime = le[1];
+	  while (*pime && *pime != '-') ++pime;
+	  if (*pime == '-')  *pime++ = 0x0;
+
 	  if ((strcmp ((gchar*)le[0], iiim_lang_name) == 0) &&
 	      !(g_slist_find_custom (lelist, le[1], (GCompareFunc)string_comp)))
-	      lelist = g_slist_append (lelist, le[1]);
+	     {
+	       lelist = g_slist_append (lelist, le[1]);
+		if (*pime) *imelist = pime;
+	     }
 	}
     }
   g_strfreev (input_method_list);
@@ -210,6 +231,7 @@
       char *name;
       char *mnemonic_label;
       char *iiim_lang_name;
+      char *imelist;
       name = gimlet_language_get_name (tmp->data);
       if (strcmp (name, "ASCII") == 0)
 	continue;
@@ -223,7 +245,8 @@
       else
 	key_navi[0] += 1;
 
-      GSList *lelist = get_iiimf_lang_le_list (gimlet, iiim_lang_name);
+      /* FIXME - it wouldn't be correct in case of multiple LE per lang */
+      GSList *lelist = get_iiimf_lang_le_list (gimlet, iiim_lang_name, &imelist);
       int len = g_slist_length (lelist);
       if (len > 1)
 	{
@@ -250,12 +273,47 @@
 	}
       else if (len == 1)
 	{
-	  g_object_set_data (G_OBJECT (menuitem), "iiim-lang-name",
-			     iiim_lang_name);
-	  g_object_set_data (G_OBJECT (menuitem), "iiim-display-name",
-			     name);
-	  g_signal_connect (GTK_MENU_ITEM (menuitem), "activate",
-			    G_CALLBACK (activate_cb), gimlet);
+	 if (imelist)
+	  {
+	   GtkWidget *le_submenu;
+	   GtkWidget *le_submenu_item;
+	   gchar **imes = g_strsplit (imelist, ",", -1);
+	   gchar **p = imes;
+
+	   le_submenu = gtk_menu_new ();
+	   for (; *p; ++p)
+	    {
+	       /* here 'p' refers to the name of input method engine in UTF8 */
+               le_submenu_item = gtk_menu_item_new_with_label (*p);
+               g_object_set_data (G_OBJECT (le_submenu_item), "iiim-lang-name",
+                                  iiim_lang_name);
+               g_object_set_data (G_OBJECT (le_submenu_item), "iiim-display-name",
+                                  name);
+               g_object_set_data (G_OBJECT (le_submenu_item), "iiim-le-name",
+                                  lelist->data);
+               gtk_menu_shell_append (GTK_MENU_SHELL (le_submenu), le_submenu_item);
+               g_signal_connect (le_submenu_item, "activate",
+                                 G_CALLBACK (activate_cb), gimlet);
+               gtk_widget_show (le_submenu_item);
+	    }
+
+            gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), le_submenu);
+            gtk_widget_show (le_submenu);
+
+             for (; *imes; ++imes)
+               g_free (*imes);
+	  }
+	 else
+	  {
+	     g_object_set_data (G_OBJECT (menuitem), "iiim-lang-name",
+			        iiim_lang_name);
+	     g_object_set_data (G_OBJECT (menuitem), "iiim-display-name",
+			        name);
+             g_object_set_data (G_OBJECT (menuitem), "iiim-le-name",
+                                 NULL);
+	     g_signal_connect (GTK_MENU_ITEM (menuitem), "activate",
+			        G_CALLBACK (activate_cb), gimlet);
+	  }
 	}
 
       g_slist_free (lelist);
Index: lib/iiimcf/input-method.c
===================================================================
--- lib/iiimcf/input-method.c	(revision 2714)
+++ lib/iiimcf/input-method.c	(working copy)
@@ -296,6 +296,20 @@
 }
 
 IIIMF_status
+iiimcf_set_input_method_name (
+    IIIMCF_input_method input_method,
+    const IIIMP_card16* input_method_name
+)
+{
+    IIIMCF_input_method_rec *pi = (IIIMCF_input_method_rec*) input_method;
+
+    if (pi->imname) free (pi->imname);
+    pi->imname = input_method_name;
+
+    return IIIMF_STATUS_SUCCESS;
+}
+
+IIIMF_status
 iiimcf_get_input_method_imeinfos(
     IIIMCF_input_method input_method,
     int *pinput_method_imeinfo_size,
Index: lib/iiimcf/libiiimcf.sym
===================================================================
--- lib/iiimcf/libiiimcf.sym	(revision 2714)
+++ lib/iiimcf/libiiimcf.sym	(working copy)
@@ -27,6 +27,7 @@
 iiimcf_get_input_method_desc
 iiimcf_get_input_method_languages
 iiimcf_get_input_method_imeinfos
+iiimcf_set_input_method_name
 iiimcf_get_downloaded_object_descriptor
 iiimcf_get_downloaded_object_filename
 iiimcf_get_downloaded_objects
Index: iiimgcf/gtkimcontextiiim.c
===================================================================
--- iiimgcf/gtkimcontextiiim.c	(revision 2714)
+++ iiimgcf/gtkimcontextiiim.c	(working copy)
@@ -352,6 +352,48 @@
 	  NULL);
 }
 
+static IIIMP_card16 *
+format_utf8_string (char *utf8str)
+{
+  return (utf8str != NULL ?
+          g_utf8_to_utf16 (utf8str, -1, NULL, NULL, NULL) :
+ 	  NULL);
+}
+
+static char *
+get_imelist (int nimeinfos, IIIMCF_imeinfo_rec **ppimeinfos)
+{
+  int i;
+  char *imelist = NULL, *p;
+
+  imelist = g_new0 (char, 1024);
+
+  p = imelist;
+  for (i = 0; i < nimeinfos; ++i)
+     {
+	char *imename = NULL;
+
+	if (!ppimeinfos[i]->enable) continue;
+	
+	if (i) *p++ = ',';
+
+	imename = format_iiimcf_string (ppimeinfos[i]->imename);
+
+	strcpy (p, imename); /* imename shouldn't be NULL */
+	p += strlen (imename);
+
+	g_free (imename);
+     }
+
+  if (*imelist == 0)
+    {
+	g_free (imelist);
+	imelist = NULL;
+    }
+
+  return imelist;
+}
+
 /*
  * Get IM List
  */
@@ -369,6 +411,9 @@
   gsize separator_len;
   gchar *le_list, *ptr;
   char *lename = NULL;
+  char *imelist;
+  IIIMCF_imeinfo_rec **ppimeinfos;
+  int nimeinfos;
 
   separator_len = strlen (separator);
 
@@ -395,10 +440,27 @@
 
       lename = format_iiimcf_string (u16hrn);
 
+      st = iiimcf_get_input_method_imeinfos (input_methods[i],
+					     &nimeinfos, &ppimeinfos);
+      if (st != IIIMF_STATUS_SUCCESS)
+	goto error;
+
+      /* append the list of IME name to lename */
+      if (nimeinfos && ppimeinfos && (imelist = get_imelist (nimeinfos, ppimeinfos)))
+	{
+	   char *tmp_lename = g_new0 (char, strlen (lename) + strlen (imelist) + 2);
+
+	   sprintf (tmp_lename, "%s-%s", lename, imelist); /* FIXME */
+	   g_free (lename);
+	   lename = tmp_lename;
+	   g_free (imelist);
+	 }
+
       st = iiimcf_get_input_method_languages (input_methods[i],
 					      &nlangs, &plangs);
       if (st != IIIMF_STATUS_SUCCESS)
 	goto error;
+
       for (j = 0; j < nlangs; j++)
 	{
 	  st = iiimcf_get_language_id (plangs[j],
@@ -428,6 +490,22 @@
 					 &u16hrn, &u16domain);
       lename = format_iiimcf_string (u16hrn);
 
+      st = iiimcf_get_input_method_imeinfos (input_methods[i],
+                                              &nimeinfos, &ppimeinfos);
+      if (st != IIIMF_STATUS_SUCCESS)
+        goto error;
+
+      /* append the list of IME name to lename */
+      if (nimeinfos && ppimeinfos && (imelist = get_imelist (nimeinfos, ppimeinfos)))
+        {
+           char *tmp_lename = g_new0 (char, strlen (lename) + strlen (imelist) + 2);
+
+           sprintf (tmp_lename, "%s-%s", lename, imelist);
+           g_free (lename);
+           lename = tmp_lename;
+           g_free (imelist);
+        }
+
       st = iiimcf_get_input_method_languages (input_methods[i],
 					      &nlangs, &plangs);
       if (st != IIIMF_STATUS_SUCCESS)
@@ -2645,6 +2723,10 @@
   if (le_name != NULL)
     {
       char *idname = NULL;
+      /* probably le_name consists of 'lename' and 'imename' which are separated by '-' */
+      char **le_ime = g_strsplit (le_name, "-", -1);
+      char *lename = le_ime[0], *imename = le_ime[1];
+
       st = iiimcf_get_supported_input_methods (iiim, &n, &pims);
       if (st != IIIMF_STATUS_SUCCESS)
 	return;
@@ -2656,8 +2738,15 @@
 	    return;
 
 	  idname = format_iiimcf_string (u16hrn);
-          if (idname != NULL && strcmp (le_name, idname) == 0)
+          if (idname != NULL && strcmp (lename, idname) == 0)
 	    {
+	      /* update the pims[i]->imname with the new imname that contains imename */
+	      if (imename)
+               {
+	      	 IIIMP_card16 *imname = format_utf8_string (le_name);
+	      	 iiimcf_set_input_method_name (pims[i], imname);
+	       }
+
 	      g_free (idname);
 	      found_le = TRUE;
 	      break;
@@ -2667,6 +2756,7 @@
 	    idname = NULL;
 	  }
 	}
+       g_free (le_ime);
     }
   /* save conversion mode to restore this after switching */
   st = iiimcf_get_current_conversion_mode (context_iiim->context,