diff --git a/ChangeLog b/ChangeLog index f4e19fa615..e1c8129714 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2005-09-02 Matthias Clasen + + * gtk/gtkimcontextsimple.c: Rework the Unicode hex input + code. Now we only steal a single key combination, Ctrl-Shift-U, + instead of sixteen. + 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. + (#82011, Owen Taylor) + 2005-09-02 Matthias Clasen * gtk/gtkmain.c (gtk_get_event_widget): If the window is destroyed, diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index f4e19fa615..e1c8129714 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,18 @@ +2005-09-02 Matthias Clasen + + * gtk/gtkimcontextsimple.c: Rework the Unicode hex input + code. Now we only steal a single key combination, Ctrl-Shift-U, + instead of sixteen. + 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. + (#82011, Owen Taylor) + 2005-09-02 Matthias Clasen * gtk/gtkmain.c (gtk_get_event_widget): If the window is destroyed, diff --git a/gtk/gtkimcontextsimple.c b/gtk/gtkimcontextsimple.c index 26f0d4dd18..8c6719e224 100644 --- a/gtk/gtkimcontextsimple.c +++ b/gtk/gtkimcontextsimple.c @@ -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; } - /* First key in sequence; decide if it's a 14755 hex sequence */ - if (n_compose == 0) - context_simple->in_hex_sequence = have_hex_mods; + /* 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"); - /* Then, check for compose sequences - */ + return TRUE; + } + + /* 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; } /**