diff options
author | Bram Moolenaar <Bram@vim.org> | 2017-08-30 22:00:20 +0200 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2017-08-30 22:00:20 +0200 |
commit | 5c6dbcb03fa552d7b0e61c8fcf425147eb6bf7d5 (patch) | |
tree | 13f37aa604997567c63b0e5786ee2625282d8d4b /src/mbyte.c | |
parent | 4e83961985abb78757b135f29ac4ffde675247af (diff) | |
download | vim-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.c | 340 |
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., |