(take_action_list): Move the handling of candidate-list
[m17n/m17n-lib.git] / src / input.c
index 6fbd21d..2de5547 100644 (file)
@@ -1,5 +1,5 @@
 /* input.c -- input method module.
-   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
      National Institute of Advanced Industrial Science and Technology (AIST)
      Registration Number H15PRO112
 
@@ -220,6 +220,7 @@ typedef MPlist *(*MIMExternalFunc) (MPlist *plist);
 
 typedef struct
 {
+  MSymbol name;
   void *handle;
   MPlist *func_list;           /* function name vs (MIMExternalFunc *) */
 } MIMExternalModule;
@@ -269,7 +270,7 @@ static int update_custom_info (void);
 static MInputMethodInfo *get_im_info (MSymbol, MSymbol, MSymbol, MSymbol);
 
 \f
-void
+static void
 fully_initialize ()
 {
   char *key_names[32]
@@ -401,7 +402,7 @@ fully_initialize ()
        msymbol_put (alias[j], M_key_alias, alias[j + 1]);
     }
 
-  buf3[0] = 255;
+  buf3[0] = (char) 255;
   alias[0] = alias[3] = msymbol (buf3);
   alias[1] = one_char_symbol[255] = msymbol ("M-Delete");
   alias[2] = msymbol ("A-Delete");
@@ -1200,13 +1201,17 @@ load_macros (MInputMethodInfo *im_info, MPlist *plist)
   return 0;
 }
 
-/* Load an external module from PLIST into IM_INFO->externals.
+/* Load an external module from PLIST, and return a pointer to
+   MIMExternalModule.
+
    PLIST has this form:
       PLIST ::= ( MODULE-NAME FUNCTION * )
-   IM_INFO->externals is a plist of MODULE-NAME vs (MIMExternalModule *).  */
+   IM_INFO->externals is a plist of MODULE-NAME vs (MIMExternalModule *).
 
-static int
-load_external_module (MInputMethodInfo *im_info, MPlist *plist)
+   On error, return NULL.  */
+
+static MIMExternalModule *
+load_external_module (MPlist *plist)
 {
   void *handle;
   MSymbol module;
@@ -1227,10 +1232,7 @@ load_external_module (MInputMethodInfo *im_info, MPlist *plist)
 
   handle = dlopen (module_file, RTLD_NOW);
   if (MFAILP (handle))
-    {
-      fprintf (stderr, "%s\n", dlerror ());
-      return -1;
-    }
+    return NULL;
   func_list = mplist ();
   MPLIST_DO (plist, MPLIST_NEXT (plist))
     {
@@ -1243,15 +1245,23 @@ load_external_module (MInputMethodInfo *im_info, MPlist *plist)
     }
 
   MSTRUCT_MALLOC (external, MERROR_IM);
+  external->name = module;
   external->handle = handle;
   external->func_list = func_list;
-  mplist_add (im_info->externals, module, external);
-  return 0;
+  return external;
 
  err_label:
-  dlclose (handle);
   M17N_OBJECT_UNREF (func_list);
-  return -1;
+  dlclose (handle);
+  return NULL;
+}
+
+static void
+unload_external_module (MIMExternalModule *external)
+{
+  dlclose (external->handle);
+  M17N_OBJECT_UNREF (external->func_list);
+  free (external);
 }
 
 static void
@@ -1380,11 +1390,7 @@ fini_im_info (MInputMethodInfo *im_info)
     {
       MPLIST_DO (plist, im_info->externals)
        {
-         MIMExternalModule *external = MPLIST_VAL (plist);
-
-         dlclose (external->handle);
-         M17N_OBJECT_UNREF (external->func_list);
-         free (external);
+         unload_external_module (MPLIST_VAL (plist));
          MPLIST_KEY (plist) = Mt;
        }
       M17N_OBJECT_UNREF (im_info->externals);
@@ -1566,7 +1572,7 @@ update_global_info (void)
 }
 
 
-/* Return an IM_INFO for the an method specified by LANGUAGE, NAME,
+/* Return an IM_INFO for the input method specified by LANGUAGE, NAME,
    and EXTRA.  KEY, if not Mnil, tells which kind of information about
    the input method is necessary, and the returned IM_INFO may contain
    only that information.  */
