forked from aniani/vim
patch 8.0.1026: GTK on-the-spot input has problems
Problem: GTK on-the-spot input has problems. (Gerd Wachsmuth) Solution: Support over-the-spot. (Yukihiro Nakadaira, Ketn Takata, closes #1215)
This commit is contained in:
346
src/mbyte.c
346
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4851,6 +4859,95 @@ im_add_to_input(char_u *str, int len)
|
||||
if (input_conv.vc_type != CONV_NONE)
|
||||
vim_free(str);
|
||||
|
||||
if (p_mh) /* blank out the pointer if necessary */
|
||||
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);
|
||||
}
|
||||
@@ -4861,6 +4958,12 @@ 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,40 +5036,43 @@ 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();
|
||||
|
||||
/* Indicate that preediting has finished. */
|
||||
if (preedit_start_col == MAXCOL)
|
||||
{
|
||||
init_preedit_start_col();
|
||||
commit_with_preedit = FALSE;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
clen = mb_string2cells(im_str, len);
|
||||
|
||||
if (input_conv.vc_type != CONV_NONE)
|
||||
vim_free(im_str);
|
||||
preedit_start_col += clen;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
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
|
||||
* us carry on processing the raw keycode so that it may be used in
|
||||
@@ -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;
|
||||
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;
|
||||
/* 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 (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);
|
||||
}
|
||||
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;
|
||||
|
||||
if (p > str)
|
||||
{
|
||||
im_add_to_input(str, (int)(p - str));
|
||||
im_correct_cursor(num_move_back);
|
||||
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 (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.,
|
||||
|
Reference in New Issue
Block a user