]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkimcontextsimple.c
Rework the Unicode hex input code. Now we only steal a single key
[~andy/gtk] / gtk / gtkimcontextsimple.c
index 26f0d4dd18165ff65e739adf4376dc4ba6c36311..8c6719e2242a0a0bef6fa22ac3a06259cb29f2f6 100644 (file)
@@ -1032,14 +1032,10 @@ gtk_im_context_simple_commit_char (GtkIMContext *context,
   len = g_unichar_to_utf8 (ch, buf);
   buf[len] = '\0';
 
-  context_simple->in_hex_sequence = FALSE;
-  
-  if (context_simple->tentative_match)
-    {
-      context_simple->tentative_match = 0;
-      context_simple->tentative_match_len = 0;
-      g_signal_emit_by_name (context_simple, "preedit_changed");
-    }
+  context_simple->in_hex_sequence = FALSE;  
+  context_simple->tentative_match = 0;
+  context_simple->tentative_match_len = 0;
+  g_signal_emit_by_name (context_simple, "preedit_changed");
 
   g_signal_emit_by_name (context, "commit", &buf);
 }
@@ -1116,7 +1112,7 @@ check_table (GtkIMContextSimple    *context_simple,
                {
                  context_simple->tentative_match = value;
                  context_simple->tentative_match_len = n_compose;
-
+               
                  g_signal_emit_by_name (context_simple, "preedit_changed");
 
                  return TRUE;
@@ -1134,14 +1130,21 @@ check_table (GtkIMContextSimple    *context_simple,
 }
 
 /* In addition to the table-driven sequences, we allow Unicode hex
- * codes entered with Ctrl-Shift held down as specified in ISO
- * 14755. 14755 actually allows a different set of modifiers to be
- * used at our discretion, but for now using Ctrl-Shift as in their
- * examples. While holding Ctrl-Shift, pressing space commits the
- * character, and pressing a non-hex-digit is an error.
+ * codes to be entered. The method chosen here is similar to the
+ * one recommended in ISO 14755, but not exactly the same, since we
+ * don't want to steal 16 valuable key combinations. 
+ * 
+ * A hex Unicode sequence must be started with Ctrl-Shift-U, followed
+ * by a sequence of hex digits entered with Ctrl-Shift still held.
+ * Releasing one of the modifiers or pressing space while the modifiers
+ * are still held commits the character. It is possible to erase
+ * digits using backspace.
+ *
+ * As an extension to the above, we also allow to start the sequence
+ * with Ctrl-Shift-U, then release the modifiers before typing any
+ * digits, and enter the digits without modifiers.
  */
-
-#define ISO_14755_MOD_MASK (GDK_CONTROL_MASK | GDK_SHIFT_MASK)
+#define HEX_MOD_MASK (GDK_CONTROL_MASK | GDK_SHIFT_MASK)
 
 static gboolean
 check_hex (GtkIMContextSimple *context_simple,
@@ -1153,7 +1156,10 @@ check_hex (GtkIMContextSimple *context_simple,
   gulong n;
   gchar *nptr = NULL;
   gchar buf[7];
-  
+
+  context_simple->tentative_match = 0;
+  context_simple->tentative_match_len = 0;
+
   str = g_string_new (NULL);
   
   i = 0;
@@ -1189,14 +1195,11 @@ check_hex (GtkIMContextSimple *context_simple,
   else
     g_string_free (str, TRUE);
 
-  /* don't allow invalid Unicode or nul bytes */
-  if (n == 0 || !g_unichar_validate (n))
-    return FALSE;
-  
-  context_simple->tentative_match = n;
-  context_simple->tentative_match_len = n_compose;
-  
-  g_signal_emit_by_name (context_simple, "preedit_changed");
+  if (g_unichar_validate (n))
+    {
+      context_simple->tentative_match = n;
+      context_simple->tentative_match_len = n_compose;
+    }
   
   return TRUE;
 }
@@ -1313,22 +1316,43 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
   GSList *tmp_list;  
   int n_compose = 0;
   gboolean have_hex_mods;
+  gboolean is_hex_start;
+  gboolean is_space;
+  gboolean is_backspace;
   guint hex_keyval;
   int i;
 
+  while (context_simple->compose_buffer[n_compose] != 0)
+    n_compose++;
+
   if (event->type == GDK_KEY_RELEASE)
     {
       if (context_simple->in_hex_sequence &&
          (event->keyval == GDK_Control_L || event->keyval == GDK_Control_R ||
           event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R))
        {
-         if (context_simple->tentative_match)
+         if (context_simple->tentative_match &&
+             g_unichar_validate (context_simple->tentative_match))
            {
              gtk_im_context_simple_commit_char (context, context_simple->tentative_match);
              context_simple->compose_buffer[0] = 0;
+
+           }
+         else if (n_compose == 0)
+           {
+             context_simple->modifiers_dropped = TRUE;
            }
          else
-           context_simple->in_hex_sequence = 0;
+           {
+             /* invalid hex sequence */
+             gdk_display_beep (gdk_drawable_get_display (event->window));
+             
+             context_simple->tentative_match = 0;
+             context_simple->in_hex_sequence = FALSE;
+             context_simple->compose_buffer[0] = 0;
+             
+             g_signal_emit_by_name (context_simple, "preedit_changed");
+           }
 
          return TRUE;
        }
@@ -1336,37 +1360,78 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
        return FALSE;
     }
 
-  /* Ignore modifier key presses
-   */
+  /* Ignore modifier key presses */
   for (i=0; i < G_N_ELEMENTS (gtk_compose_ignore); i++)
     if (event->keyval == gtk_compose_ignore[i])
       return FALSE;
 
-  have_hex_mods = (event->state & (ISO_14755_MOD_MASK)) == ISO_14755_MOD_MASK;
+  if (context_simple->in_hex_sequence && context_simple->modifiers_dropped)
+    have_hex_mods = TRUE;
+  else
+    have_hex_mods = (event->state & (HEX_MOD_MASK)) == HEX_MOD_MASK;
+  is_hex_start = event->keyval == GDK_U;
+  is_space = event->keyval == GDK_space || event->keyval == GDK_KP_Space;
+  is_backspace = event->keyval == GDK_BackSpace;
   hex_keyval = canonical_hex_keyval (event);
 
-  while (context_simple->compose_buffer[n_compose] != 0)
-    n_compose++;
-
   /* If we are already in a non-hex sequence, or
-   * this keystroke is not 14755 modifiers + hex digit, don't filter 
+   * this keystroke is not hex modifiers + hex digit, don't filter 
    * key events with accelerator modifiers held down.
    */
-  if ((n_compose > 0 && !context_simple->in_hex_sequence) || !have_hex_mods || !hex_keyval)
+  if (!have_hex_mods ||
+      (n_compose > 0 && !context_simple->in_hex_sequence) || 
+      (n_compose == 0 && !context_simple->in_hex_sequence && !is_hex_start) ||
+      (context_simple->in_hex_sequence && !hex_keyval && !is_space && !is_backspace))
     {
-      if (event->state &
-          (gtk_accelerator_get_default_mod_mask () & ~GDK_SHIFT_MASK))
-        return FALSE;
+      if (event->state & (gtk_accelerator_get_default_mod_mask () & ~GDK_SHIFT_MASK))
+       return FALSE;
+    }
+  
+  /* Handle backspace */
+  if (context_simple->in_hex_sequence && have_hex_mods && is_backspace)
+    {
+      if (n_compose > 0)
+       {
+         n_compose--;
+         context_simple->compose_buffer[n_compose] = 0;
+          check_hex (context_simple, n_compose);
+       }
+      else
+       {
+         context_simple->in_hex_sequence = FALSE;
+       }
+
+      g_signal_emit_by_name (context_simple, "preedit_changed");
+      
+      return TRUE;
     }
+
+  /* Check for hex sequence start */
+  if (n_compose == 0 && !context_simple->in_hex_sequence && have_hex_mods && is_hex_start)
+    {
+      context_simple->compose_buffer[0] = 0;
+      context_simple->in_hex_sequence = TRUE;
+      context_simple->modifiers_dropped = FALSE;
+      context_simple->tentative_match = 0;
+
+      g_signal_emit_by_name (context_simple, "preedit_changed");
   
-  /* First key in sequence; decide if it's a 14755 hex sequence */
-  if (n_compose == 0)
-    context_simple->in_hex_sequence = have_hex_mods;
+      return TRUE;
+    }
   
-  /* Then, check for compose sequences
-   */
+  /* Then, check for compose sequences */
   if (context_simple->in_hex_sequence)
-    context_simple->compose_buffer[n_compose++] = hex_keyval ? hex_keyval : event->keyval;
+    {
+      if (hex_keyval)
+       context_simple->compose_buffer[n_compose++] = hex_keyval;
+      else if (!is_space)
+       {
+         /* non-hex character in hex sequence */
+         gdk_display_beep (gdk_drawable_get_display (event->window));
+         
+         return TRUE;
+       }
+    }
   else
     context_simple->compose_buffer[n_compose++] = event->keyval;
 
@@ -1378,16 +1443,30 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
       if (have_hex_mods)
         {
           /* space ends the sequence, and we eat the space */
-          if (n_compose > 1 &&
-              (event->keyval == GDK_space ||
-               event->keyval == GDK_KP_Space))
+          if (n_compose > 0 && is_space)
             {
-              gtk_im_context_simple_commit_char (context, context_simple->tentative_match);
-              context_simple->compose_buffer[0] = 0;
-              return TRUE;
+             if (context_simple->tentative_match &&
+                 g_unichar_validate (context_simple->tentative_match))
+               {
+                 gtk_im_context_simple_commit_char (context, context_simple->tentative_match);
+                 context_simple->compose_buffer[0] = 0;
+               }
+             else
+               {
+                 /* invalid hex sequence */
+                 gdk_display_beep (gdk_drawable_get_display (event->window));
+
+                 context_simple->tentative_match = 0;
+                 context_simple->in_hex_sequence = FALSE;
+                 context_simple->compose_buffer[0] = 0;
+               }
             }
-          else if (check_hex (context_simple, n_compose))
-            return TRUE;
+          else if (!check_hex (context_simple, n_compose))
+           gdk_display_beep (gdk_drawable_get_display (event->window));
+         
+         g_signal_emit_by_name (context_simple, "preedit_changed");
+
+         return TRUE;
         }
     }
   else
@@ -1412,13 +1491,15 @@ static void
 gtk_im_context_simple_reset (GtkIMContext *context)
 {
   GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context);
-  
+
   context_simple->compose_buffer[0] = 0;
 
   if (context_simple->tentative_match)
     gtk_im_context_simple_commit_char (context, context_simple->tentative_match);
-
+  
   context_simple->in_hex_sequence = FALSE;
+
+  g_signal_emit_by_name (context_simple, "preedit_changed");
 }
 
 static void     
@@ -1432,32 +1513,32 @@ gtk_im_context_simple_get_preedit_string (GtkIMContext   *context,
   
   GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context);
 
-  if (context_simple->tentative_match)
+  if (context_simple->in_hex_sequence)
     {
-      if (context_simple->in_hex_sequence)
-        {
-          int hexchars = 0;
-          
-          while (context_simple->compose_buffer[hexchars] != 0)
-            {
-              len += g_unichar_to_utf8 (gdk_keyval_to_unicode (context_simple->compose_buffer[hexchars]),
-                                        outbuf + len);
-              ++hexchars;
-            }
-        }
-      else
-        {
-          len = g_unichar_to_utf8 (context_simple->tentative_match, outbuf);
-        }
-      
+      int hexchars = 0;
+         
+      outbuf[0] = 'u';
+      len = 1;
+
+      while (context_simple->compose_buffer[hexchars] != 0)
+       {
+         len += g_unichar_to_utf8 (gdk_keyval_to_unicode (context_simple->compose_buffer[hexchars]),
+                                   outbuf + len);
+         ++hexchars;
+       }
+
       g_assert (len < 25);
       outbuf[len] = '\0';      
     }
+  else if (context_simple->tentative_match)
+    {
+      len = g_unichar_to_utf8 (context_simple->tentative_match, outbuf);
+    }
   else
     {
       outbuf[0] = '\0';
     }
-  
+      
   if (str)
     *str = g_strdup (outbuf);
 
@@ -1475,7 +1556,7 @@ gtk_im_context_simple_get_preedit_string (GtkIMContext   *context,
     }
 
   if (cursor_pos)
-    *cursor_pos = (context_simple->tentative_match ? len : 0);
+    *cursor_pos = len;
 }
 
 /**