@@ -2290,9 +2296,13 @@ load_im_info (MPlist *plist, MInputMethodInfo *im_info)
              im_info->externals = mplist ();
            MPLIST_DO (elt, MPLIST_NEXT (elt))
              {
+               MIMExternalModule *external;
+
                if (MFAILP (MPLIST_PLIST_P (elt)))
                  continue;
-               load_external_module (im_info, MPLIST_PLIST (elt));
+               external = load_external_module (MPLIST_PLIST (elt));
+               if (external)
+                 mplist_add (im_info->externals, external->name, external);
              }
          }
        else if (key == Mstate)
@@ -2395,6 +2405,10 @@ load_im_info (MPlist *plist, MInputMethodInfo *im_info)
 static int take_action_list (MInputContext *ic, MPlist *action_list);
 static void preedit_commit (MInputContext *ic, int need_prefix);
 
+/* Shift to the state of name STATE_NAME.  If STATE_NAME is `t', shift
+   to the previous state (if any).  If STATE_NAME is `nil', shift to
+   the initial state.  */
+
 static void
 shift_state (MInputContext *ic, MSymbol state_name)
 {
@@ -2441,7 +2455,7 @@ shift_state (MInputContext *ic, MSymbol state_name)
     preedit_commit (ic, 0);
   mtext_cpy (ic_info->preedit_saved, ic->preedit);
   ic_info->state_pos = ic->cursor_pos;
-  if (state != orig_state)
+  if (state != orig_state || state_name == Mnil)
     {
       if (state == (MIMState *) MPLIST_VAL (im_info->states))
        {
@@ -2458,13 +2472,7 @@ shift_state (MInputContext *ic, MSymbol state_name)
       else
        ic->status = im_info->title;
       ic->status_changed = 1;
-      if (ic_info->map == ic_info->state->map
-         && ic_info->map->map_actions)
-       {
-         MDEBUG_PRINT1 ("  [IM] [%s] init-actions:",
-                        MSYMBOL_NAME (state->name));
-         take_action_list (ic, ic_info->map->map_actions);
-       }
+      ic_info->state_hook = ic_info->map->map_actions;
     }
 }
 
@@ -2996,9 +3004,6 @@ static int
 take_action_list (MInputContext *ic, MPlist *action_list)
 {
   MInputContextInfo *ic_info = (MInputContextInfo *) ic->info;
-  MPlist *candidate_list = ic->candidate_list;
-  int candidate_index = ic->candidate_index;
-  int candidate_show = ic->candidate_show;
   MTextProperty *prop;
 
   MPLIST_DO (action_list, action_list)
@@ -3472,31 +3477,6 @@ take_action_list (MInputContext *ic, MPlist *action_list)
            };
        }
     }
-
-  if (ic->candidate_list)
-    {
-      M17N_OBJECT_UNREF (ic->candidate_list);
-      ic->candidate_list = NULL;
-    }
-  if (ic->cursor_pos > 0
-      && (prop = mtext_get_property (ic->preedit, ic->cursor_pos - 1,
-                                    Mcandidate_list)))
-    {
-      ic->candidate_list = mtext_property_value (prop);
-      M17N_OBJECT_REF (ic->candidate_list);
-      ic->candidate_index
-       = (int) mtext_get_prop (ic->preedit, ic->cursor_pos - 1,
-                               Mcandidate_index);
-      ic->candidate_from = mtext_property_start (prop);
-      ic->candidate_to = mtext_property_end (prop);
-    }
-
-  if (candidate_list != ic->candidate_list)
-    ic->candidates_changed |= MINPUT_CANDIDATES_LIST_CHANGED;
-  if (candidate_index != ic->candidate_index)
-    ic->candidates_changed |= MINPUT_CANDIDATES_INDEX_CHANGED;
-  if (candidate_show != ic->candidate_show)
-    ic->candidates_changed |= MINPUT_CANDIDATES_SHOW_CHANGED;    
   return 0;
 }
 
@@ -3516,6 +3496,14 @@ handle_key (MInputContext *ic)
   MSymbol alias = Mnil;
   int i;
 
