summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gtk/gtkimcontextsimple.c222
1 files changed, 187 insertions, 35 deletions
diff --git a/gtk/gtkimcontextsimple.c b/gtk/gtkimcontextsimple.c
index 5404f0a1ef..f843b4d8d9 100644
--- a/gtk/gtkimcontextsimple.c
+++ b/gtk/gtkimcontextsimple.c
@@ -72,6 +72,7 @@ struct _GtkIMContextSimplePrivate
gint tentative_match_len;
guint in_hex_sequence : 1;
+ guint in_emoji_sequence : 1;
guint modifiers_dropped : 1;
};
@@ -295,9 +296,10 @@ gtk_im_context_simple_commit_char (GtkIMContext *context,
len = g_unichar_to_utf8 (ch, buf);
buf[len] = '\0';
- if (priv->tentative_match || priv->in_hex_sequence)
+ if (priv->tentative_match || priv->in_hex_sequence || priv->in_emoji_sequence)
{
priv->in_hex_sequence = FALSE;
+ priv->in_emoji_sequence = FALSE;
priv->tentative_match = 0;
priv->tentative_match_len = 0;
g_signal_emit_by_name (context_simple, "preedit-changed");
@@ -865,6 +867,104 @@ check_hex (GtkIMContextSimple *context_simple,
return TRUE;
}
+typedef struct {
+ const char *name;
+ gunichar ch;
+} EmojiItem;
+
+static EmojiItem emoji[] = {
+ { ":-)", 0x1f642 },
+ { "8-)", 0x1f60d },
+ { "<3", 0x02764 },
+ { "kiss", 0x1f48b },
+ { "grin", 0x1f601 },
+ { "joy", 0x1f602 },
+ { ":-*", 0x1f61a },
+ { "xD", 0x1f606 },
+ { "like", 0x1f44d },
+ { "dislike", 0x1f44e },
+ { "up", 0x1f446 },
+ { "v", 0x0270c },
+ { "ok", 0x1f44c },
+ { "B-)", 0x1f60e },
+ { ":-D", 0x1f603 },
+ { ";-)", 0x1f609 },
+ { ";-P", 0x1f61c },
+ { ":-p", 0x1f60b },
+ { "3(", 0x1f614 },
+ { ":-(", 0x1f61e },
+ { ":]", 0x1f60f },
+ { ":'(", 0x1f622 },
+ { ":_(", 0x1f62d },
+ { ":((", 0x1f629 },
+ { ":o", 0x1f628 },
+ { ":|", 0x1f610 },
+ { "3-)", 0x1f60c },
+ { ">(", 0x1f620 },
+ { ">((", 0x1f621 },
+ { "O:)", 0x1f607 },
+ { ";o", 0x1f630 },
+ { "8|", 0x1f633 },
+ { "8o", 0x1f632 },
+ { ":X", 0x1f637 },
+ { "}:)", 0x1f608 },
+ { NULL, 0 }
+};
+
+static gboolean
+check_emoji (GtkIMContextSimple *context_simple,
+ gint n_compose)
+{
+ GtkIMContextSimplePrivate *priv = context_simple->priv;
+ GString *str;
+ gint i;
+ gchar buf[7];
+ char *lower;
+
+ priv->tentative_match = 0;
+ priv->tentative_match_len = 0;
+
+ str = g_string_new (NULL);
+
+ i = 0;
+ while (i < n_compose)
+ {
+ gunichar ch;
+
+ ch = gdk_keyval_to_unicode (priv->compose_buffer[i]);
+
+ if (ch == 0)
+ return FALSE;
+
+ if (priv->in_hex_sequence && !g_unichar_isxdigit (ch))
+ return FALSE;
+
+ buf[g_unichar_to_utf8 (ch, buf)] = '\0';
+
+ g_string_append (str, buf);
+
+ ++i;
+ }
+
+ lower = g_utf8_strdown (str->str, str->len);
+
+ for (i = 0; emoji[i].name; i++)
+ {
+ if (strcmp (str->str, emoji[i].name) == 0 ||
+ strcmp (lower, emoji[i].name) == 0)
+ {
+ priv->tentative_match = emoji[i].ch;
+ priv->tentative_match_len = n_compose;
+ break;
+ }
+ }
+
+ g_string_free (str, TRUE);
+ g_free (lower);
+
+ return priv->tentative_match != 0;
+}
+
static void
beep_window (GdkWindow *window)
{
@@ -984,6 +1084,12 @@ canonical_hex_keyval (GdkEventKey *event)
return 0;
}
+static guint
+canonical_emoji_keyval (GdkEventKey *event)
+{
+ return event->keyval;
+}
+
static gboolean
gtk_im_context_simple_filter_keypress (GtkIMContext *context,
GdkEventKey *event)
@@ -991,37 +1097,41 @@ 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;
gboolean is_hex_start;
- gboolean is_hex_end;
+ gboolean is_end;
+ gboolean is_emoji_start;
gboolean is_backspace;
gboolean is_escape;
guint hex_keyval;
+ guint emoji_keyval;
int i;
gboolean compose_finish;
gboolean compose_match;
gunichar output_char;
+ GtkInputPurpose purpose;
while (priv->compose_buffer[n_compose] != 0)
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 ||
+ if ((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 &&
+ if ((priv->in_hex_sequence || priv->in_emoji_sequence) &&
+ priv->tentative_match &&
g_unichar_validate (priv->tentative_match))
{
gtk_im_context_simple_commit_char (context, priv->tentative_match);
priv->compose_buffer[0] = 0;
}
- else if (n_compose == 0)
+ else if (priv->in_emoji_sequence ||
+ (priv->in_hex_sequence && n_compose == 0))
{
priv->modifiers_dropped = TRUE;
}
@@ -1029,11 +1139,12 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
{
/* invalid hex sequence */
beep_window (event->window);
-
+
priv->tentative_match = 0;
priv->in_hex_sequence = FALSE;
+ priv->in_emoji_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");
}
@@ -1053,19 +1164,25 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
GDK_MODIFIER_INTENT_PRIMARY_ACCELERATOR);
hex_mod_mask |= GDK_SHIFT_MASK;
- if (priv->in_hex_sequence && priv->modifiers_dropped)
+ if ((priv->in_hex_sequence || priv->in_emoji_sequence) && priv->modifiers_dropped)
have_hex_mods = TRUE;
else
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);
+ g_object_get (context, "input-purpose", &purpose, NULL);
+ if (purpose == GTK_INPUT_PURPOSE_FREE_FORM)
+ is_emoji_start = event->keyval == GDK_KEY_E;
+ else
+ is_emoji_start = FALSE;
+ is_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);
is_backspace = event->keyval == GDK_KEY_BackSpace;
is_escape = event->keyval == GDK_KEY_Escape;
hex_keyval = canonical_hex_keyval (event);
+ emoji_keyval = canonical_emoji_keyval (event);
/* If we are already in a non-hex sequence, or
* this keystroke is not hex modifiers + hex digit, don't filter
@@ -1075,10 +1192,11 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
* ISO_Level3_Switch.
*/
if (!have_hex_mods ||
- (n_compose > 0 && !priv->in_hex_sequence) ||
- (n_compose == 0 && !priv->in_hex_sequence && !is_hex_start) ||
+ (n_compose > 0 && !priv->in_hex_sequence && !priv->in_emoji_sequence) ||
+ (n_compose == 0 && !priv->in_hex_sequence && !priv->in_emoji_sequence &&
+ !is_hex_start && !is_emoji_start) ||
(priv->in_hex_sequence && !hex_keyval &&
- !is_hex_start && !is_hex_end && !is_escape && !is_backspace))
+ !is_hex_start && !is_end && !is_escape && !is_backspace))
{
GdkModifierType no_text_input_mask;
@@ -1087,7 +1205,7 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
GDK_MODIFIER_INTENT_NO_TEXT_INPUT);
if (event->state & no_text_input_mask ||
- (priv->in_hex_sequence && priv->modifiers_dropped &&
+ ((priv->in_hex_sequence || priv->in_emoji_sequence) && priv->modifiers_dropped &&
(event->keyval == GDK_KEY_Return ||
event->keyval == GDK_KEY_ISO_Enter ||
event->keyval == GDK_KEY_KP_Enter)))
@@ -1097,17 +1215,21 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
}
/* Handle backspace */
- if (priv->in_hex_sequence && have_hex_mods && is_backspace)
+ if ((priv->in_hex_sequence || priv->in_emoji_sequence) && have_hex_mods && is_backspace)
{
if (n_compose > 0)
{
n_compose--;
priv->compose_buffer[n_compose] = 0;
- check_hex (context_simple, n_compose);
+ if (priv->in_hex_sequence)
+ check_hex (context_simple, n_compose);
+ else if (priv->in_emoji_sequence)
+ check_emoji (context_simple, n_compose);
}
else
{
priv->in_hex_sequence = FALSE;
+ priv->in_emoji_sequence = FALSE;
}
g_signal_emit_by_name (context_simple, "preedit-changed");
@@ -1127,7 +1249,7 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
gtk_im_context_simple_commit_char (context, priv->tentative_match);
priv->compose_buffer[0] = 0;
}
- else
+ else
{
/* invalid hex sequence */
if (n_compose > 0)
@@ -1153,6 +1275,20 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
return TRUE;
}
+ /* Check for emoji sequence start */
+ if (!priv->in_emoji_sequence && have_hex_mods && is_emoji_start)
+ {
+ priv->compose_buffer[0] = 0;
+ priv->in_emoji_sequence = TRUE;
+ priv->modifiers_dropped = FALSE;
+ priv->tentative_match = 0;
+
+ g_signal_emit_by_name (context_simple, "preedit-start");
+ g_signal_emit_by_name (context_simple, "preedit-changed");
+
+ return TRUE;
+ }
+
/* Then, check for compose sequences */
if (priv->in_hex_sequence)
{
@@ -1161,29 +1297,42 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
else if (is_escape)
{
gtk_im_context_simple_reset (context);
-
return TRUE;
}
- else if (!is_hex_end)
+ else if (!is_end)
{
/* non-hex character in hex sequence */
beep_window (event->window);
-
return TRUE;
}
}
+ else if (priv->in_emoji_sequence)
+ {
+ if (emoji_keyval)
+ priv->compose_buffer[n_compose++] = emoji_keyval;
+ else if (is_escape)
+ {
+ gtk_im_context_simple_reset (context);
+ return TRUE;
+ }
+ else
+ {
+ beep_window (event->window);
+ return TRUE;
+ }
+ }
else
priv->compose_buffer[n_compose++] = event->keyval;
priv->compose_buffer[n_compose] = 0;
- if (priv->in_hex_sequence)
+ if (priv->in_hex_sequence || priv->in_emoji_sequence)
{
/* If the modifiers are still held down, consider the sequence again */
if (have_hex_mods)
{
/* space or return ends the sequence, and we eat the key */
- if (n_compose > 0 && is_hex_end)
+ if (n_compose > 0 && is_end)
{
if (priv->tentative_match &&
g_unichar_validate (priv->tentative_match))
@@ -1198,15 +1347,17 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
priv->tentative_match = 0;
priv->in_hex_sequence = FALSE;
+ priv->in_emoji_sequence = FALSE;
priv->compose_buffer[0] = 0;
}
}
- else if (!check_hex (context_simple, n_compose))
+ else if ((priv->in_hex_sequence && !check_hex (context_simple, n_compose)) ||
+ (priv->in_emoji_sequence && !check_emoji (context_simple, n_compose)))
beep_window (event->window);
-
+
g_signal_emit_by_name (context_simple, "preedit-changed");
- if (!priv->in_hex_sequence)
+ if (!priv->in_hex_sequence && !priv->in_emoji_sequence)
g_signal_emit_by_name (context_simple, "preedit-end");
return TRUE;
@@ -1327,9 +1478,10 @@ gtk_im_context_simple_reset (GtkIMContext *context)
priv->compose_buffer[0] = 0;
- if (priv->tentative_match || priv->in_hex_sequence)
+ if (priv->tentative_match || priv->in_hex_sequence || priv->in_emoji_sequence)
{
priv->in_hex_sequence = FALSE;
+ priv->in_emoji_sequence = FALSE;
priv->tentative_match = 0;
priv->tentative_match_len = 0;
g_signal_emit_by_name (context_simple, "preedit-changed");
@@ -1348,11 +1500,11 @@ gtk_im_context_simple_get_preedit_string (GtkIMContext *context,
char outbuf[37]; /* up to 6 hex digits */
int len = 0;
- if (priv->in_hex_sequence)
+ if (priv->in_hex_sequence || priv->in_emoji_sequence)
{
int hexchars = 0;
- outbuf[0] = 'u';
+ outbuf[0] = priv->in_hex_sequence ? 'u' : 'e';
len = 1;
while (priv->compose_buffer[hexchars] != 0)
@@ -1366,8 +1518,8 @@ gtk_im_context_simple_get_preedit_string (GtkIMContext *context,
}
else if (priv->tentative_match)
len = g_unichar_to_utf8 (priv->tentative_match, outbuf);
-
- outbuf[len] = '\0';
+
+ outbuf[len] = '\0';
if (str)
*str = g_strdup (outbuf);