summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2021-02-08 20:28:49 -0500
committerMatthias Clasen <mclasen@redhat.com>2021-02-08 20:28:49 -0500
commit85d34932f3888caecd4b50dcfce530c8b82a7a5b (patch)
tree63693429b70d737a45f553a9beb7e4b45ea83dfa
parenta0818f5bae2675f6cbca8b16256a63728036fdd5 (diff)
downloadgtk+-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.c823
-rw-r--r--gtk/gtkcomposetable.h45
-rw-r--r--gtk/gtkimcontextsimple.c1007
-rw-r--r--gtk/gtkimcontextsimpleprivate.h42
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 (&gtk_compose_table_compact,
- keysyms,
- n_compose,
- &compose_finish,
- NULL,
- &output_char) &&
+ if (gtk_compose_table_compact_check (&gtk_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 (&gtk_compose_table_compact,
- priv->compose_buffer,
- n_compose, &compose_finish,
- &compose_match, &output_char))
+ if (gtk_compose_table_compact_check (&gtk_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__ */