+  if (ic_info->state_hook)
+    {
+      MDEBUG_PRINT1 ("  [IM] [%s] init-actions:",
+                    MSYMBOL_NAME (ic_info->state->name));
+      take_action_list (ic, ic_info->state_hook);
+      ic_info->state_hook = NULL;
+    }
+
   MDEBUG_PRINT2 ("  [IM] [%s] handle `%s'", 
                 MSYMBOL_NAME (ic_info->state->name), msymbol_name (key));
 
@@ -3606,6 +3594,7 @@ handle_key (MInputContext *ic)
              && ic_info->key_head < ic_info->used)
            {
              MDEBUG_PRINT (" unhandled\n");
+             ic_info->state_hook = map->map_actions;
              return -1;
            }
 
@@ -3788,7 +3777,7 @@ open_im (MInputMethod *im)
 {
   MInputMethodInfo *im_info = get_im_info (im->language, im->name, Mnil, Mnil);
 
-  if (! im_info || ! im_info->states)
+  if (! im_info || ! im_info->states || MPLIST_LENGTH (im_info->states) == 0)
     MERROR (MERROR_IM, -1);
   im->info = im_info;
 
@@ -3897,7 +3886,37 @@ filter (MInputContext *ic, MSymbol key, void *arg)
   ic_info->key_unhandled = 0;
 
   do {
-    if (handle_key (ic) < 0)
+    MPlist *candidate_list = ic->candidate_list;
+    int candidate_index = ic->candidate_index;
+    int candidate_show = ic->candidate_show;
+    MTextProperty *prop;
+    int result = handle_key (ic);
+
+    if (ic->candidate_list)
+      {
+       M17N_OBJECT_UNREF (ic->candidate_list);
+       ic->candidate_list = NULL;
+      }
+    if (ic->cursor_pos > 0
+       && (prop = mtext_get_property (ic->preedit, ic->cursor_pos - 1,
+                                      Mcandidate_list)))
+      {
+       ic->candidate_list = mtext_property_value (prop);
+       M17N_OBJECT_REF (ic->candidate_list);
+       ic->candidate_index
+         = (int) mtext_get_prop (ic->preedit, ic->cursor_pos - 1,
+                                 Mcandidate_index);
+       ic->candidate_from = mtext_property_start (prop);
+       ic->candidate_to = mtext_property_end (prop);
+      }
+    if (candidate_list != ic->candidate_list)
+      ic->candidates_changed |= MINPUT_CANDIDATES_LIST_CHANGED;
+    if (candidate_index != ic->candidate_index)
+      ic->candidates_changed |= MINPUT_CANDIDATES_INDEX_CHANGED;
+    if (candidate_show != ic->candidate_show)
+      ic->candidates_changed |= MINPUT_CANDIDATES_SHOW_CHANGED;    
+
+    if (result < 0)
       {
        /* KEY was not handled.  Delete it from the current key sequence.  */
        if (ic_info->used > 0)
@@ -4004,24 +4023,24 @@ dump_im_map (MPlist *map_list, int indent)
   memset (prefix, 32, indent);
   prefix[indent] = '\0';
 
-  fprintf (stderr, "(\"%s\" ", msymbol_name (key));
+  fprintf (mdebug__output, "(\"%s\" ", msymbol_name (key));
   if (map->map_actions)
     mdebug_dump_plist (map->map_actions, indent + 2);
   if (map->submaps)
     {
       MPLIST_DO (map_list, map->submaps)
        {
-         fprintf (stderr, "\n%s  ", prefix);
+         fprintf (mdebug__output, "\n%s  ", prefix);
          dump_im_map (map_list, indent + 2);
        }
     }
   if (map->branch_actions)
     {
-      fprintf (stderr, "\n%s  (branch\n%s    ", prefix, prefix);
+      fprintf (mdebug__output, "\n%s  (branch\n%s    ", prefix, prefix);
       mdebug_dump_plist (map->branch_actions, indent + 4);
-      fprintf (stderr, ")");      
+      fprintf (mdebug__output, ")");      
     }
-  fprintf (stderr, ")");
+  fprintf (mdebug__output, ")");
 }
 
 
@@ -4035,16 +4054,16 @@ dump_im_state (MIMState *state, int indent)
   memset (prefix, 32, indent);
   prefix[indent] = '\0';
 
-  fprintf (stderr, "(%s", msymbol_name (state->name));
+  fprintf (mdebug__output, "(%s", msymbol_name (state->name));
   if (state->map->submaps)
     {
       MPLIST_DO (map_list, state->map->submaps)
        {
-         fprintf (stderr, "\n%s  ", prefix);
+         fprintf (mdebug__output, "\n%s  ", prefix);
          dump_im_map (map_list, indent + 2);
        }
     }
-  fprintf (stderr, ")");
+  fprintf (mdebug__output, ")");
 }
 
 \f
@@ -5916,6 +5935,153 @@ minput_save_config (void)
   return (ret < 0 ? -1 : 1);
 }
 
