summaryrefslogtreecommitdiff
path: root/src/mbyte.c
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2017-08-30 22:00:20 +0200
committerBram Moolenaar <Bram@vim.org>2017-08-30 22:00:20 +0200
commit5c6dbcb03fa552d7b0e61c8fcf425147eb6bf7d5 (patch)
tree13f37aa604997567c63b0e5786ee2625282d8d4b /src/mbyte.c
parent4e83961985abb78757b135f29ac4ffde675247af (diff)
downloadvim-git-5c6dbcb03fa552d7b0e61c8fcf425147eb6bf7d5.tar.gz
patch 8.0.1026: GTK on-the-spot input has problemsv8.0.1026
Problem: GTK on-the-spot input has problems. (Gerd Wachsmuth) Solution: Support over-the-spot. (Yukihiro Nakadaira, Ketn Takata, closes #1215)
Diffstat (limited to 'src/mbyte.c')
-rw-r--r--src/mbyte.c340
1 files changed, 238 insertions, 102 deletions
diff --git a/src/mbyte.c b/src/mbyte.c
index ece5c8af8..617a97d0e 100644
--- a/src/mbyte.c
+++ b/src/mbyte.c
@@ -4788,6 +4788,11 @@ static unsigned long im_commit_handler_id = 0;
static unsigned int im_activatekey_keyval = GDK_VoidSymbol;
static unsigned int im_activatekey_state = 0;
+static GtkWidget *preedit_window = NULL;
+static GtkWidget *preedit_label = NULL;
+
+static void im_preedit_window_set_position(void);
+
void
im_set_active(int active)
{
@@ -4825,6 +4830,9 @@ im_set_position(int row, int col)
area.height = gui.char_height;
gtk_im_context_set_cursor_location(xic, &area);
+
+ if (p_imst == IM_OVER_THE_SPOT)
+ im_preedit_window_set_position();
}
}
@@ -4855,12 +4863,107 @@ im_add_to_input(char_u *str, int len)
gui_mch_mousehide(TRUE);
}
+ static void
+im_preedit_window_set_position(void)
+{
+ int x, y, w, h, sw, sh;
+
+ if (preedit_window == NULL)
+ return;
+
+ sw = gdk_screen_get_width(gtk_widget_get_screen(preedit_window));
+ sh = gdk_screen_get_height(gtk_widget_get_screen(preedit_window));
+#if GTK_CHECK_VERSION(3,0,0)
+ gdk_window_get_origin(gtk_widget_get_window(gui.drawarea), &x, &y);
+#else
+ gdk_window_get_origin(gui.drawarea->window, &x, &y);
+#endif
+ gtk_window_get_size(GTK_WINDOW(preedit_window), &w, &h);
+ x = x + FILL_X(gui.col);
+ y = y + FILL_Y(gui.row);
+ if (x + w > sw)
+ x = sw - w;
+ if (y + h > sh)
+ y = sh - h;
+ gtk_window_move(GTK_WINDOW(preedit_window), x, y);
+}
+
+ static void
+im_preedit_window_open()
+{
+ char *preedit_string;
+ char buf[8];
+ PangoAttrList *attr_list;
+ PangoLayout *layout;
+ GdkColor color;
+ gint w, h;
+
+ if (preedit_window == NULL)
+ {
+ preedit_window = gtk_window_new(GTK_WINDOW_POPUP);
+ preedit_label = gtk_label_new("");
+ gtk_container_add(GTK_CONTAINER(preedit_window), preedit_label);
+ }
+
+ gtk_widget_modify_font(preedit_label, gui.norm_font);
+
+ vim_snprintf(buf, sizeof(buf), "#%06X", gui.norm_pixel);
+ gdk_color_parse(buf, &color);
+ gtk_widget_modify_fg(preedit_label, GTK_STATE_NORMAL, &color);
+
+ vim_snprintf(buf, sizeof(buf), "#%06X", gui.back_pixel);
+ gdk_color_parse(buf, &color);
+ gtk_widget_modify_bg(preedit_window, GTK_STATE_NORMAL, &color);
+
+ gtk_im_context_get_preedit_string(xic, &preedit_string, &attr_list, NULL);
+
+ if (preedit_string[0] != NUL)
+ {
+ gtk_label_set_text(GTK_LABEL(preedit_label), preedit_string);
+ gtk_label_set_attributes(GTK_LABEL(preedit_label), attr_list);
+
+ layout = gtk_label_get_layout(GTK_LABEL(preedit_label));
+ pango_layout_get_pixel_size(layout, &w, &h);
+ h = MAX(h, gui.char_height);
+ gtk_window_resize(GTK_WINDOW(preedit_window), w, h);
+
+ gtk_widget_show_all(preedit_window);
+
+ im_preedit_window_set_position();
+ }
+
+ g_free(preedit_string);
+ pango_attr_list_unref(attr_list);
+}
+
+ static void
+im_preedit_window_close()
+{
+ if (preedit_window != NULL)
+ gtk_widget_hide(preedit_window);
+}
+
+ static void
+im_show_preedit()
+{
+ im_preedit_window_open();
+
+ if (p_mh) /* blank out the pointer if necessary */
+ gui_mch_mousehide(TRUE);
+}
+
static void
im_delete_preedit(void)
{
char_u bskey[] = {CSI, 'k', 'b'};
char_u delkey[] = {CSI, 'k', 'D'};
+ if (p_imst == IM_OVER_THE_SPOT)
+ {
+ im_preedit_window_close();
+ return;
+ }
+
if (State & NORMAL)
{
im_preedit_cursor = 0;
@@ -4933,39 +5036,42 @@ im_commit_cb(GtkIMContext *context UNUSED,
xim_log("im_commit_cb(): %s\n", str);
#endif
- /* The imhangul module doesn't reset the preedit string before
- * committing. Call im_delete_preedit() to work around that. */
- im_delete_preedit();
-
- /* Indicate that preediting has finished. */
- if (preedit_start_col == MAXCOL)
+ if (p_imst == IM_ON_THE_SPOT)
{
- init_preedit_start_col();
- commit_with_preedit = FALSE;
- }
+ /* The imhangul module doesn't reset the preedit string before
+ * committing. Call im_delete_preedit() to work around that. */
+ im_delete_preedit();
- /* The thing which setting "preedit_start_col" to MAXCOL means that
- * "preedit_start_col" will be set forcedly when calling
- * preedit_changed_cb() next time.
- * "preedit_start_col" should not reset with MAXCOL on this part. Vim
- * is simulating the preediting by using add_to_input_str(). when
- * preedit begin immediately before committed, the typebuf is not
- * flushed to screen, then it can't get correct "preedit_start_col".
- * Thus, it should calculate the cells by adding cells of the committed
- * string. */
- if (input_conv.vc_type != CONV_NONE)
- {
- im_str = string_convert(&input_conv, (char_u *)str, &len);
- g_return_if_fail(im_str != NULL);
- }
- else
- im_str = (char_u *)str;
+ /* Indicate that preediting has finished. */
+ if (preedit_start_col == MAXCOL)
+ {
+ init_preedit_start_col();
+ commit_with_preedit = FALSE;
+ }
- clen = mb_string2cells(im_str, len);
+ /* The thing which setting "preedit_start_col" to MAXCOL means that
+ * "preedit_start_col" will be set forcedly when calling
+ * preedit_changed_cb() next time.
+ * "preedit_start_col" should not reset with MAXCOL on this part. Vim
+ * is simulating the preediting by using add_to_input_str(). when
+ * preedit begin immediately before committed, the typebuf is not
+ * flushed to screen, then it can't get correct "preedit_start_col".
+ * Thus, it should calculate the cells by adding cells of the committed
+ * string. */
+ if (input_conv.vc_type != CONV_NONE)
+ {
+ im_str = string_convert(&input_conv, (char_u *)str, &len);
+ g_return_if_fail(im_str != NULL);
+ }
+ else
+ im_str = (char_u *)str;
- if (input_conv.vc_type != CONV_NONE)
- vim_free(im_str);
- preedit_start_col += clen;
+ clen = mb_string2cells(im_str, len);
+
+ if (input_conv.vc_type != CONV_NONE)
+ vim_free(im_str);
+ preedit_start_col += clen;
+ }
/* Is this a single character that matches a keypad key that's just
* been pressed? If so, we don't want it to be entered as such - let
@@ -4990,14 +5096,17 @@ im_commit_cb(GtkIMContext *context UNUSED,
if (add_to_input)
im_add_to_input((char_u *)str, slen);
- /* Inserting chars while "im_is_active" is set does not cause a change of
- * buffer. When the chars are committed the buffer must be marked as
- * changed. */
- if (!commit_with_preedit)
- preedit_start_col = MAXCOL;
-
- /* This flag is used in changed() at next call. */
- xim_changed_while_preediting = TRUE;
+ if (p_imst == IM_ON_THE_SPOT)
+ {
+ /* Inserting chars while "im_is_active" is set does not cause a
+ * change of buffer. When the chars are committed the buffer must be
+ * marked as changed. */
+ if (!commit_with_preedit)
+ preedit_start_col = MAXCOL;
+
+ /* This flag is used in changed() at next call. */
+ xim_changed_while_preediting = TRUE;
+ }
if (gtk_main_level() > 0)
gtk_main_quit();
@@ -5031,7 +5140,8 @@ im_preedit_end_cb(GtkIMContext *context UNUSED, gpointer data UNUSED)
im_delete_preedit();
/* Indicate that preediting has finished */
- preedit_start_col = MAXCOL;
+ if (p_imst == IM_ON_THE_SPOT)
+ preedit_start_col = MAXCOL;
xim_has_preediting = FALSE;
#if 0
@@ -5092,9 +5202,14 @@ im_preedit_changed_cb(GtkIMContext *context, gpointer data UNUSED)
char_u *p;
int i;
- gtk_im_context_get_preedit_string(context,
- &preedit_string, NULL,
- &cursor_index);
+ if (p_imst == IM_ON_THE_SPOT)
+ gtk_im_context_get_preedit_string(context,
+ &preedit_string, NULL,
+ &cursor_index);
+ else
+ gtk_im_context_get_preedit_string(context,
+ &preedit_string, NULL,
+ NULL);
#ifdef XIM_DEBUG
xim_log("im_preedit_changed_cb(): %s\n", preedit_string);
@@ -5102,66 +5217,82 @@ im_preedit_changed_cb(GtkIMContext *context, gpointer data UNUSED)
g_return_if_fail(preedit_string != NULL); /* just in case */
- /* If preedit_start_col is MAXCOL set it to the current cursor position. */
- if (preedit_start_col == MAXCOL && preedit_string[0] != '\0')
+ if (p_imst == IM_OVER_THE_SPOT)
{
- xim_has_preediting = TRUE;
-
- /* Urgh, this breaks if the input buffer isn't empty now */
- init_preedit_start_col();
+ if (preedit_string[0] == NUL)
+ {
+ xim_has_preediting = FALSE;
+ im_delete_preedit();
+ }
+ else
+ {
+ xim_has_preediting = TRUE;
+ im_show_preedit();
+ }
}
- else if (cursor_index == 0 && preedit_string[0] == '\0')
+ else
{
- xim_has_preediting = FALSE;
+ /* If preedit_start_col is MAXCOL set it to the current cursor position. */
+ if (preedit_start_col == MAXCOL && preedit_string[0] != '\0')
+ {
+ xim_has_preediting = TRUE;
- /* If at the start position (after typing backspace)
- * preedit_start_col must be reset. */
- preedit_start_col = MAXCOL;
- }
+ /* Urgh, this breaks if the input buffer isn't empty now */
+ init_preedit_start_col();
+ }
+ else if (cursor_index == 0 && preedit_string[0] == '\0')
+ {
+ xim_has_preediting = FALSE;
- im_delete_preedit();
+ /* If at the start position (after typing backspace)
+ * preedit_start_col must be reset. */
+ preedit_start_col = MAXCOL;
+ }
- /*
- * Compute the end of the preediting area: "preedit_end_col".
- * According to the documentation of gtk_im_context_get_preedit_string(),
- * the cursor_pos output argument returns the offset in bytes. This is
- * unfortunately not true -- real life shows the offset is in characters,
- * and the GTK+ source code agrees with me. Will file a bug later.
- */
- if (preedit_start_col != MAXCOL)
- preedit_end_col = preedit_start_col;
- str = (char_u *)preedit_string;
- for (p = str, i = 0; *p != NUL; p += utf_byte2len(*p), ++i)
- {
- int is_composing;
+ im_delete_preedit();
- is_composing = ((*p & 0x80) != 0 && utf_iscomposing(utf_ptr2char(p)));
/*
- * These offsets are used as counters when generating <BS> and <Del>
- * to delete the preedit string. So don't count composing characters
- * unless 'delcombine' is enabled.
+ * Compute the end of the preediting area: "preedit_end_col".
+ * According to the documentation of gtk_im_context_get_preedit_string(),
+ * the cursor_pos output argument returns the offset in bytes. This is
+ * unfortunately not true -- real life shows the offset is in characters,
+ * and the GTK+ source code agrees with me. Will file a bug later.
*/
- if (!is_composing || p_deco)
+ if (preedit_start_col != MAXCOL)
+ preedit_end_col = preedit_start_col;
+ str = (char_u *)preedit_string;
+ for (p = str, i = 0; *p != NUL; p += utf_byte2len(*p), ++i)
{
- if (i < cursor_index)
- ++im_preedit_cursor;
- else
- ++im_preedit_trailing;
+ int is_composing;
+
+ is_composing = ((*p & 0x80) != 0 && utf_iscomposing(utf_ptr2char(p)));
+ /*
+ * These offsets are used as counters when generating <BS> and <Del>
+ * to delete the preedit string. So don't count composing characters
+ * unless 'delcombine' is enabled.
+ */
+ if (!is_composing || p_deco)
+ {
+ if (i < cursor_index)
+ ++im_preedit_cursor;
+ else
+ ++im_preedit_trailing;
+ }
+ if (!is_composing && i >= cursor_index)
+ {
+ /* This is essentially the same as im_preedit_trailing, except
+ * composing characters are not counted even if p_deco is set. */
+ ++num_move_back;
+ }
+ if (preedit_start_col != MAXCOL)
+ preedit_end_col += utf_ptr2cells(p);
}
- if (!is_composing && i >= cursor_index)
+
+ if (p > str)
{
- /* This is essentially the same as im_preedit_trailing, except
- * composing characters are not counted even if p_deco is set. */
- ++num_move_back;
+ im_add_to_input(str, (int)(p - str));
+ im_correct_cursor(num_move_back);
}
- if (preedit_start_col != MAXCOL)
- preedit_end_col += utf_ptr2cells(p);
- }
-
- if (p > str)
- {
- im_add_to_input(str, (int)(p - str));
- im_correct_cursor(num_move_back);
}
g_free(preedit_string);
@@ -5310,7 +5441,8 @@ im_shutdown(void)
}
im_is_active = FALSE;
im_commit_handler_id = 0;
- preedit_start_col = MAXCOL;
+ if (p_imst == IM_ON_THE_SPOT)
+ preedit_start_col = MAXCOL;
xim_has_preediting = FALSE;
}
@@ -5465,7 +5597,8 @@ xim_reset(void)
}
}
- preedit_start_col = MAXCOL;
+ if (p_imst == IM_ON_THE_SPOT)
+ preedit_start_col = MAXCOL;
xim_has_preediting = FALSE;
}
@@ -5570,19 +5703,22 @@ xim_queue_key_press_event(GdkEventKey *event, int down)
{
int imresult = gtk_im_context_filter_keypress(xic, event);
- /* Some XIM send following sequence:
- * 1. preedited string.
- * 2. committed string.
- * 3. line changed key.
- * 4. preedited string.
- * 5. remove preedited string.
- * if 3, Vim can't move back the above line for 5.
- * thus, this part should not parse the key. */
- if (!imresult && preedit_start_col != MAXCOL
- && event->keyval == GDK_Return)
+ if (p_imst == IM_ON_THE_SPOT)
{
- im_synthesize_keypress(GDK_Return, 0U);
- return FALSE;
+ /* Some XIM send following sequence:
+ * 1. preedited string.
+ * 2. committed string.
+ * 3. line changed key.
+ * 4. preedited string.
+ * 5. remove preedited string.
+ * if 3, Vim can't move back the above line for 5.
+ * thus, this part should not parse the key. */
+ if (!imresult && preedit_start_col != MAXCOL
+ && event->keyval == GDK_Return)
+ {
+ im_synthesize_keypress(GDK_Return, 0U);
+ return FALSE;
+ }
}
/* If XIM tried to commit a keypad key as a single char.,