diff options
author | Matthias Clasen <mclasen@redhat.com> | 2021-02-08 20:28:49 -0500 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2021-02-08 20:28:49 -0500 |
commit | 85d34932f3888caecd4b50dcfce530c8b82a7a5b (patch) | |
tree | 63693429b70d737a45f553a9beb7e4b45ea83dfa | |
parent | a0818f5bae2675f6cbca8b16256a63728036fdd5 (diff) | |
download | gtk+-85d34932f3888caecd4b50dcfce530c8b82a7a5b.tar.gz |
imcontext: Backport compose sequence improvements
Improve compose sequence handling:
- Show preedit for compose seqences
- Support sequences of up to 20 code points
- Warn when ignoring Compose file features
- Support compose sequences producing multiple characters
- Support hex escapes
-rw-r--r-- | gtk/gtkcomposetable.c | 823 | ||||
-rw-r--r-- | gtk/gtkcomposetable.h | 45 | ||||
-rw-r--r-- | gtk/gtkimcontextsimple.c | 1007 | ||||
-rw-r--r-- | gtk/gtkimcontextsimpleprivate.h | 42 |
4 files changed, 900 insertions, 1017 deletions
diff --git a/gtk/gtkcomposetable.c b/gtk/gtkcomposetable.c index fcc171b184..95cb16f9b8 100644 --- a/gtk/gtkcomposetable.c +++ b/gtk/gtkcomposetable.c @@ -26,16 +26,17 @@ #include "gtkcomposetable.h" #include "gtkimcontextsimple.h" -#include "gtkimcontextsimpleprivate.h" - #define GTK_COMPOSE_TABLE_MAGIC "GtkComposeTable" -#define GTK_COMPOSE_TABLE_VERSION (1) +#define GTK_COMPOSE_TABLE_VERSION (2) + +/* Maximum length of sequences we parse */ + +#define MAX_COMPOSE_LEN 20 typedef struct { - gunichar *sequence; - gunichar value[2]; - gchar *comment; + gunichar *sequence; + char *value; } GtkComposeData; @@ -43,7 +44,7 @@ static void gtk_compose_data_free (GtkComposeData *compose_data) { g_free (compose_data->sequence); - g_free (compose_data->comment); + g_free (compose_data->value); g_slice_free (GtkComposeData, compose_data); } @@ -54,7 +55,7 @@ gtk_compose_list_element_free (GtkComposeData *compose_data, gpointer data) } static gboolean -is_codepoint (const gchar *str) +is_codepoint (const char *str) { int i; @@ -73,70 +74,94 @@ is_codepoint (const gchar *str) static gboolean parse_compose_value (GtkComposeData *compose_data, - const gchar *val, - const gchar *line) + const char *val, + const char *line) { - gchar **words = g_strsplit (val, "\"", 3); - gunichar uch; - - if (g_strv_length (words) < 3) + char *word; + const char *p; + gsize len; + GString *value; + gunichar ch; + char *endp; + + len = strlen (val); + if (val[0] != '"' || val[len - 1] != '"') { g_warning ("Need to double-quote the value: %s: %s", val, line); goto fail; } - uch = g_utf8_get_char (words[1]); + word = g_strndup (val + 1, len - 2); - if (uch == 0) - { - g_warning ("Invalid value: %s: %s", val, line); - goto fail; - } - else if (uch == '\\') - { - uch = words[1][1]; + value = g_string_new (""); - /* The escaped string "\"" is separated with '\\' and '"'. */ - if (uch == '\0' && words[2][0] == '"') - uch = '"'; - /* The escaped octal */ - else if (uch >= '0' && uch <= '8') - uch = g_ascii_strtoll(words[1] + 1, NULL, 8); - /* If we need to handle other escape sequences. */ - else if (uch != '\\') + p = word; + while (*p) + { + if (*p == '\\') { - g_warning ("Invalid escape sequence: %s: %s", val, line); + if (p[1] == '"') + { + g_string_append_c (value, '"'); + p += 2; + } + else if (p[1] == '\\') + { + g_string_append_c (value, '\\'); + p += 2; + } + else if (p[1] >= '0' && p[1] < '8') + { + ch = g_ascii_strtoll (p + 1, &endp, 8); + if (ch == 0) + { + g_warning ("Invalid escape sequence: %s: %s", val, line); + goto fail; + } + g_string_append_unichar (value, ch); + p = endp; + } + else if (p[1] == 'x' || p[1] == 'X') + { + ch = g_ascii_strtoll (p + 2, &endp, 16); + if (ch == 0) + { + g_warning ("Invalid escape sequence: %s: %s", val, line); + goto fail; + } + g_string_append_unichar (value, ch); + p = endp; + } + else + { + g_warning ("Invalid escape sequence: %s: %s", val, line); + goto fail; + } + } + else + { + ch = g_utf8_get_char (p); + g_string_append_unichar (value, ch); + p = g_utf8_next_char (p); } } - if (g_utf8_get_char (g_utf8_next_char (words[1])) > 0) - { - g_warning ("GTK+ supports to output one char only: %s: %s", val, line); - goto fail; - } - - compose_data->value[1] = uch; + compose_data->value = g_string_free (value, FALSE); - if (uch == '"') - compose_data->comment = g_strdup (g_strstrip (words[2] + 1)); - else - compose_data->comment = g_strdup (g_strstrip (words[2])); - - g_strfreev (words); + g_free (word); return TRUE; fail: - g_strfreev (words); return FALSE; } static gboolean parse_compose_sequence (GtkComposeData *compose_data, - const gchar *seq, - const gchar *line) + const char *seq, + const char *line) { - gchar **words = g_strsplit (seq, "<", -1); + char **words = g_strsplit (seq, "<", -1); int i; int n = 0; @@ -148,9 +173,9 @@ parse_compose_sequence (GtkComposeData *compose_data, for (i = 1; words[i] != NULL; i++) { - gchar *start = words[i]; - gchar *end = strchr (words[i], '>'); - gchar *match; + char *start = words[i]; + char *end = strchr (words[i], '>'); + char *match; gunichar codepoint; if (words[i][0] == '\0') @@ -189,10 +214,10 @@ parse_compose_sequence (GtkComposeData *compose_data, } g_strfreev (words); - if (0 == n || n >= GTK_MAX_COMPOSE_LEN) + if (0 == n || n > MAX_COMPOSE_LEN) { - g_warning ("The max number of sequences is %d: %s", - GTK_MAX_COMPOSE_LEN, line); + g_warning ("Suspicious compose sequence length (%d). Are you sure this is right?: %s", + n, line); return FALSE; } @@ -205,16 +230,19 @@ fail: static void parse_compose_line (GList **compose_list, - const gchar *line) + const char *line) { - gchar **components = NULL; + char **components = NULL; GtkComposeData *compose_data = NULL; if (line[0] == '\0' || line[0] == '#') return; if (g_str_has_prefix (line, "include ")) - return; + { + g_warning ("include in Compose files not supported: %s", line); + return; + } components = g_strsplit (line, ":", 2); @@ -244,11 +272,13 @@ fail: gtk_compose_data_free (compose_data); } +extern const GtkComposeTableCompact gtk_compose_table_compact; + static GList * -gtk_compose_list_parse_file (const gchar *compose_file) +gtk_compose_list_parse_file (const char *compose_file) { - gchar *contents = NULL; - gchar **lines = NULL; + char *contents = NULL; + char **lines = NULL; gsize length = 0; GError *error = NULL; GList *compose_list = NULL; @@ -279,18 +309,19 @@ gtk_compose_list_check_duplicated (GList *compose_list) for (list = compose_list; list != NULL; list = list->next) { - static guint16 keysyms[GTK_MAX_COMPOSE_LEN + 1]; + static guint16 keysyms[MAX_COMPOSE_LEN + 1]; int i; int n_compose = 0; gboolean compose_finish; gunichar output_char; + char buf[8] = { 0, }; compose_data = list->data; - for (i = 0; i < GTK_MAX_COMPOSE_LEN + 1; i++) + for (i = 0; i < MAX_COMPOSE_LEN + 1; i++) keysyms[i] = 0; - for (i = 0; i < GTK_MAX_COMPOSE_LEN + 1; i++) + for (i = 0; i < MAX_COMPOSE_LEN + 1; i++) { gunichar codepoint = compose_data->sequence[i]; keysyms[i] = (guint16) codepoint; @@ -301,20 +332,21 @@ gtk_compose_list_check_duplicated (GList *compose_list) n_compose++; } - if (gtk_check_compact_table (>k_compose_table_compact, - keysyms, - n_compose, - &compose_finish, - NULL, - &output_char) && + if (gtk_compose_table_compact_check (>k_compose_table_compact, + keysyms, n_compose, + &compose_finish, + NULL, + &output_char) && compose_finish) { - if (compose_data->value[1] == output_char) + g_unichar_to_utf8 (output_char, buf); + if (strcmp (compose_data->value, buf) == 0) removed_list = g_list_prepend (removed_list, compose_data); } else if (gtk_check_algorithmically (keysyms, n_compose, &output_char)) { - if (compose_data->value[1] == output_char) + g_unichar_to_utf8 (output_char, buf); + if (strcmp (compose_data->value, buf) == 0) removed_list = g_list_prepend (removed_list, compose_data); } } @@ -343,7 +375,7 @@ gtk_compose_list_check_uint16 (GList *compose_list) int i; compose_data = list->data; - for (i = 0; i < GTK_MAX_COMPOSE_LEN; i++) + for (i = 0; i < MAX_COMPOSE_LEN; i++) { gunichar codepoint = compose_data->sequence[i]; @@ -384,7 +416,7 @@ gtk_compose_list_format_for_gtk (GList *compose_list, for (list = compose_list; list != NULL; list = list->next) { compose_data = list->data; - for (i = 0; i < GTK_MAX_COMPOSE_LEN + 1; i++) + for (i = 0; i < MAX_COMPOSE_LEN + 1; i++) { codepoint = compose_data->sequence[i]; if (codepoint == 0) @@ -401,21 +433,10 @@ gtk_compose_list_format_for_gtk (GList *compose_list, if (p_n_index_stride) *p_n_index_stride = max_compose_len + 2; - for (list = compose_list; list != NULL; list = list->next) - { - compose_data = list->data; - codepoint = compose_data->value[1]; - if (codepoint > 0xffff) - { - compose_data->value[0] = codepoint / 0x10000; - compose_data->value[1] = codepoint - codepoint / 0x10000 * 0x10000; - } - } - return compose_list; } -static gint +static int gtk_compose_data_compare (gpointer a, gpointer b, gpointer data) @@ -436,61 +457,6 @@ gtk_compose_data_compare (gpointer a, return 0; } -static void -gtk_compose_list_print (GList *compose_list, - int max_compose_len, - int n_index_stride) -{ - GList *list; - int i, j; - GtkComposeData *compose_data; - int total_size = 0; - gunichar upper; - gunichar lower; - const gchar *comment; - const gchar *keyval; - - for (list = compose_list; list != NULL; list = list->next) - { - compose_data = list->data; - g_printf (" "); - - for (i = 0; i < max_compose_len; i++) - { - if (compose_data->sequence[i] == 0) - { - for (j = i; j < max_compose_len; j++) - { - if (j == max_compose_len - 1) - g_printf ("0,\n"); - else - g_printf ("0, "); - } - break; - } - - keyval = gdk_keyval_name (compose_data->sequence[i]); - if (i == max_compose_len - 1) - g_printf ("%s,\n", keyval ? keyval : "(null)"); - else - g_printf ("%s, ", keyval ? keyval : "(null)"); - } - upper = compose_data->value[0]; - lower = compose_data->value[1]; - comment = compose_data->comment; - - if (list == g_list_last (compose_list)) - g_printf (" %#06X, %#06X /* %s */\n", upper, lower, comment); - else - g_printf (" %#06X, %#06X, /* %s */\n", upper, lower, comment); - - total_size += n_index_stride; - } - - g_printerr ("TOTAL_SIZE: %d\nMAX_COMPOSE_LEN: %d\nN_INDEX_STRIDE: %d\n", - total_size, max_compose_len, n_index_stride); -} - /* Implemented from g_str_hash() */ static guint32 gtk_compose_table_data_hash (gconstpointer v, int length) @@ -510,12 +476,12 @@ gtk_compose_table_data_hash (gconstpointer v, int length) return h; } -static gchar * +static char * gtk_compose_hash_get_cache_path (guint32 hash) { - gchar *basename = NULL; - gchar *dir = NULL; - gchar *path = NULL; + char *basename = NULL; + char *dir = NULL; + char *path = NULL; basename = g_strdup_printf ("%08x.cache", hash); @@ -534,18 +500,19 @@ gtk_compose_hash_get_cache_path (guint32 hash) return path; } -static gchar * +static char * gtk_compose_table_serialize (GtkComposeTable *compose_table, gsize *count) { - gchar *p, *contents; + char *p, *contents; gsize length, total_length; guint16 bytes; - const gchar *header = GTK_COMPOSE_TABLE_MAGIC; + const char *header = GTK_COMPOSE_TABLE_MAGIC; const guint16 version = GTK_COMPOSE_TABLE_VERSION; guint16 max_seq_len = compose_table->max_seq_len; guint16 index_stride = max_seq_len + 2; guint16 n_seqs = compose_table->n_seqs; + guint16 n_chars = compose_table->n_chars; guint32 i; g_return_val_if_fail (compose_table != NULL, NULL); @@ -553,49 +520,39 @@ gtk_compose_table_serialize (GtkComposeTable *compose_table, g_return_val_if_fail (index_stride > 0, NULL); length = strlen (header); - total_length = length + sizeof (guint16) * (3 + index_stride * n_seqs); + total_length = length + sizeof (guint16) * (4 + index_stride * n_seqs) + n_chars; if (count) *count = total_length; - p = contents = g_slice_alloc (total_length); + p = contents = g_malloc (total_length); memcpy (p, header, length); p += length; - /* Copy by byte for endian */ -#define BYTE_COPY_FROM_BUF(element) \ - bytes = GUINT16_TO_BE ((element)); \ - memcpy (p, &bytes, length); \ - p += length; \ - if (p - contents > total_length) \ - { \ - g_warning ("data size %lld is bigger than %" G_GSIZE_FORMAT, \ - (long long) (p - contents), total_length); \ - g_free (contents); \ - if (count) \ - { \ - *count = 0; \ - } \ - return NULL; \ - } - - length = sizeof (guint16); - - BYTE_COPY_FROM_BUF (version); - BYTE_COPY_FROM_BUF (max_seq_len); - BYTE_COPY_FROM_BUF (n_seqs); +#define APPEND_GUINT16(elt) \ + bytes = GUINT16_TO_BE (elt); \ + memcpy (p, &bytes, sizeof (guint16)); \ + p += sizeof (guint16); + + APPEND_GUINT16 (version); + APPEND_GUINT16 (max_seq_len); + APPEND_GUINT16 (n_seqs); + APPEND_GUINT16 (n_chars); for (i = 0; i < (guint32) index_stride * n_seqs; i++) { - BYTE_COPY_FROM_BUF (compose_table->data[i]); + APPEND_GUINT16 (compose_table->data[i]); } -#undef BYTE_COPY_FROM_BUF + if (compose_table->n_chars > 0) + memcpy (p, compose_table->char_data, compose_table->n_chars); + +#undef APPEND_GUINT16 return contents; } -static gint +static int gtk_compose_table_find (gconstpointer data1, gconstpointer data2) { @@ -605,25 +562,26 @@ gtk_compose_table_find (gconstpointer data1, } static GtkComposeTable * -gtk_compose_table_load_cache (const gchar *compose_file) +gtk_compose_table_load_cache (const char *compose_file) { guint32 hash; - gchar *path = NULL; - gchar *contents = NULL; - gchar *p; + char *path = NULL; + char *contents = NULL; + char *p; GStatBuf original_buf; GStatBuf cache_buf; gsize total_length; - gsize length; GError *error = NULL; guint16 bytes; guint16 version; guint16 max_seq_len; guint16 index_stride; guint16 n_seqs; + guint16 n_chars; guint32 i; guint16 *gtk_compose_seqs = NULL; GtkComposeTable *retval; + char *char_data = NULL; hash = g_str_hash (compose_file); if ((path = gtk_compose_hash_get_cache_path (hash)) == NULL) @@ -642,16 +600,10 @@ gtk_compose_table_load_cache (const gchar *compose_file) goto out_load_cache; } - /* Copy by byte for endian */ -#define BYTE_COPY_TO_BUF(element) \ - memcpy (&bytes, p, length); \ - element = GUINT16_FROM_BE (bytes); \ - p += length; \ - if (p - contents > total_length) \ - { \ - g_warning ("Broken cache content %s in %s", path, #element); \ - goto out_load_cache; \ - } +#define GET_GUINT16(elt) \ + memcpy (&bytes, p, sizeof (guint16)); \ + elt = GUINT16_FROM_BE (bytes); \ + p += sizeof (guint16); p = contents; if (g_ascii_strncasecmp (p, GTK_COMPOSE_TABLE_MAGIC, @@ -660,6 +612,7 @@ gtk_compose_table_load_cache (const gchar *compose_file) g_warning ("The file is not a GtkComposeTable cache file %s", path); goto out_load_cache; } + p += strlen (GTK_COMPOSE_TABLE_MAGIC); if (p - contents > total_length) { @@ -667,9 +620,7 @@ gtk_compose_table_load_cache (const gchar *compose_file) goto out_load_cache; } - length = sizeof (guint16); - - BYTE_COPY_TO_BUF (version); + GET_GUINT16 (version); if (version != GTK_COMPOSE_TABLE_VERSION) { g_warning ("cache version is different %u != %u", @@ -677,8 +628,9 @@ gtk_compose_table_load_cache (const gchar *compose_file) goto out_load_cache; } - BYTE_COPY_TO_BUF (max_seq_len); - BYTE_COPY_TO_BUF (n_seqs); + GET_GUINT16 (max_seq_len); + GET_GUINT16 (n_seqs); + GET_GUINT16 (n_chars); if (max_seq_len == 0 || n_seqs == 0) { @@ -691,13 +643,22 @@ gtk_compose_table_load_cache (const gchar *compose_file) for (i = 0; i < (guint32) index_stride * n_seqs; i++) { - BYTE_COPY_TO_BUF (gtk_compose_seqs[i]); + GET_GUINT16 (gtk_compose_seqs[i]); + } + + if (n_chars > 0) + { + char_data = g_new (char, n_chars + 1); + memcpy (char_data, p, n_chars); + char_data[n_chars] = '\0'; } retval = g_new0 (GtkComposeTable, 1); retval->data = gtk_compose_seqs; retval->max_seq_len = max_seq_len; retval->n_seqs = n_seqs; + retval->char_data = char_data; + retval->n_chars = n_chars; retval->id = hash; g_free (contents); @@ -705,10 +666,11 @@ gtk_compose_table_load_cache (const gchar *compose_file) return retval; -#undef BYTE_COPY_TO_BUF +#undef GET_GUINT16 out_load_cache: g_free (gtk_compose_seqs); + g_free (char_data); g_free (contents); g_free (path); return NULL; @@ -717,8 +679,8 @@ out_load_cache: static void gtk_compose_table_save_cache (GtkComposeTable *compose_table) { - gchar *path = NULL; - gchar *contents = NULL; + char *path = NULL; + char *contents = NULL; GError *error = NULL; gsize length = 0; @@ -739,7 +701,7 @@ gtk_compose_table_save_cache (GtkComposeTable *compose_table) } out_save_cache: - g_slice_free1 (length, contents); + g_free (contents); g_free (path); } @@ -756,6 +718,8 @@ gtk_compose_table_new_with_list (GList *compose_list, GList *list; GtkComposeData *compose_data; GtkComposeTable *retval = NULL; + gunichar codepoint; + GString *char_data; g_return_val_if_fail (compose_list != NULL, NULL); @@ -763,6 +727,8 @@ gtk_compose_table_new_with_list (GList *compose_list, gtk_compose_seqs = g_new0 (guint16, length * n_index_stride); + char_data = g_string_new (""); + for (list = compose_list; list != NULL; list = list->next) { compose_data = list->data; @@ -776,8 +742,24 @@ gtk_compose_table_new_with_list (GList *compose_list, } gtk_compose_seqs[n++] = (guint16) compose_data->sequence[i]; } - gtk_compose_seqs[n++] = (guint16) compose_data->value[0]; - gtk_compose_seqs[n++] = (guint16) compose_data->value[1]; + + if (g_utf8_strlen (compose_data->value, -1) > 1) + { + if (char_data->len > 0) + g_string_append_c (char_data, 0); + + codepoint = char_data->len | (1 << 31); + + g_string_append (char_data, compose_data->value); + } + else + { + codepoint = g_utf8_get_char (compose_data->value); + g_assert ((codepoint & (1 << 31)) == 0); + } + + gtk_compose_seqs[n++] = (codepoint & 0xffff0000) >> 16; + gtk_compose_seqs[n++] = codepoint & 0xffff; } retval = g_new0 (GtkComposeTable, 1); @@ -785,12 +767,14 @@ gtk_compose_table_new_with_list (GList *compose_list, retval->max_seq_len = max_compose_len; retval->n_seqs = length; retval->id = hash; + retval->n_chars = char_data->len; + retval->char_data = g_string_free (char_data, FALSE); return retval; } GtkComposeTable * -gtk_compose_table_new_with_file (const gchar *compose_file) +gtk_compose_table_new_with_file (const char *compose_file) { GList *compose_list = NULL; GtkComposeTable *compose_table; @@ -816,9 +800,6 @@ gtk_compose_table_new_with_file (const gchar *compose_file) return NULL; } - if (g_getenv ("GTK_COMPOSE_TABLE_PRINT") != NULL) - gtk_compose_list_print (compose_list, max_compose_len, n_index_stride); - compose_table = gtk_compose_table_new_with_list (compose_list, max_compose_len, n_index_stride, @@ -830,18 +811,26 @@ gtk_compose_table_new_with_file (const gchar *compose_file) GSList * gtk_compose_table_list_add_array (GSList *compose_tables, const guint16 *data, - gint max_seq_len, - gint n_seqs) + int max_seq_len, + int n_seqs) { guint32 hash; GtkComposeTable *compose_table; - int n_index_stride = max_seq_len + 2; - int length = n_index_stride * n_seqs; + gsize n_index_stride; + gsize length; int i; guint16 *gtk_compose_seqs = NULL; g_return_val_if_fail (data != NULL, compose_tables); - g_return_val_if_fail (max_seq_len <= GTK_MAX_COMPOSE_LEN, compose_tables); + g_return_val_if_fail (max_seq_len >= 0, compose_tables); + g_return_val_if_fail (n_seqs >= 0, compose_tables); + + n_index_stride = max_seq_len + 2; + if (!g_size_checked_mul (&length, n_index_stride, n_seqs)) + { + g_critical ("Overflow in the compose sequences"); + return compose_tables; + } hash = gtk_compose_table_data_hash (data, length); @@ -857,13 +846,15 @@ gtk_compose_table_list_add_array (GSList *compose_tables, compose_table->max_seq_len = max_seq_len; compose_table->n_seqs = n_seqs; compose_table->id = hash; + compose_table->char_data = NULL; + compose_table->n_chars = 0; return g_slist_prepend (compose_tables, compose_table); } GSList * -gtk_compose_table_list_add_file (GSList *compose_tables, - const gchar *compose_file) +gtk_compose_table_list_add_file (GSList *compose_tables, + const char *compose_file) { guint32 hash; GtkComposeTable *compose_table; @@ -884,3 +875,409 @@ gtk_compose_table_list_add_file (GSList *compose_tables, gtk_compose_table_save_cache (compose_table); return g_slist_prepend (compose_tables, compose_table); } + +static int +compare_seq (const void *key, const void *value) +{ + int i = 0; + const guint16 *keysyms = key; + const guint16 *seq = value; + + while (keysyms[i]) + { + if (keysyms[i] < seq[i]) + return -1; + else if (keysyms[i] > seq[i]) + return 1; + + i++; + } + + return 0; +} + +/* + * gtk_compose_table_check: + * @table: the table to check + * @compose_buffer: the key vals to match + * @n_compose: number of non-zero key vals in @compose_buffer + * @compose_finish: (out): return location for whether there may be longer matches + * @compose_match: (out): return location for whether there is a match + * @output: (out) (caller-allocates): return location for the match values + * + * Looks for matches for a key sequence in @table. + * + * Returns: %TRUE if there were any matches, %FALSE otherwise + */ +gboolean +gtk_compose_table_check (const GtkComposeTable *table, + const guint16 *compose_buffer, + int n_compose, + gboolean *compose_finish, + gboolean *compose_match, + GString *output) +{ + int row_stride = table->max_seq_len + 2; + guint16 *seq; + + *compose_finish = FALSE; + *compose_match = FALSE; + + g_string_set_size (output, 0); + + /* Will never match, if the sequence in the compose buffer is longer + * than the sequences in the table. Further, compare_seq (key, val) + * will overrun val if key is longer than val. + */ + if (n_compose > table->max_seq_len) + return FALSE; + + seq = bsearch (compose_buffer, + table->data, table->n_seqs, + sizeof (guint16) * row_stride, + compare_seq); + + if (seq) + { + guint16 *prev_seq; + + /* Back up to the first sequence that matches to make sure + * we find the exact match if there is one. + */ + while (seq > table->data) + { + prev_seq = seq - row_stride; + if (compare_seq (compose_buffer, prev_seq) != 0) + break; + seq = prev_seq; + } + + if (n_compose == table->max_seq_len || + seq[n_compose] == 0) /* complete sequence */ + { + guint16 *next_seq; + gunichar value; + + value = (seq[table->max_seq_len] << 16) | seq[table->max_seq_len + 1]; + if ((value & (1 << 31)) != 0) + g_string_append (output, &table->char_data[value & ~(1 << 31)]); + else + g_string_append_unichar (output, value); + + *compose_match = TRUE; + + /* We found a tentative match. See if there are any longer + * sequences containing this subsequence + */ + next_seq = seq + row_stride; + if (next_seq < table->data + row_stride * table->n_seqs) + { + if (compare_seq (compose_buffer, next_seq) == 0) + return TRUE; + } + + *compose_finish = TRUE; + return TRUE; + } + + return TRUE; + } + + return FALSE; +} + +static int +compare_seq_index (const void *key, const void *value) +{ + const guint16 *keysyms = key; + const guint16 *seq = value; + + if (keysyms[0] < seq[0]) + return -1; + else if (keysyms[0] > seq[0]) + return 1; + + return 0; +} + +gboolean +gtk_compose_table_compact_check (const GtkComposeTableCompact *table, + const guint16 *compose_buffer, + int n_compose, + gboolean *compose_finish, + gboolean *compose_match, + gunichar *output_char) +{ + int row_stride; + guint16 *seq_index; + guint16 *seq; + int i; + gboolean match; + gunichar value; + + if (compose_finish) + *compose_finish = FALSE; + if (compose_match) + *compose_match = FALSE; + if (output_char) + *output_char = 0; + + /* Will never match, if the sequence in the compose buffer is longer + * than the sequences in the table. Further, compare_seq (key, val) + * will overrun val if key is longer than val. + */ + if (n_compose > table->max_seq_len) + return FALSE; + + seq_index = bsearch (compose_buffer, + table->data, + table->n_index_size, + sizeof (guint16) * table->n_index_stride, + compare_seq_index); + + if (!seq_index) + return FALSE; + + if (n_compose == 1) + return TRUE; + + seq = NULL; + match = FALSE; + value = 0; + + for (i = n_compose - 1; i < table->max_seq_len; i++) + { + row_stride = i + 1; + + if (seq_index[i + 1] - seq_index[i] > 0) + { + seq = bsearch (compose_buffer + 1, + table->data + seq_index[i], + (seq_index[i + 1] - seq_index[i]) / row_stride, + sizeof (guint16) * row_stride, + compare_seq); + + if (seq) + { + if (i == n_compose - 1) + { + value = seq[row_stride - 1]; + match = TRUE; + } + else + { + if (output_char) + *output_char = value; + if (match) + { + if (compose_match) + *compose_match = TRUE; + } + + return TRUE; + } + } + } + } + + if (match) + { + if (compose_match) + *compose_match = TRUE; + if (compose_finish) + *compose_finish = TRUE; + if (output_char) + *output_char = value; + + return TRUE; + } + + return FALSE; +} + +/* Checks if a keysym is a dead key. + * Dead key keysym values are defined in ../gdk/gdkkeysyms.h and the + * first is GDK_KEY_dead_grave. As X.Org is updated, more dead keys + * are added and we need to update the upper limit. + */ +#define IS_DEAD_KEY(k) \ + ((k) >= GDK_KEY_dead_grave && (k) <= GDK_KEY_dead_greek) + +/* This function receives a sequence of Unicode characters and tries to + * normalize it (NFC). We check for the case where the resulting string + * has length 1 (single character). + * NFC normalisation normally rearranges diacritic marks, unless these + * belong to the same Canonical Combining Class. + * If they belong to the same canonical combining class, we produce all + * permutations of the diacritic marks, then attempt to normalize. + */ +static gboolean +check_normalize_nfc (gunichar *combination_buffer, + int n_compose) +{ + gunichar *combination_buffer_temp; + char *combination_utf8_temp = NULL; + char *nfc_temp = NULL; + int n_combinations; + gunichar temp_swap; + int i; + + combination_buffer_temp = g_alloca (n_compose * sizeof (gunichar)); + + n_combinations = 1; + + for (i = 1; i < n_compose; i++) + n_combinations *= i; + + /* Xorg reuses dead_tilde for the perispomeni diacritic mark. + * We check if base character belongs to Greek Unicode block, + * and if so, we replace tilde with perispomeni. + */ + if (combination_buffer[0] >= 0x390 && combination_buffer[0] <= 0x3FF) + { + for (i = 1; i < n_compose; i++ ) + if (combination_buffer[i] == 0x303) + combination_buffer[i] = 0x342; + } + + memcpy (combination_buffer_temp, combination_buffer, n_compose * sizeof (gunichar) ); + + for (i = 0; i < n_combinations; i++) + { + g_unicode_canonical_ordering (combination_buffer_temp, n_compose); + combination_utf8_temp = g_ucs4_to_utf8 (combination_buffer_temp, n_compose, NULL, NULL, NULL); + nfc_temp = g_utf8_normalize (combination_utf8_temp, -1, G_NORMALIZE_NFC); + + if (g_utf8_strlen (nfc_temp, -1) == 1) + { + memcpy (combination_buffer, combination_buffer_temp, n_compose * sizeof (gunichar) ); + + g_free (combination_utf8_temp); + g_free (nfc_temp); + + return TRUE; + } + + g_free (combination_utf8_temp); + g_free (nfc_temp); + + if (n_compose > 2) + { + temp_swap = combination_buffer_temp[i % (n_compose - 1) + 1]; + combination_buffer_temp[i % (n_compose - 1) + 1] = combination_buffer_temp[(i+1) % (n_compose - 1) + 1]; + combination_buffer_temp[(i+1) % (n_compose - 1) + 1] = temp_swap; + } + else + break; + } + + return FALSE; +} + +gboolean +gtk_check_algorithmically (const guint16 *compose_buffer, + int n_compose, + gunichar *output_char) + +{ + int i; + gunichar *combination_buffer; + char *combination_utf8, *nfc; + + combination_buffer = alloca (sizeof (gunichar) * (n_compose + 1)); + + if (output_char) + *output_char = 0; + + for (i = 0; i < n_compose && IS_DEAD_KEY (compose_buffer[i]); i++) + ; + if (i == n_compose) + return TRUE; + + if (i > 0 && i == n_compose - 1) + { + combination_buffer[0] = gdk_keyval_to_unicode (compose_buffer[i]); + combination_buffer[n_compose] = 0; + i--; + while (i >= 0) + { + switch (compose_buffer[i]) + { +#define CASE(keysym, unicode) \ + case GDK_KEY_dead_##keysym: combination_buffer[i+1] = unicode; break + + CASE (grave, 0x0300); + CASE (acute, 0x0301); + CASE (circumflex, 0x0302); + CASE (tilde, 0x0303); /* Also used with perispomeni, 0x342. */ + CASE (macron, 0x0304); + CASE (breve, 0x0306); + CASE (abovedot, 0x0307); + CASE (diaeresis, 0x0308); + CASE (abovering, 0x30A); + CASE (hook, 0x0309); + CASE (doubleacute, 0x030B); + CASE (caron, 0x030C); + CASE (cedilla, 0x0327); + CASE (ogonek, 0x0328); /* Legacy use for dasia, 0x314.*/ + CASE (iota, 0x0345); + CASE (voiced_sound, 0x3099); /* Per Markus Kuhn keysyms.txt file. */ + CASE (semivoiced_sound, 0x309A); /* Per Markus Kuhn keysyms.txt file. */ + CASE (belowdot, 0x0323); + CASE (horn, 0x031B); /* Legacy use for psili, 0x313 (or 0x343). */ + CASE (stroke, 0x335); + CASE (abovecomma, 0x0313); /* Equivalent to psili */ + CASE (abovereversedcomma, 0x0314); /* Equivalent to dasia */ + CASE (doublegrave, 0x30F); + CASE (belowring, 0x325); + CASE (belowmacron, 0x331); + CASE (belowcircumflex, 0x32D); + CASE (belowtilde, 0x330); + CASE (belowbreve, 0x32e); + CASE (belowdiaeresis, 0x324); + CASE (invertedbreve, 0x32f); + CASE (belowcomma, 0x326); + CASE (lowline, 0x332); + CASE (aboveverticalline, 0x30D); + CASE (belowverticalline, 0x329); + CASE (longsolidusoverlay, 0x338); + CASE (a, 0x363); + CASE (A, 0x363); + CASE (e, 0x364); + CASE (E, 0x364); + CASE (i, 0x365); + CASE (I, 0x365); + CASE (o, 0x366); + CASE (O, 0x366); + CASE (u, 0x367); + CASE (U, 0x367); + CASE (small_schwa, 0x1DEA); + CASE (capital_schwa, 0x1DEA); +#undef CASE + default: + combination_buffer[i+1] = gdk_keyval_to_unicode (compose_buffer[i]); + } + i--; + } + + /* If the buffer normalizes to a single character, then modify the order + * of combination_buffer accordingly, if necessary, and return TRUE. + */ + if (check_normalize_nfc (combination_buffer, n_compose)) + { + combination_utf8 = g_ucs4_to_utf8 (combination_buffer, -1, NULL, NULL, NULL); + nfc = g_utf8_normalize (combination_utf8, -1, G_NORMALIZE_NFC); + + if (output_char) + *output_char = g_utf8_get_char (nfc); + + g_free (combination_utf8); + g_free (nfc); + + return TRUE; + } + } + + return FALSE; +} + diff --git a/gtk/gtkcomposetable.h b/gtk/gtkcomposetable.h index f2e8e8de3d..101aa94359 100644 --- a/gtk/gtkcomposetable.h +++ b/gtk/gtkcomposetable.h @@ -29,26 +29,47 @@ typedef struct _GtkComposeTableCompact GtkComposeTableCompact; struct _GtkComposeTable { guint16 *data; - gint max_seq_len; - gint n_seqs; + char *char_data; + int max_seq_len; + int n_seqs; + int n_chars; guint32 id; }; struct _GtkComposeTableCompact { const guint16 *data; - gint max_seq_len; - gint n_index_size; - gint n_index_stride; + int max_seq_len; + int n_index_size; + int n_index_stride; }; -GtkComposeTable * gtk_compose_table_new_with_file (const gchar *compose_file); -GSList *gtk_compose_table_list_add_array (GSList *compose_tables, - const guint16 *data, - gint max_seq_len, - gint n_seqs); -GSList *gtk_compose_table_list_add_file (GSList *compose_tables, - const gchar *compose_file); +GtkComposeTable * gtk_compose_table_new_with_file (const char *compose_file); +GSList * gtk_compose_table_list_add_array (GSList *compose_tables, + const guint16 *data, + int max_seq_len, + int n_seqs); +GSList * gtk_compose_table_list_add_file (GSList *compose_tables, + const char *compose_file); + +gboolean gtk_compose_table_check (const GtkComposeTable *table, + const guint16 *compose_buffer, + int n_compose, + gboolean *compose_finish, + gboolean *compose_match, + GString *output); + +gboolean gtk_compose_table_compact_check (const GtkComposeTableCompact *table, + const guint16 *compose_buffer, + int n_compose, + gboolean *compose_finish, + gboolean *compose_match, + gunichar *output_char); + +gboolean gtk_check_algorithmically (const guint16 *compose_buffer, + int n_compose, + gunichar *output); + G_END_DECLS diff --git a/gtk/gtkimcontextsimple.c b/gtk/gtkimcontextsimple.c index 5dc724cf70..e73f39215e 100644 --- a/gtk/gtkimcontextsimple.c +++ b/gtk/gtkimcontextsimple.c @@ -19,16 +19,6 @@ #include <gdk/gdk.h> -#ifdef GDK_WINDOWING_X11 -#include <gdk/gdkx.h> -#endif -#ifdef GDK_WINDOWING_WAYLAND -#include <wayland/gdkwayland.h> -#endif -#ifdef GDK_WINDOWING_WIN32 -#include <win32/gdkwin32.h> -#endif - #include <stdlib.h> #include <string.h> @@ -40,8 +30,8 @@ #include "gtkdebug.h" #include "gtkintl.h" #include "gtkcomposetable.h" +#include "gtkimmoduleprivate.h" -#include "gtkimcontextsimpleprivate.h" #include "gtkimcontextsimpleseqs.h" /** @@ -59,6 +49,8 @@ * Compose file). The syntax of these files is described in the Compose(5) * manual page. * + * ## Unicode characters + * * GtkIMContextSimple also supports numeric entry of Unicode characters * by typing Ctrl-Shift-u, followed by a hexadecimal Unicode codepoint. * For example, Ctrl-Shift-u 1 2 3 Enter yields U+0123 LATIN SMALL LETTER @@ -67,9 +59,10 @@ struct _GtkIMContextSimplePrivate { - guint16 compose_buffer[GTK_MAX_COMPOSE_LEN + 1]; - gunichar tentative_match; - gint tentative_match_len; + guint16 *compose_buffer; + int compose_buffer_len; + GString *tentative_match; + int tentative_match_len; guint in_hex_sequence : 1; guint modifiers_dropped : 1; @@ -114,11 +107,13 @@ static gboolean gtk_im_context_simple_filter_keypress (GtkIMContext GdkEventKey *key); static void gtk_im_context_simple_reset (GtkIMContext *context); static void gtk_im_context_simple_get_preedit_string (GtkIMContext *context, - gchar **str, + char **str, PangoAttrList **attrs, - gint *cursor_pos); -static void gtk_im_context_simple_set_client_window (GtkIMContext *context, - GdkWindow *window); + int *cursor_pos); + +static void init_compose_table_async (GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); G_DEFINE_TYPE_WITH_PRIVATE (GtkIMContextSimple, gtk_im_context_simple, GTK_TYPE_IM_CONTEXT) @@ -131,16 +126,17 @@ gtk_im_context_simple_class_init (GtkIMContextSimpleClass *class) im_context_class->filter_keypress = gtk_im_context_simple_filter_keypress; im_context_class->reset = gtk_im_context_simple_reset; im_context_class->get_preedit_string = gtk_im_context_simple_get_preedit_string; - im_context_class->set_client_window = gtk_im_context_simple_set_client_window; gobject_class->finalize = gtk_im_context_simple_finalize; + + init_compose_table_async (NULL, NULL, NULL); } -static gchar* +static char * get_x11_compose_file_dir (void) { - gchar* compose_file_dir; + char * compose_file_dir; -#if defined (GDK_WINDOWING_X11) +#if defined (X11_DATA_PREFIX) compose_file_dir = g_strdup (X11_DATA_PREFIX "/share/X11/locale"); #else compose_file_dir = g_build_filename (_gtk_get_datadir (), "X11", "locale", NULL); @@ -150,26 +146,28 @@ get_x11_compose_file_dir (void) } static void -gtk_im_context_simple_init_compose_table (GtkIMContextSimple *im_context_simple) +gtk_im_context_simple_init_compose_table (void) { - gchar *path = NULL; - const gchar *home; - const gchar *locale; - gchar **langs = NULL; - gchar **lang = NULL; - gchar * const sys_langs[] = { "el_gr", "fi_fi", "pt_br", NULL }; - gchar * const *sys_lang = NULL; - gchar *x11_compose_file_dir = get_x11_compose_file_dir (); - - path = g_build_filename (g_get_user_config_dir (), "gtk-3.0", "Compose", NULL); + char *path = NULL; + const char *home; + const char *locale; + char **langs = NULL; + char **lang = NULL; + const char * const sys_langs[] = { "el_gr", "fi_fi", "pt_br", NULL }; + const char * const *sys_lang = NULL; + char *x11_compose_file_dir = get_x11_compose_file_dir (); + + path = g_build_filename (g_get_user_config_dir (), "gtk-4.0", "Compose", NULL); if (g_file_test (path, G_FILE_TEST_EXISTS)) { - gtk_im_context_simple_add_compose_file (im_context_simple, path); + G_LOCK (global_tables); + global_tables = gtk_compose_table_list_add_file (global_tables, path); + G_UNLOCK (global_tables); + g_free (path); return; } - g_free (path); - path = NULL; + g_clear_pointer (&path, g_free); home = g_get_home_dir (); if (home == NULL) @@ -178,12 +176,13 @@ gtk_im_context_simple_init_compose_table (GtkIMContextSimple *im_context_simple) path = g_build_filename (home, ".XCompose", NULL); if (g_file_test (path, G_FILE_TEST_EXISTS)) { - gtk_im_context_simple_add_compose_file (im_context_simple, path); + G_LOCK (global_tables); + global_tables = gtk_compose_table_list_add_file (global_tables, path); + G_UNLOCK (global_tables); g_free (path); return; } - g_free (path); - path = NULL; + g_clear_pointer (&path, g_free); locale = g_getenv ("LC_CTYPE"); if (locale == NULL) @@ -216,17 +215,19 @@ gtk_im_context_simple_init_compose_table (GtkIMContextSimple *im_context_simple) if (g_file_test (path, G_FILE_TEST_EXISTS)) break; - g_free (path); - path = NULL; + g_clear_pointer (&path, g_free); } g_free (x11_compose_file_dir); g_strfreev (langs); if (path != NULL) - gtk_im_context_simple_add_compose_file (im_context_simple, path); - g_free (path); - path = NULL; + { + G_LOCK (global_tables); + global_tables = gtk_compose_table_list_add_file (global_tables, path); + G_UNLOCK (global_tables); + } + g_clear_pointer (&path, g_free); } static void @@ -235,36 +236,47 @@ init_compose_table_thread_cb (GTask *task, gpointer task_data, GCancellable *cancellable) { + gint64 before G_GNUC_UNUSED; + if (g_task_return_error_if_cancelled (task)) return; - g_return_if_fail (GTK_IS_IM_CONTEXT_SIMPLE (task_data)); - - gtk_im_context_simple_init_compose_table (GTK_IM_CONTEXT_SIMPLE (task_data)); + gtk_im_context_simple_init_compose_table (); } -void -init_compose_table_async (GtkIMContextSimple *im_context_simple, - GCancellable *cancellable, +static void +init_compose_table_async (GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task = g_task_new (NULL, cancellable, callback, user_data); g_task_set_source_tag (task, init_compose_table_async); - g_task_set_task_data (task, g_object_ref (im_context_simple), g_object_unref); g_task_run_in_thread (task, init_compose_table_thread_cb); g_object_unref (task); } static void -gtk_im_context_simple_init (GtkIMContextSimple *im_context_simple) +gtk_im_context_simple_init (GtkIMContextSimple *context_simple) { - im_context_simple->priv = gtk_im_context_simple_get_instance_private (im_context_simple); + GtkIMContextSimplePrivate *priv; + + priv = context_simple->priv = gtk_im_context_simple_get_instance_private (context_simple); + + priv->compose_buffer_len = gtk_compose_table_compact.max_seq_len + 1; + priv->compose_buffer = g_new0 (guint16, priv->compose_buffer_len); + priv->tentative_match = g_string_new (""); + priv->tentative_match_len = 0; } static void gtk_im_context_simple_finalize (GObject *obj) { + GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (obj); + GtkIMContextSimplePrivate *priv = context_simple->priv;; + + g_free (priv->compose_buffer); + g_string_free (priv->tentative_match, TRUE); + G_OBJECT_CLASS (gtk_im_context_simple_parent_class)->finalize (obj); } @@ -282,512 +294,29 @@ gtk_im_context_simple_new (void) } static void -gtk_im_context_simple_commit_char (GtkIMContext *context, - gunichar ch) -{ - GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context); - GtkIMContextSimplePrivate *priv = context_simple->priv; - gchar buf[10]; - gint len; - - g_return_if_fail (g_unichar_validate (ch)); - - len = g_unichar_to_utf8 (ch, buf); - buf[len] = '\0'; - - if (priv->tentative_match || priv->in_hex_sequence) - { - priv->in_hex_sequence = FALSE; - priv->tentative_match = 0; - priv->tentative_match_len = 0; - g_signal_emit_by_name (context_simple, "preedit-changed"); - g_signal_emit_by_name (context_simple, "preedit-end"); - } - - g_signal_emit_by_name (context, "commit", &buf); -} - -static int -compare_seq_index (const void *key, const void *value) -{ - const guint16 *keysyms = key; - const guint16 *seq = value; - - if (keysyms[0] < seq[0]) - return -1; - else if (keysyms[0] > seq[0]) - return 1; - - return 0; -} - -static int -compare_seq (const void *key, const void *value) -{ - int i = 0; - const guint16 *keysyms = key; - const guint16 *seq = value; - - while (keysyms[i]) - { - if (keysyms[i] < seq[i]) - return -1; - else if (keysyms[i] > seq[i]) - return 1; - - i++; - } - - return 0; -} - -static gboolean -check_table (GtkIMContextSimple *context_simple, - const GtkComposeTable *table, - gint n_compose) +gtk_im_context_simple_commit_string (GtkIMContextSimple *context_simple, + const char *str) { GtkIMContextSimplePrivate *priv = context_simple->priv; - gint row_stride = table->max_seq_len + 2; - guint16 *seq; - - /* Will never match, if the sequence in the compose buffer is longer - * than the sequences in the table. Further, compare_seq (key, val) - * will overrun val if key is longer than val. */ - if (n_compose > table->max_seq_len) - return FALSE; - - seq = bsearch (priv->compose_buffer, - table->data, table->n_seqs, - sizeof (guint16) * row_stride, - compare_seq); - - if (seq) - { - guint16 *prev_seq; - - /* Back up to the first sequence that matches to make sure - * we find the exact match if there is one. - */ - while (seq > table->data) - { - prev_seq = seq - row_stride; - if (compare_seq (priv->compose_buffer, prev_seq) != 0) - break; - seq = prev_seq; - } - - if (n_compose == table->max_seq_len || - seq[n_compose] == 0) /* complete sequence */ - { - guint16 *next_seq; - gunichar value = - 0x10000 * seq[table->max_seq_len] + seq[table->max_seq_len + 1]; - - /* We found a tentative match. See if there are any longer - * sequences containing this subsequence - */ - next_seq = seq + row_stride; - if (next_seq < table->data + row_stride * table->n_seqs) - { - if (compare_seq (priv->compose_buffer, next_seq) == 0) - { - priv->tentative_match = value; - priv->tentative_match_len = n_compose; - - g_signal_emit_by_name (context_simple, "preedit-changed"); - - return TRUE; - } - } - - gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple), value); - priv->compose_buffer[0] = 0; - } - - return TRUE; - } - - return FALSE; -} - -/* Checks if a keysym is a dead key. Dead key keysym values are defined in - * ../gdk/gdkkeysyms.h and the first is GDK_KEY_dead_grave. As X.Org is updated, - * more dead keys are added and we need to update the upper limit. - * Currently, the upper limit is GDK_KEY_dead_dasia+1. The +1 has to do with - * a temporary issue in the X.Org header files. - * In future versions it will be just the keysym (no +1). - */ -#define IS_DEAD_KEY(k) \ - ((k) >= GDK_KEY_dead_grave && (k) <= (GDK_KEY_dead_dasia+1)) - -#ifdef GDK_WINDOWING_WIN32 - -/* On Windows, user expectation is that typing a dead accent followed - * by space will input the corresponding spacing character. The X - * compose tables are different for dead acute and diaeresis, which - * when followed by space produce a plain ASCII apostrophe and double - * quote respectively. So special-case those. - */ - -static gboolean -check_win32_special_cases (GtkIMContextSimple *context_simple, - gint n_compose) -{ - GtkIMContextSimplePrivate *priv = context_simple->priv; - if (n_compose == 2 && - priv->compose_buffer[1] == GDK_KEY_space) - { - gunichar value = 0; - - switch (priv->compose_buffer[0]) - { - case GDK_KEY_dead_acute: - value = 0x00B4; break; - case GDK_KEY_dead_diaeresis: - value = 0x00A8; break; - } - if (value > 0) - { - gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple), value); - priv->compose_buffer[0] = 0; + priv->in_hex_sequence = FALSE; + g_string_set_size (priv->tentative_match, 0); + priv->tentative_match_len = 0; + priv->compose_buffer[0] = 0; - return TRUE; - } - } - return FALSE; + g_signal_emit_by_name (context_simple, "preedit-changed"); + g_signal_emit_by_name (context_simple, "preedit-end"); + g_signal_emit_by_name (context_simple, "commit", str); } static void -check_win32_special_case_after_compact_match (GtkIMContextSimple *context_simple, - gint n_compose, - guint value) -{ - GtkIMContextSimplePrivate *priv = context_simple->priv; - - /* On Windows user expectation is that typing two dead accents will input - * two corresponding spacing accents. - */ - if (n_compose == 2 && - priv->compose_buffer[0] == priv->compose_buffer[1] && - IS_DEAD_KEY (priv->compose_buffer[0])) - { - gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple), value); - } -} - -#endif - -#ifdef GDK_WINDOWING_QUARTZ - -static gboolean -check_quartz_special_cases (GtkIMContextSimple *context_simple, - gint n_compose) -{ - GtkIMContextSimplePrivate *priv = context_simple->priv; - guint value = 0; - - if (n_compose == 2) - { - switch (priv->compose_buffer[0]) - { - case GDK_KEY_dead_doubleacute: - switch (priv->compose_buffer[1]) - { - case GDK_KEY_dead_doubleacute: - case GDK_KEY_space: - value = GDK_KEY_quotedbl; break; - - case 'a': value = GDK_KEY_adiaeresis; break; - case 'A': value = GDK_KEY_Adiaeresis; break; - case 'e': value = GDK_KEY_ediaeresis; break; - case 'E': value = GDK_KEY_Ediaeresis; break; - case 'i': value = GDK_KEY_idiaeresis; break; - case 'I': value = GDK_KEY_Idiaeresis; break; - case 'o': value = GDK_KEY_odiaeresis; break; - case 'O': value = GDK_KEY_Odiaeresis; break; - case 'u': value = GDK_KEY_udiaeresis; break; - case 'U': value = GDK_KEY_Udiaeresis; break; - case 'y': value = GDK_KEY_ydiaeresis; break; - case 'Y': value = GDK_KEY_Ydiaeresis; break; - } - break; - - case GDK_KEY_dead_acute: - switch (priv->compose_buffer[1]) - { - case 'c': value = GDK_KEY_ccedilla; break; - case 'C': value = GDK_KEY_Ccedilla; break; - } - break; - } - } - - if (value > 0) - { - gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple), - gdk_keyval_to_unicode (value)); - priv->compose_buffer[0] = 0; - - return TRUE; - } - - return FALSE; -} - -#endif - -gboolean -gtk_check_compact_table (const GtkComposeTableCompact *table, - guint16 *compose_buffer, - gint n_compose, - gboolean *compose_finish, - gboolean *compose_match, - gunichar *output_char) -{ - gint row_stride; - guint16 *seq_index; - guint16 *seq; - gint i; - gboolean match; - gunichar value; - - if (compose_finish) - *compose_finish = FALSE; - if (compose_match) - *compose_match = FALSE; - if (output_char) - *output_char = 0; - - /* Will never match, if the sequence in the compose buffer is longer - * than the sequences in the table. Further, compare_seq (key, val) - * will overrun val if key is longer than val. - */ - if (n_compose > table->max_seq_len) - return FALSE; - - seq_index = bsearch (compose_buffer, - table->data, - table->n_index_size, - sizeof (guint16) * table->n_index_stride, - compare_seq_index); - - if (!seq_index) - return FALSE; - - if (seq_index && n_compose == 1) - return TRUE; - - seq = NULL; - match = FALSE; - value = 0; - - for (i = n_compose - 1; i < table->max_seq_len; i++) - { - row_stride = i + 1; - - if (seq_index[i + 1] - seq_index[i] > 0) - { - seq = bsearch (compose_buffer + 1, - table->data + seq_index[i], - (seq_index[i + 1] - seq_index[i]) / row_stride, - sizeof (guint16) * row_stride, - compare_seq); - - if (seq) - { - if (i == n_compose - 1) - { - value = seq[row_stride - 1]; - match = TRUE; - } - else - { - if (output_char) - *output_char = value; - if (match) - { - if (compose_match) - *compose_match = TRUE; - } - - return TRUE; - } - } - } - } - - if (match) - { - if (compose_match) - *compose_match = TRUE; - if (compose_finish) - *compose_finish = TRUE; - if (output_char) - *output_char = value; - - return TRUE; - } - - return FALSE; -} - -/* This function receives a sequence of Unicode characters and tries to - * normalize it (NFC). We check for the case where the resulting string - * has length 1 (single character). - * NFC normalisation normally rearranges diacritic marks, unless these - * belong to the same Canonical Combining Class. - * If they belong to the same canonical combining class, we produce all - * permutations of the diacritic marks, then attempt to normalize. - */ -static gboolean -check_normalize_nfc (gunichar* combination_buffer, gint n_compose) +gtk_im_context_simple_commit_char (GtkIMContextSimple *context_simple, + gunichar ch) { - gunichar combination_buffer_temp[GTK_MAX_COMPOSE_LEN]; - gchar *combination_utf8_temp = NULL; - gchar *nfc_temp = NULL; - gint n_combinations; - gunichar temp_swap; - gint i; - - n_combinations = 1; - - for (i = 1; i < n_compose; i++ ) - n_combinations *= i; - - /* Xorg reuses dead_tilde for the perispomeni diacritic mark. - * We check if base character belongs to Greek Unicode block, - * and if so, we replace tilde with perispomeni. - */ - if (combination_buffer[0] >= 0x390 && combination_buffer[0] <= 0x3FF) - { - for (i = 1; i < n_compose; i++ ) - if (combination_buffer[i] == 0x303) - combination_buffer[i] = 0x342; - } - - memcpy (combination_buffer_temp, combination_buffer, GTK_MAX_COMPOSE_LEN * sizeof (gunichar) ); - - for (i = 0; i < n_combinations; i++ ) - { - g_unicode_canonical_ordering (combination_buffer_temp, n_compose); - combination_utf8_temp = g_ucs4_to_utf8 (combination_buffer_temp, -1, NULL, NULL, NULL); - nfc_temp = g_utf8_normalize (combination_utf8_temp, -1, G_NORMALIZE_NFC); - - if (g_utf8_strlen (nfc_temp, -1) == 1) - { - memcpy (combination_buffer, combination_buffer_temp, GTK_MAX_COMPOSE_LEN * sizeof (gunichar) ); - - g_free (combination_utf8_temp); - g_free (nfc_temp); - - return TRUE; - } - - g_free (combination_utf8_temp); - g_free (nfc_temp); - - if (n_compose > 2) - { - temp_swap = combination_buffer_temp[i % (n_compose - 1) + 1]; - combination_buffer_temp[i % (n_compose - 1) + 1] = combination_buffer_temp[(i+1) % (n_compose - 1) + 1]; - combination_buffer_temp[(i+1) % (n_compose - 1) + 1] = temp_swap; - } - else - break; - } - - return FALSE; -} - -gboolean -gtk_check_algorithmically (const guint16 *compose_buffer, - gint n_compose, - gunichar *output_char) - -{ - gint i; - gunichar combination_buffer[GTK_MAX_COMPOSE_LEN]; - gchar *combination_utf8, *nfc; - - if (output_char) - *output_char = 0; - - if (n_compose >= GTK_MAX_COMPOSE_LEN) - return FALSE; - - for (i = 0; i < n_compose && IS_DEAD_KEY (compose_buffer[i]); i++) - ; - if (i == n_compose) - return TRUE; - - if (i > 0 && i == n_compose - 1) - { - combination_buffer[0] = gdk_keyval_to_unicode (compose_buffer[i]); - combination_buffer[n_compose] = 0; - i--; - while (i >= 0) - { - switch (compose_buffer[i]) - { -#define CASE(keysym, unicode) \ - case GDK_KEY_dead_##keysym: combination_buffer[i+1] = unicode; break - - CASE (grave, 0x0300); - CASE (acute, 0x0301); - CASE (circumflex, 0x0302); - CASE (tilde, 0x0303); /* Also used with perispomeni, 0x342. */ - CASE (macron, 0x0304); - CASE (breve, 0x0306); - CASE (abovedot, 0x0307); - CASE (diaeresis, 0x0308); - CASE (hook, 0x0309); - CASE (abovering, 0x030A); - CASE (doubleacute, 0x030B); - CASE (caron, 0x030C); - CASE (abovecomma, 0x0313); /* Equivalent to psili */ - CASE (abovereversedcomma, 0x0314); /* Equivalent to dasia */ - CASE (horn, 0x031B); /* Legacy use for psili, 0x313 (or 0x343). */ - CASE (belowdot, 0x0323); - CASE (cedilla, 0x0327); - CASE (ogonek, 0x0328); /* Legacy use for dasia, 0x314.*/ - CASE (iota, 0x0345); - CASE (voiced_sound, 0x3099); /* Per Markus Kuhn keysyms.txt file. */ - CASE (semivoiced_sound, 0x309A); /* Per Markus Kuhn keysyms.txt file. */ - - /* The following cases are to be removed once xkeyboard-config, - * xorg are fully updated. - */ - /* Workaround for typo in 1.4.x xserver-xorg */ - case 0xfe66: combination_buffer[i+1] = 0x314; break; - /* CASE (dasia, 0x314); */ - /* CASE (perispomeni, 0x342); */ - /* CASE (psili, 0x343); */ -#undef CASE - default: - combination_buffer[i+1] = gdk_keyval_to_unicode (compose_buffer[i]); - } - i--; - } - - /* If the buffer normalizes to a single character, then modify the order - * of combination_buffer accordingly, if necessary, and return TRUE. - */ - if (check_normalize_nfc (combination_buffer, n_compose)) - { - combination_utf8 = g_ucs4_to_utf8 (combination_buffer, -1, NULL, NULL, NULL); - nfc = g_utf8_normalize (combination_utf8, -1, G_NORMALIZE_NFC); - - if (output_char) - *output_char = g_utf8_get_char (nfc); - - g_free (combination_utf8); - g_free (nfc); + char buf[8] = { 0, }; - return TRUE; - } - } + g_unichar_to_utf8 (ch, buf); - return FALSE; + gtk_im_context_simple_commit_string (context_simple, buf); } /* In addition to the table-driven sequences, we allow Unicode hex @@ -808,17 +337,17 @@ gtk_check_algorithmically (const guint16 *compose_buffer, static gboolean check_hex (GtkIMContextSimple *context_simple, - gint n_compose) + int n_compose) { GtkIMContextSimplePrivate *priv = context_simple->priv; /* See if this is a hex sequence, return TRUE if so */ - gint i; + int i; GString *str; gulong n; - gchar *nptr = NULL; - gchar buf[7]; + char *nptr = NULL; + char buf[7]; - priv->tentative_match = 0; + g_string_set_size (priv->tentative_match, 0); priv->tentative_match_len = 0; str = g_string_new (NULL); @@ -858,7 +387,8 @@ check_hex (GtkIMContextSimple *context_simple, if (g_unichar_validate (n)) { - priv->tentative_match = n; + g_string_set_size (priv->tentative_match, 0); + g_string_append_unichar (priv->tentative_match, n); priv->tentative_match_len = n_compose; } @@ -881,30 +411,37 @@ beep_window (GdkWindow *window) static gboolean no_sequence_matches (GtkIMContextSimple *context_simple, - gint n_compose, + int n_compose, GdkEventKey *event) { GtkIMContextSimplePrivate *priv = context_simple->priv; GtkIMContext *context; gunichar ch; - + context = GTK_IM_CONTEXT (context_simple); /* No compose sequences found, check first if we have a partial * match pending. */ - if (priv->tentative_match) + if (priv->tentative_match_len > 0) { - gint len = priv->tentative_match_len; + int len = priv->tentative_match_len; int i; - - gtk_im_context_simple_commit_char (context, priv->tentative_match); - priv->compose_buffer[0] = 0; - - for (i=0; i < n_compose - len - 1; i++) - { - GdkEvent *tmp_event = gdk_event_copy ((GdkEvent *)event); - tmp_event->key.keyval = priv->compose_buffer[len + i]; + guint16 *compose_buffer; + char *str; + + compose_buffer = alloca (sizeof (guint16) * priv->compose_buffer_len); + + memcpy (compose_buffer, priv->compose_buffer, sizeof (guint16) * priv->compose_buffer_len); + + str = g_strdup (priv->tentative_match->str); + gtk_im_context_simple_commit_string (context_simple, str); + g_free (str); + + for (i = 0; i < n_compose - len - 1; i++) + { + GdkEvent *tmp_event = gdk_event_copy ((GdkEvent *)event); + tmp_event->key.keyval = compose_buffer[len + i]; gtk_im_context_filter_keypress (context, (GdkEventKey *)tmp_event); gdk_event_free (tmp_event); @@ -918,18 +455,21 @@ no_sequence_matches (GtkIMContextSimple *context_simple, if (n_compose > 1) /* Invalid sequence */ { beep_window (event->window); + g_signal_emit_by_name (context, "preedit-changed"); + g_signal_emit_by_name (context, "preedit-end"); return TRUE; } ch = gdk_keyval_to_unicode (event->keyval); if (ch != 0 && !g_unichar_iscntrl (ch)) { - gtk_im_context_simple_commit_char (context, ch); + gtk_im_context_simple_commit_char (context_simple, ch); return TRUE; } else return FALSE; } + return FALSE; } static gboolean @@ -946,9 +486,9 @@ canonical_hex_keyval (GdkEventKey *event) GdkKeymap *keymap = gdk_keymap_get_for_display (gdk_window_get_display (event->window)); guint keyval; guint *keyvals = NULL; - gint n_vals = 0; - gint i; - + int n_vals = 0; + int i; + /* See if the keyval is already a hex digit */ if (is_hex_keyval (event->keyval)) return event->keyval; @@ -957,9 +497,9 @@ canonical_hex_keyval (GdkEventKey *event) * any other state, and return that hex keyval if so */ gdk_keymap_get_entries_for_keycode (keymap, - event->hardware_keycode, - NULL, - &keyvals, &n_vals); + event->hardware_keycode, + NULL, + &keyvals, &n_vals); keyval = 0; i = 0; @@ -991,7 +531,7 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context, GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context); GtkIMContextSimplePrivate *priv = context_simple->priv; GdkDisplay *display = gdk_window_get_display (event->window); - GSList *tmp_list; + GSList *tmp_list; int n_compose = 0; GdkModifierType hex_mod_mask; gboolean have_hex_mods; @@ -1005,43 +545,46 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context, gboolean compose_match; gunichar output_char; - while (priv->compose_buffer[n_compose] != 0) + while (priv->compose_buffer[n_compose] != 0 && n_compose < priv->compose_buffer_len) n_compose++; if (event->type == GDK_KEY_RELEASE) { if (priv->in_hex_sequence && - (event->keyval == GDK_KEY_Control_L || event->keyval == GDK_KEY_Control_R || + (event->keyval == GDK_KEY_Control_L || event->keyval == GDK_KEY_Control_R || event->keyval == GDK_KEY_Shift_L || event->keyval == GDK_KEY_Shift_R)) { - if (priv->tentative_match && - g_unichar_validate (priv->tentative_match)) + if (priv->tentative_match->len > 0) { - gtk_im_context_simple_commit_char (context, priv->tentative_match); - priv->compose_buffer[0] = 0; + char *str = g_strdup (priv->tentative_match->str); + gtk_im_context_simple_commit_string (context_simple, str); + g_free (str); + return TRUE; } else if (n_compose == 0) { priv->modifiers_dropped = TRUE; + + return TRUE; } - else + else if (priv->in_hex_sequence) { /* invalid hex sequence */ beep_window (event->window); - - priv->tentative_match = 0; + + g_string_set_size (priv->tentative_match, 0); priv->in_hex_sequence = FALSE; priv->compose_buffer[0] = 0; - + g_signal_emit_by_name (context_simple, "preedit-changed"); g_signal_emit_by_name (context_simple, "preedit-end"); - } - return TRUE; + return TRUE; + } } - else - return FALSE; + + return FALSE; } /* Ignore modifier key presses */ @@ -1049,9 +592,7 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context, if (event->keyval == gtk_compose_ignore[i]) return FALSE; - hex_mod_mask = gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display), - GDK_MODIFIER_INTENT_PRIMARY_ACCELERATOR); - hex_mod_mask |= GDK_SHIFT_MASK; + hex_mod_mask = GDK_CONTROL_MASK|GDK_SHIFT_MASK; if (priv->in_hex_sequence && priv->modifiers_dropped) have_hex_mods = TRUE; @@ -1059,10 +600,10 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context, have_hex_mods = (event->state & (hex_mod_mask)) == hex_mod_mask; is_hex_start = event->keyval == GDK_KEY_U; is_hex_end = (event->keyval == GDK_KEY_space || - event->keyval == GDK_KEY_KP_Space || - event->keyval == GDK_KEY_Return || - event->keyval == GDK_KEY_ISO_Enter || - event->keyval == GDK_KEY_KP_Enter); + event->keyval == GDK_KEY_KP_Space || + event->keyval == GDK_KEY_Return || + event->keyval == GDK_KEY_ISO_Enter || + event->keyval == GDK_KEY_KP_Enter); is_backspace = event->keyval == GDK_KEY_BackSpace; is_escape = event->keyval == GDK_KEY_Escape; hex_keyval = canonical_hex_keyval (event); @@ -1118,22 +659,35 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context, return TRUE; } + if (!priv->in_hex_sequence && n_compose > 0 && is_backspace) + { + n_compose--; + priv->compose_buffer[n_compose] = 0; + + g_signal_emit_by_name (context_simple, "preedit-changed"); + + if (n_compose == 0) + g_signal_emit_by_name (context_simple, "preedit-end"); + + return TRUE; + } + /* Check for hex sequence restart */ if (priv->in_hex_sequence && have_hex_mods && is_hex_start) { - if (priv->tentative_match && - g_unichar_validate (priv->tentative_match)) + if (priv->tentative_match->len > 0) { - gtk_im_context_simple_commit_char (context, priv->tentative_match); - priv->compose_buffer[0] = 0; + char *str = g_strdup (priv->tentative_match->str); + gtk_im_context_simple_commit_string (context_simple, str); + g_free (str); } - else + else { /* invalid hex sequence */ if (n_compose > 0) beep_window (event->window); - - priv->tentative_match = 0; + + g_string_set_size (priv->tentative_match, 0); priv->in_hex_sequence = FALSE; priv->compose_buffer[0] = 0; } @@ -1145,7 +699,7 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context, priv->compose_buffer[0] = 0; priv->in_hex_sequence = TRUE; priv->modifiers_dropped = FALSE; - priv->tentative_match = 0; + g_string_set_size (priv->tentative_match, 0); g_signal_emit_by_name (context_simple, "preedit-start"); g_signal_emit_by_name (context_simple, "preedit-changed"); @@ -1156,24 +710,30 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context, /* Then, check for compose sequences */ if (priv->in_hex_sequence) { - if (hex_keyval) + if (hex_keyval && n_compose < 6) priv->compose_buffer[n_compose++] = hex_keyval; else if (is_escape) { gtk_im_context_simple_reset (context); - return TRUE; } else if (!is_hex_end) { - /* non-hex character in hex sequence */ + /* non-hex character in hex sequence, or sequence too long */ beep_window (event->window); - return TRUE; } } else - priv->compose_buffer[n_compose++] = event->keyval; + { + if (n_compose + 1 == priv->compose_buffer_len) + { + priv->compose_buffer_len += 1; + priv->compose_buffer = g_renew (guint16, priv->compose_buffer, priv->compose_buffer_len); + } + + priv->compose_buffer[n_compose++] = event->keyval; + } priv->compose_buffer[n_compose] = 0; @@ -1185,25 +745,25 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context, /* space or return ends the sequence, and we eat the key */ if (n_compose > 0 && is_hex_end) { - if (priv->tentative_match && - g_unichar_validate (priv->tentative_match)) + if (priv->tentative_match->len > 0) { - gtk_im_context_simple_commit_char (context, priv->tentative_match); - priv->compose_buffer[0] = 0; + char *str = g_strdup (priv->tentative_match->str); + gtk_im_context_simple_commit_string (context_simple, str); + g_free (str); } else { /* invalid hex sequence */ beep_window (event->window); - priv->tentative_match = 0; + g_string_set_size (priv->tentative_match, 0); priv->in_hex_sequence = FALSE; priv->compose_buffer[0] = 0; } } else if (!check_hex (context_simple, n_compose)) beep_window (event->window); - + g_signal_emit_by_name (context_simple, "preedit-changed"); if (!priv->in_hex_sequence) @@ -1215,109 +775,81 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context, else { gboolean success = FALSE; + GString *output; -#ifdef GDK_WINDOWING_WIN32 - if (GDK_IS_WIN32_DISPLAY (display)) - { - guint16 output[2]; - gsize output_size = 2; - - switch (gdk_win32_keymap_check_compose (GDK_WIN32_KEYMAP (gdk_keymap_get_default ()), - priv->compose_buffer, - n_compose, - output, &output_size)) - { - case GDK_WIN32_KEYMAP_MATCH_NONE: - break; - case GDK_WIN32_KEYMAP_MATCH_EXACT: - case GDK_WIN32_KEYMAP_MATCH_PARTIAL: - for (i = 0; i < output_size; i++) - { - output_char = gdk_keyval_to_unicode (output[i]); - gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple), - output_char); - } - priv->compose_buffer[0] = 0; - return TRUE; - case GDK_WIN32_KEYMAP_MATCH_INCOMPLETE: - return TRUE; - } - } -#endif + output = g_string_new (""); G_LOCK (global_tables); tmp_list = global_tables; while (tmp_list) { - if (check_table (context_simple, tmp_list->data, n_compose)) + if (gtk_compose_table_check ((GtkComposeTable *)tmp_list->data, + priv->compose_buffer, n_compose, + &compose_finish, &compose_match, + output)) { + if (compose_finish) + { + if (compose_match) + gtk_im_context_simple_commit_string (context_simple, output->str); + } + else + { + if (compose_match) + { + g_string_assign (priv->tentative_match, output->str); + priv->tentative_match_len = n_compose; + } + g_signal_emit_by_name (context_simple, "preedit-changed"); + } + success = TRUE; break; } + tmp_list = tmp_list->next; } G_UNLOCK (global_tables); - if (success) - return TRUE; - -#ifdef GDK_WINDOWING_WIN32 - if (check_win32_special_cases (context_simple, n_compose)) - return TRUE; -#endif + g_string_free (output, TRUE); -#ifdef GDK_WINDOWING_QUARTZ - if (check_quartz_special_cases (context_simple, n_compose)) + if (success) return TRUE; -#endif - if (gtk_check_compact_table (>k_compose_table_compact, - priv->compose_buffer, - n_compose, &compose_finish, - &compose_match, &output_char)) + if (gtk_compose_table_compact_check (>k_compose_table_compact, + priv->compose_buffer, n_compose, + &compose_finish, &compose_match, + &output_char)) { if (compose_finish) { if (compose_match) - { - gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple), - output_char); -#ifdef G_OS_WIN32 - check_win32_special_case_after_compact_match (context_simple, - n_compose, - output_char); -#endif - priv->compose_buffer[0] = 0; - } + gtk_im_context_simple_commit_char (context_simple, output_char); } else { if (compose_match) { - priv->tentative_match = output_char; + g_string_set_size (priv->tentative_match, 0); + g_string_append_unichar (priv->tentative_match, output_char); priv->tentative_match_len = n_compose; } - if (output_char) - g_signal_emit_by_name (context_simple, "preedit-changed"); + g_signal_emit_by_name (context_simple, "preedit-changed"); } return TRUE; } - + if (gtk_check_algorithmically (priv->compose_buffer, n_compose, &output_char)) { if (output_char) - { - gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple), - output_char); - priv->compose_buffer[0] = 0; - } - return TRUE; + gtk_im_context_simple_commit_char (context_simple, output_char); + return TRUE; } } - + /* The current compose_buffer doesn't match anything */ return no_sequence_matches (context_simple, n_compose, event); } @@ -1330,90 +862,69 @@ gtk_im_context_simple_reset (GtkIMContext *context) priv->compose_buffer[0] = 0; - if (priv->tentative_match || priv->in_hex_sequence) + if (priv->tentative_match->len > 0 || priv->in_hex_sequence) { priv->in_hex_sequence = FALSE; - priv->tentative_match = 0; + g_string_set_size (priv->tentative_match, 0); priv->tentative_match_len = 0; g_signal_emit_by_name (context_simple, "preedit-changed"); g_signal_emit_by_name (context_simple, "preedit-end"); } } -static void +static void gtk_im_context_simple_get_preedit_string (GtkIMContext *context, - gchar **str, - PangoAttrList **attrs, - gint *cursor_pos) + char **str, + PangoAttrList **attrs, + int *cursor_pos) { GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context); GtkIMContextSimplePrivate *priv = context_simple->priv; - char outbuf[37]; /* up to 6 hex digits */ - int len = 0; + GString *s; + int i; + + s = g_string_new (""); if (priv->in_hex_sequence) { - int hexchars = 0; - - outbuf[0] = 'u'; - len = 1; + g_string_append_c (s, 'u'); - while (priv->compose_buffer[hexchars] != 0) - { - len += g_unichar_to_utf8 (gdk_keyval_to_unicode (priv->compose_buffer[hexchars]), - outbuf + len); - ++hexchars; - } - - g_assert (len < 25); + for (i = 0; priv->compose_buffer[i]; i++) + g_string_append_unichar (s, gdk_keyval_to_unicode (priv->compose_buffer[i])); } - else if (priv->tentative_match) - len = g_unichar_to_utf8 (priv->tentative_match, outbuf); - - outbuf[len] = '\0'; - - if (str) - *str = g_strdup (outbuf); - - if (attrs) + else if (priv->tentative_match->len > 0 && priv->compose_buffer[0] != 0) { - *attrs = pango_attr_list_new (); - - if (len) - { - PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE); - attr->start_index = 0; - attr->end_index = len; - pango_attr_list_insert (*attrs, attr); - } + g_string_append (s, priv->tentative_match->str); + } + else + { + for (i = 0; priv->compose_buffer[i]; i++) + { + if (priv->compose_buffer[i] == GDK_KEY_Multi_key) + g_string_append_unichar (s, 0x2384); /* U+2384 COMPOSITION SYMBOL */ + else + g_string_append_unichar (s, gdk_keyval_to_unicode (priv->compose_buffer[i])); + } } if (cursor_pos) - *cursor_pos = len; -} + *cursor_pos = s->len; -static void -gtk_im_context_simple_set_client_window (GtkIMContext *context, - GdkWindow *window) -{ - GtkIMContextSimple *im_context_simple = GTK_IM_CONTEXT_SIMPLE (context); - gboolean run_compose_table = FALSE; - - if (!window) - return; + if (attrs) + { + *attrs = pango_attr_list_new (); - /* Load compose table for X11 or Wayland. */ -#ifdef GDK_WINDOWING_X11 - if (GDK_IS_X11_DISPLAY (gdk_window_get_display (window))) - run_compose_table = TRUE; -#endif -#ifdef GDK_WINDOWING_WAYLAND - if (GDK_IS_WAYLAND_DISPLAY (gdk_window_get_display (window))) - run_compose_table = TRUE; -#endif + if (s->len) + { + PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE); + attr->start_index = 0; + attr->end_index = s->len; + pango_attr_list_insert (*attrs, attr); + } + } - if (run_compose_table) - init_compose_table_async (im_context_simple, NULL, NULL, NULL); + if (str) + *str = g_string_free (s, FALSE); } /** @@ -1421,9 +932,8 @@ gtk_im_context_simple_set_client_window (GtkIMContext *context, * @context_simple: A #GtkIMContextSimple * @data: (array): the table * @max_seq_len: Maximum length of a sequence in the table - * (cannot be greater than #GTK_MAX_COMPOSE_LEN) * @n_seqs: number of sequences in the table - * + * * Adds an additional table to search to the input context. * Each row of the table consists of @max_seq_len key symbols * followed by two #guint16 interpreted as the high and low @@ -1437,8 +947,8 @@ gtk_im_context_simple_set_client_window (GtkIMContext *context, void gtk_im_context_simple_add_table (GtkIMContextSimple *context_simple, guint16 *data, - gint max_seq_len, - gint n_seqs) + int max_seq_len, + int n_seqs) { g_return_if_fail (GTK_IS_IM_CONTEXT_SIMPLE (context_simple)); @@ -1450,25 +960,22 @@ gtk_im_context_simple_add_table (GtkIMContextSimple *context_simple, G_UNLOCK (global_tables); } -/* +/** * gtk_im_context_simple_add_compose_file: * @context_simple: A #GtkIMContextSimple * @compose_file: The path of compose file * * Adds an additional table from the X11 compose file. - * - * Since: 3.20 */ void gtk_im_context_simple_add_compose_file (GtkIMContextSimple *context_simple, - const gchar *compose_file) + const char *compose_file) { g_return_if_fail (GTK_IS_IM_CONTEXT_SIMPLE (context_simple)); G_LOCK (global_tables); - global_tables = gtk_compose_table_list_add_file (global_tables, - compose_file); + global_tables = gtk_compose_table_list_add_file (global_tables, compose_file); G_UNLOCK (global_tables); } diff --git a/gtk/gtkimcontextsimpleprivate.h b/gtk/gtkimcontextsimpleprivate.h deleted file mode 100644 index 401156a84e..0000000000 --- a/gtk/gtkimcontextsimpleprivate.h +++ /dev/null @@ -1,42 +0,0 @@ -/* GTK - The GIMP Toolkit - * Copyright (C) 2000 Red Hat Software - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef __GTK_IM_CONTEXT_SIMPLE_PRIVATE_H__ -#define __GTK_IM_CONTEXT_SIMPLE_PRIVATE_H__ - -#include <glib.h> - -#include "gdk/gdkkeysyms.h" - -G_BEGIN_DECLS - -extern const GtkComposeTableCompact gtk_compose_table_compact; - -gboolean gtk_check_algorithmically (const guint16 *compose_buffer, - gint n_compose, - gunichar *output); -gboolean gtk_check_compact_table (const GtkComposeTableCompact *table, - guint16 *compose_buffer, - gint n_compose, - gboolean *compose_finish, - gboolean *compose_match, - gunichar *output_char); - -G_END_DECLS - - -#endif /* __GTK_IM_CONTEXT_SIMPLE_PRIVATE_H__ */ |