+/***en
+    @brief List available input methods.
+
+    The minput_list () function returns a list of currently available
+    input methods whose language is $LANGUAGE.  If $LANGUAGE is #Mnil,
+    all input methods are listed.
+
+    @return
+    The returned value is a plist of this form:
+       ((LANGUAGE-NAME INPUT-METHOD-NAME SANE) ...)
+    The third element SANE of each input method is #Mt if it can be
+    successfully used, or #Mnil if it has some problem (e.g. syntax
+    error of MIM file, unavailable external module, unavailable
+    including input method).  */
+
+#if EXAMPLE_CODE
+#include <stdio.h>
+#include <string.h>
+#include <m17n.h>
+
+int
+main (int argc, char **argv)
+{
+  MPlist *imlist, *pl;
+
+  M17N_INIT ();
+  imlist = minput_list ((argc > 1) ? msymbol (argv[1]) : Mnil);
+  for (pl = imlist; mplist_key (pl) != Mnil; pl = mplist_next (pl))
+    {
+      MPlist *p = mplist_value (pl);
+      MSymbol lang, name, sane;
+
+      lang = mplist_value (p);
+      p = mplist_next (p);
+      name = mplist_value (p);
+      p = mplist_next (p);
+      sane = mplist_value (p);
+
+      printf ("%s %s %s\n", msymbol_name (lang), msymbol_name (name),
+             sane == Mt ? "ok" : "no");
+    }
+
+  m17n_object_unref (imlist);
+  M17N_FINI ();
+  exit (0);
+}
+#endif
+
+MPlist *
+minput_list (MSymbol language)
+{
+  MPlist *plist, *pl;
+  MPlist *imlist = mplist ();
+  
+  MINPUT__INIT ();
+  plist = mdatabase_list (Minput_method, language, Mnil, Mnil);
+  if (! plist)
+    return imlist;
+  MPLIST_DO (pl, plist)
+    {
+      MDatabase *mdb = MPLIST_VAL (pl);
+      MSymbol *tag = mdatabase_tag (mdb);
+      MPlist *imdata, *p, *elm;
+      int num_maps = 0, num_states = 0;
+
+      if (tag[2] == Mnil)
+       continue;
+      imdata = mdatabase_load (mdb);
+      if (! imdata)
+       continue;
+      MPLIST_DO (p, imdata)
+       if (MPLIST_PLIST_P (p))
+         {
+           /* Check these basic functionarity:
+              All external modules (if any) are loadable.
+              All included input method (if any) are loadable.
+              At least one map is defined or included.
+              At least one state is defined or included. */
+           MPlist *elt = MPLIST_PLIST (p);
+           MSymbol key;
+
+           if (MFAILP (MPLIST_SYMBOL_P (elt)))
+             break;
+           key = MPLIST_SYMBOL (elt);
+           if (key == Mmap)
+             num_maps++;
+           else if (key == Mstate)
+             num_states++;
+           else if (key == Mmodule)
+             {
+               MPLIST_DO (elt, MPLIST_NEXT (elt))
+                 {
+                   MIMExternalModule *external;
+
+                   if (MFAILP (MPLIST_PLIST_P (elt)))
+                     break;
+                   external = load_external_module (MPLIST_PLIST (elt));
+                   if (MFAILP (external))
+                     break;
+                   unload_external_module (external);
+                 }
+               if (! MPLIST_TAIL_P (elt))
+                 break;
+             }
+           else if (key == Minclude)
+             {
+               MInputMethodInfo *im_info;
+
+               elt = MPLIST_NEXT (elt);
+               if (MFAILP (MPLIST_PLIST_P (elt)))
+                 break;
+               im_info = get_im_info_by_tags (MPLIST_PLIST (elt));
+               if (MFAILP (im_info))
+                 break;
+               elt = MPLIST_NEXT (elt);
+               if (MFAILP (MPLIST_SYMBOL_P (elt)))
+                 break;
+               key = MPLIST_SYMBOL (elt);
+               if (key == Mmap)
+                 {
+                   if (! im_info->maps)
+                     break;
+                   num_maps++;
+                 }
+               else if (key == Mstate)
+                 {
+                   if (! im_info->states)
+                     break;
+                   num_states++;
+                 }
+             }
+         }
+      elm = mplist ();
+      mplist_add (elm, Msymbol, tag[1]);
+      mplist_add (elm, Msymbol, tag[2]);
+      if (MPLIST_TAIL_P (p) && num_maps > 0 && num_states > 0)
+       mplist_add (elm, Msymbol, Mt);
+      else
+       mplist_add (elm, Msymbol, Mnil);
+      mplist_push (imlist, Mplist, elm);
+      M17N_OBJECT_UNREF (elm);
+      M17N_OBJECT_UNREF (imdata);
+    }
+  M17N_OBJECT_UNREF (plist);
+  return imlist;
+}
+
 /*=*/
 /*** @} */
 /*=*/
@@ -6346,16 +6512,18 @@ minput_callback (MInputContext *ic, MSymbol command)
     @brief Dump an input method.
 
     The mdebug_dump_im () function prints the input method $IM in a
-    human readable way to the stderr.  $INDENT specifies how many
-    columns to indent the lines but the first one.
+    human readable way to the stderr or to what specified by the
+    environment variable MDEBUG_OUTPUT_FILE.  $INDENT specifies how
+    many columns to indent the lines but the first one.
 
     @return
     This function returns $IM.  */
 /***ja
     @brief ÆþÎϥ᥽¥Ã¥É¤ò¥À¥ó¥×¤¹¤ë.
 
-    ´Ø¿ô mdebug_dump_im () ¤ÏÆþÎϥ᥽¥Ã¥É $IM ¤ò stderr 
-    ¤Ë¿Í´Ö¤Ë²ÄÆɤʷÁ¤Ç°õºþ¤¹¤ë¡£$INDENT ¤Ï£²¹ÔÌܰʹߤΥ¤¥ó¥Ç¥ó¥È¤ò»ØÄꤹ¤ë¡£
+    ´Ø¿ô mdebug_dump_im () ¤ÏÆþÎϥ᥽¥Ã¥É $IM ¤òɸ½à¥¨¥é¡¼½ÐÎϤ⤷¤¯¤Ï
+    ´Ä¶­ÊÑ¿ô MDEBUG_DUMP_FONT ¤Ç»ØÄꤵ¤ì¤¿¥Õ¥¡¥¤¥ë¤Ë¿Í´Ö¤Ë²ÄÆɤʷÁ¤Ç½Ð
+    ÎϤ¹¤ë¡£$INDENT ¤Ï£²¹ÔÌܰʹߤΥ¤¥ó¥Ç¥ó¥È¤ò»ØÄꤹ¤ë¡£
 
     @return
     ¤³¤Î´Ø¿ô¤Ï $IM ¤òÊÖ¤¹¡£  */
@@ -6370,7 +6538,7 @@ mdebug_dump_im (MInputMethod *im, int indent)
   memset (prefix, 32, indent);
   prefix[indent] = '\0';
 
-  fprintf (stderr, "(input-method %s %s ", msymbol_name (im->language),
+  fprintf (mdebug__output, "(input-method %s %s ", msymbol_name (im->language),
           msymbol_name (im->name));
   mdebug_dump_mtext (im_info->title, 0, 0);
   if (im->name != Mnil)
@@ -6379,11 +6547,11 @@ mdebug_dump_im (MInputMethod *im, int indent)
 
       MPLIST_DO (state, im_info->states)
        {
-         fprintf (stderr, "\n%s  ", prefix);
+         fprintf (mdebug__output, "\n%s  ", prefix);
          dump_im_state (MPLIST_VAL (state), indent + 2);
        }
     }
-  fprintf (stderr, ")");
+  fprintf (mdebug__output, ")");
   return im;
 }