diff options
Diffstat (limited to 'src/mbyte.c')
-rw-r--r-- | src/mbyte.c | 1764 |
1 files changed, 0 insertions, 1764 deletions
diff --git a/src/mbyte.c b/src/mbyte.c index 802df49214..de732c4fca 100644 --- a/src/mbyte.c +++ b/src/mbyte.c @@ -111,19 +111,6 @@ # endif #endif -#if defined(FEAT_GUI_GTK) && defined(FEAT_XIM) -# if GTK_CHECK_VERSION(3,0,0) -# include <gdk/gdkkeysyms-compat.h> -# else -# include <gdk/gdkkeysyms.h> -# endif -# ifdef MSWIN -# include <gdk/gdkwin32.h> -# else -# include <gdk/gdkx.h> -# endif -#endif - #ifdef HAVE_WCHAR_H # include <wchar.h> #endif @@ -179,37 +166,6 @@ static char utf8len_tab_zero[256] = 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,0,0, }; -/* - * XIM often causes trouble. Define XIM_DEBUG to get a log of XIM callbacks - * in the "xim.log" file. - */ -// #define XIM_DEBUG -#ifdef XIM_DEBUG - static void -xim_log(char *s, ...) -{ - va_list arglist; - static FILE *fd = NULL; - - if (fd == (FILE *)-1) - return; - if (fd == NULL) - { - fd = mch_fopen("xim.log", "w"); - if (fd == NULL) - { - emsg("Cannot open xim.log"); - fd = (FILE *)-1; - return; - } - } - - va_start(arglist, s); - vfprintf(fd, s, arglist); - va_end(arglist); -} -#endif - /* * Canonical encoding names and their properties. @@ -4778,1726 +4734,6 @@ iconv_end(void) # endif // DYNAMIC_ICONV # endif // USE_ICONV - -#ifdef FEAT_GUI -# define USE_IMACTIVATEFUNC (!gui.in_use && *p_imaf != NUL) -# define USE_IMSTATUSFUNC (!gui.in_use && *p_imsf != NUL) -#else -# define USE_IMACTIVATEFUNC (*p_imaf != NUL) -# define USE_IMSTATUSFUNC (*p_imsf != NUL) -#endif - -#if defined(FEAT_EVAL) && \ - (defined(FEAT_XIM) || defined(IME_WITHOUT_XIM) || defined(VIMDLL)) - static void -call_imactivatefunc(int active) -{ - typval_T argv[2]; - - argv[0].v_type = VAR_NUMBER; - argv[0].vval.v_number = active ? 1 : 0; - argv[1].v_type = VAR_UNKNOWN; - (void)call_func_retnr(p_imaf, 1, argv); -} - - static int -call_imstatusfunc(void) -{ - int is_active; - - // FIXME: Don't execute user function in unsafe situation. - if (exiting || is_autocmd_blocked()) - return FALSE; - // FIXME: :py print 'xxx' is shown duplicate result. - // Use silent to avoid it. - ++msg_silent; - is_active = call_func_retnr(p_imsf, 0, NULL); - --msg_silent; - return (is_active > 0); -} -#endif - -#if defined(FEAT_XIM) || defined(PROTO) - -# if defined(FEAT_GUI_GTK) || defined(PROTO) -static int xim_has_preediting INIT(= FALSE); // IM current status - -/* - * Set preedit_start_col to the current cursor position. - */ - static void -init_preedit_start_col(void) -{ - if (State & CMDLINE) - preedit_start_col = cmdline_getvcol_cursor(); - else if (curwin != NULL && curwin->w_buffer != NULL) - getvcol(curwin, &curwin->w_cursor, &preedit_start_col, NULL, NULL); - // Prevent that preediting marks the buffer as changed. - xim_changed_while_preediting = curbuf->b_changed; -} - -static int im_is_active = FALSE; // IM is enabled for current mode -static int preedit_is_active = FALSE; -static int im_preedit_cursor = 0; // cursor offset in characters -static int im_preedit_trailing = 0; // number of characters after cursor - -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) -{ - int was_active; - - was_active = !!im_get_status(); - im_is_active = (active && !p_imdisable); - - if (im_is_active != was_active) - xim_reset(); -} - - void -xim_set_focus(int focus) -{ - if (xic != NULL) - { - if (focus) - gtk_im_context_focus_in(xic); - else - gtk_im_context_focus_out(xic); - } -} - - void -im_set_position(int row, int col) -{ - if (xic != NULL) - { - GdkRectangle area; - - area.x = FILL_X(col); - area.y = FILL_Y(row); - area.width = gui.char_width * (mb_lefthalve(row, col) ? 2 : 1); - 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(); - } -} - -# if 0 || defined(PROTO) // apparently only used in gui_x11.c - void -xim_set_preedit(void) -{ - im_set_position(gui.row, gui.col); -} -# endif - - static void -im_add_to_input(char_u *str, int len) -{ - // Convert from 'termencoding' (always "utf-8") to 'encoding' - if (input_conv.vc_type != CONV_NONE) - { - str = string_convert(&input_conv, str, &len); - g_return_if_fail(str != NULL); - } - - add_to_input_buf_csi(str, 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, width, height; - int screen_x, screen_y, screen_width, screen_height; - - if (preedit_window == NULL) - return; - - gui_gtk_get_screen_geom_of_win(gui.drawarea, - &screen_x, &screen_y, &screen_width, &screen_height); - gdk_window_get_origin(gtk_widget_get_window(gui.drawarea), &x, &y); - gtk_window_get_size(GTK_WINDOW(preedit_window), &width, &height); - x = x + FILL_X(gui.col); - y = y + FILL_Y(gui.row); - if (x + width > screen_x + screen_width) - x = screen_x + screen_width - width; - if (y + height > screen_y + screen_height) - y = screen_y + screen_height - height; - gtk_window_move(GTK_WINDOW(preedit_window), x, y); -} - - static void -im_preedit_window_open() -{ - char *preedit_string; -#if !GTK_CHECK_VERSION(3,16,0) - char buf[8]; -#endif - PangoAttrList *attr_list; - PangoLayout *layout; -#if GTK_CHECK_VERSION(3,0,0) -# if !GTK_CHECK_VERSION(3,16,0) - GdkRGBA color; -# endif -#else - GdkColor color; -#endif - gint w, h; - - if (preedit_window == NULL) - { - preedit_window = gtk_window_new(GTK_WINDOW_POPUP); - gtk_window_set_transient_for(GTK_WINDOW(preedit_window), - GTK_WINDOW(gui.mainwin)); - preedit_label = gtk_label_new(""); - gtk_widget_set_name(preedit_label, "vim-gui-preedit-area"); - gtk_container_add(GTK_CONTAINER(preedit_window), preedit_label); - } - -#if GTK_CHECK_VERSION(3,16,0) - { - GtkStyleContext * const context - = gtk_widget_get_style_context(gui.drawarea); - GtkCssProvider * const provider = gtk_css_provider_new(); - gchar *css = NULL; - const char * const fontname - = pango_font_description_get_family(gui.norm_font); - gint fontsize - = pango_font_description_get_size(gui.norm_font) / PANGO_SCALE; - gchar *fontsize_propval = NULL; - - if (!pango_font_description_get_size_is_absolute(gui.norm_font)) - { - // fontsize was given in points. Convert it into that in pixels - // to use with CSS. - GdkScreen * const screen - = gdk_window_get_screen(gtk_widget_get_window(gui.mainwin)); - const gdouble dpi = gdk_screen_get_resolution(screen); - fontsize = dpi * fontsize / 72; - } - if (fontsize > 0) - fontsize_propval = g_strdup_printf("%dpx", fontsize); - else - fontsize_propval = g_strdup_printf("inherit"); - - css = g_strdup_printf( - "widget#vim-gui-preedit-area {\n" - " font-family: %s,monospace;\n" - " font-size: %s;\n" - " color: #%.2lx%.2lx%.2lx;\n" - " background-color: #%.2lx%.2lx%.2lx;\n" - "}\n", - fontname != NULL ? fontname : "inherit", - fontsize_propval, - (gui.norm_pixel >> 16) & 0xff, - (gui.norm_pixel >> 8) & 0xff, - gui.norm_pixel & 0xff, - (gui.back_pixel >> 16) & 0xff, - (gui.back_pixel >> 8) & 0xff, - gui.back_pixel & 0xff); - - gtk_css_provider_load_from_data(provider, css, -1, NULL); - gtk_style_context_add_provider(context, - GTK_STYLE_PROVIDER(provider), G_MAXUINT); - - g_free(css); - g_free(fontsize_propval); - g_object_unref(provider); - } -#elif GTK_CHECK_VERSION(3,0,0) - gtk_widget_override_font(preedit_label, gui.norm_font); - - vim_snprintf(buf, sizeof(buf), "#%06X", gui.norm_pixel); - gdk_rgba_parse(&color, buf); - gtk_widget_override_color(preedit_label, GTK_STATE_FLAG_NORMAL, &color); - - vim_snprintf(buf, sizeof(buf), "#%06X", gui.back_pixel); - gdk_rgba_parse(&color, buf); - gtk_widget_override_background_color(preedit_label, GTK_STATE_FLAG_NORMAL, - &color); -#else - gtk_widget_modify_font(preedit_label, gui.norm_font); - - vim_snprintf(buf, sizeof(buf), "#%06X", (unsigned)gui.norm_pixel); - gdk_color_parse(buf, &color); - gtk_widget_modify_fg(preedit_label, GTK_STATE_NORMAL, &color); - - vim_snprintf(buf, sizeof(buf), "#%06X", (unsigned)gui.back_pixel); - gdk_color_parse(buf, &color); - gtk_widget_modify_bg(preedit_window, GTK_STATE_NORMAL, &color); -#endif - - 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 -#ifdef FEAT_TERMINAL - && !term_use_loop() -#endif - ) - { - im_preedit_cursor = 0; - return; - } - for (; im_preedit_cursor > 0; --im_preedit_cursor) - add_to_input_buf(bskey, (int)sizeof(bskey)); - - for (; im_preedit_trailing > 0; --im_preedit_trailing) - add_to_input_buf(delkey, (int)sizeof(delkey)); -} - -/* - * Move the cursor left by "num_move_back" characters. - * Note that ins_left() checks im_is_preediting() to avoid breaking undo for - * these K_LEFT keys. - */ - static void -im_correct_cursor(int num_move_back) -{ - char_u backkey[] = {CSI, 'k', 'l'}; - - if (State & NORMAL) - return; -# ifdef FEAT_RIGHTLEFT - if ((State & CMDLINE) == 0 && curwin != NULL && curwin->w_p_rl) - backkey[2] = 'r'; -# endif - for (; num_move_back > 0; --num_move_back) - add_to_input_buf(backkey, (int)sizeof(backkey)); -} - -static int xim_expected_char = NUL; -static int xim_ignored_char = FALSE; - -/* - * Update the mode and cursor while in an IM callback. - */ - static void -im_show_info(void) -{ - int old_vgetc_busy; - - old_vgetc_busy = vgetc_busy; - vgetc_busy = TRUE; - showmode(); - vgetc_busy = old_vgetc_busy; - if ((State & NORMAL) || (State & INSERT)) - setcursor(); - out_flush(); -} - -/* - * Callback invoked when the user finished preediting. - * Put the final string into the input buffer. - */ - static void -im_commit_cb(GtkIMContext *context UNUSED, - const gchar *str, - gpointer data UNUSED) -{ - int slen = (int)STRLEN(str); - int add_to_input = TRUE; - int clen; - int len = slen; - int commit_with_preedit = TRUE; - char_u *im_str; - -#ifdef XIM_DEBUG - xim_log("im_commit_cb(): %s\n", str); -#endif - - if (p_imst == IM_ON_THE_SPOT) - { - // 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; - } - - // 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 - // mappings as <kSomething>. - if (xim_expected_char != NUL) - { - // We're currently processing a keypad or other special key - if (slen == 1 && str[0] == xim_expected_char) - { - // It's a match - don't do it here - xim_ignored_char = TRUE; - add_to_input = FALSE; - } - else - { - // Not a match - xim_ignored_char = FALSE; - } - } - - if (add_to_input) - im_add_to_input((char_u *)str, slen); - - 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(); -} - -/* - * Callback invoked after start to the preedit. - */ - static void -im_preedit_start_cb(GtkIMContext *context UNUSED, gpointer data UNUSED) -{ -#ifdef XIM_DEBUG - xim_log("im_preedit_start_cb()\n"); -#endif - - im_is_active = TRUE; - preedit_is_active = TRUE; - gui_update_cursor(TRUE, FALSE); - im_show_info(); -} - -/* - * Callback invoked after end to the preedit. - */ - static void -im_preedit_end_cb(GtkIMContext *context UNUSED, gpointer data UNUSED) -{ -#ifdef XIM_DEBUG - xim_log("im_preedit_end_cb()\n"); -#endif - im_delete_preedit(); - - // Indicate that preediting has finished - if (p_imst == IM_ON_THE_SPOT) - preedit_start_col = MAXCOL; - xim_has_preediting = FALSE; - -#if 0 - // Removal of this line suggested by Takuhiro Nishioka. Fixes that IM was - // switched off unintentionally. We now use preedit_is_active (added by - // SungHyun Nam). - im_is_active = FALSE; -#endif - preedit_is_active = FALSE; - gui_update_cursor(TRUE, FALSE); - im_show_info(); -} - -/* - * Callback invoked after changes to the preedit string. If the preedit - * string was empty before, remember the preedit start column so we know - * where to apply feedback attributes. Delete the previous preedit string - * if there was one, save the new preedit cursor offset, and put the new - * string into the input buffer. - * - * TODO: The pragmatic "put into input buffer" approach used here has - * several fundamental problems: - * - * - The characters in the preedit string are subject to remapping. - * That's broken, only the finally committed string should be remapped. - * - * - There is a race condition involved: The retrieved value for the - * current cursor position will be wrong if any unprocessed characters - * are still queued in the input buffer. - * - * - Due to the lack of synchronization between the file buffer in memory - * and any typed characters, it's practically impossible to implement the - * "retrieve_surrounding" and "delete_surrounding" signals reliably. IM - * modules for languages such as Thai are likely to rely on this feature - * for proper operation. - * - * Conclusions: I think support for preediting needs to be moved to the - * core parts of Vim. Ideally, until it has been committed, the preediting - * string should only be displayed and not affect the buffer content at all. - * The question how to deal with the synchronization issue still remains. - * Circumventing the input buffer is probably not desirable. Anyway, I think - * implementing "retrieve_surrounding" is the only hard problem. - * - * One way to solve all of this in a clean manner would be to queue all key - * press/release events "as is" in the input buffer, and apply the IM filtering - * at the receiving end of the queue. This, however, would have a rather large - * impact on the code base. If there is an easy way to force processing of all - * remaining input from within the "retrieve_surrounding" signal handler, this - * might not be necessary. Gotta ask on vim-dev for opinions. - */ - static void -im_preedit_changed_cb(GtkIMContext *context, gpointer data UNUSED) -{ - char *preedit_string = NULL; - int cursor_index = 0; - int num_move_back = 0; - char_u *str; - char_u *p; - int i; - - 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); -#endif - - g_return_if_fail(preedit_string != NULL); // just in case - - if (p_imst == IM_OVER_THE_SPOT) - { - if (preedit_string[0] == NUL) - { - xim_has_preediting = FALSE; - im_delete_preedit(); - } - else - { - xim_has_preediting = TRUE; - im_show_preedit(); - } - } - else - { - // 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; - - // 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; - - // If at the start position (after typing backspace) - // preedit_start_col must be reset. - preedit_start_col = MAXCOL; - } - - im_delete_preedit(); - - /* - * 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; - - 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); - - if (gtk_main_level() > 0) - gtk_main_quit(); -} - -/* - * Translate the Pango attributes at iter to Vim highlighting attributes. - * Ignore attributes not supported by Vim highlighting. This shouldn't have - * too much impact -- right now we handle even more attributes than necessary - * for the IM modules I tested with. - */ - static int -translate_pango_attributes(PangoAttrIterator *iter) -{ - PangoAttribute *attr; - int char_attr = HL_NORMAL; - - attr = pango_attr_iterator_get(iter, PANGO_ATTR_UNDERLINE); - if (attr != NULL && ((PangoAttrInt *)attr)->value - != (int)PANGO_UNDERLINE_NONE) - char_attr |= HL_UNDERLINE; - - attr = pango_attr_iterator_get(iter, PANGO_ATTR_WEIGHT); - if (attr != NULL && ((PangoAttrInt *)attr)->value >= (int)PANGO_WEIGHT_BOLD) - char_attr |= HL_BOLD; - - attr = pango_attr_iterator_get(iter, PANGO_ATTR_STYLE); - if (attr != NULL && ((PangoAttrInt *)attr)->value - != (int)PANGO_STYLE_NORMAL) - char_attr |= HL_ITALIC; - - attr = pango_attr_iterator_get(iter, PANGO_ATTR_BACKGROUND); - if (attr != NULL) - { - const PangoColor *color = &((PangoAttrColor *)attr)->color; - - // Assume inverse if black background is requested - if ((color->red | color->green | color->blue) == 0) - char_attr |= HL_INVERSE; - } - - return char_attr; -} - -/* - * Retrieve the highlighting attributes at column col in the preedit string. - * Return -1 if not in preediting mode or if col is out of range. - */ - int -im_get_feedback_attr(int col) -{ - char *preedit_string = NULL; - PangoAttrList *attr_list = NULL; - int char_attr = -1; - - if (xic == NULL) - return char_attr; - - gtk_im_context_get_preedit_string(xic, &preedit_string, &attr_list, NULL); - - if (preedit_string != NULL && attr_list != NULL) - { - int idx; - - // Get the byte index as used by PangoAttrIterator - for (idx = 0; col > 0 && preedit_string[idx] != '\0'; --col) - idx += utfc_ptr2len((char_u *)preedit_string + idx); - - if (preedit_string[idx] != '\0') - { - PangoAttrIterator *iter; - int start, end; - - char_attr = HL_NORMAL; - iter = pango_attr_list_get_iterator(attr_list); - - // Extract all relevant attributes from the list. - do - { - pango_attr_iterator_range(iter, &start, &end); - - if (idx >= start && idx < end) - char_attr |= translate_pango_attributes(iter); - } - while (pango_attr_iterator_next(iter)); - - pango_attr_iterator_destroy(iter); - } - } - - if (attr_list != NULL) - pango_attr_list_unref(attr_list); - g_free(preedit_string); - - return char_attr; -} - - void -xim_init(void) -{ -#ifdef XIM_DEBUG - xim_log("xim_init()\n"); -#endif - - g_return_if_fail(gui.drawarea != NULL); - g_return_if_fail(gtk_widget_get_window(gui.drawarea) != NULL); - - xic = gtk_im_multicontext_new(); - g_object_ref(xic); - - im_commit_handler_id = g_signal_connect(G_OBJECT(xic), "commit", - G_CALLBACK(&im_commit_cb), NULL); - g_signal_connect(G_OBJECT(xic), "preedit_changed", - G_CALLBACK(&im_preedit_changed_cb), NULL); - g_signal_connect(G_OBJECT(xic), "preedit_start", - G_CALLBACK(&im_preedit_start_cb), NULL); - g_signal_connect(G_OBJECT(xic), "preedit_end", - G_CALLBACK(&im_preedit_end_cb), NULL); - - gtk_im_context_set_client_window(xic, gtk_widget_get_window(gui.drawarea)); -} - - void -im_shutdown(void) -{ -#ifdef XIM_DEBUG - xim_log("im_shutdown()\n"); -#endif - - if (xic != NULL) - { - gtk_im_context_focus_out(xic); - g_object_unref(xic); - xic = NULL; - } - im_is_active = FALSE; - im_commit_handler_id = 0; - if (p_imst == IM_ON_THE_SPOT) - preedit_start_col = MAXCOL; - xim_has_preediting = FALSE; -} - -/* - * Convert the string argument to keyval and state for GdkEventKey. - * If str is valid return TRUE, otherwise FALSE. - * - * See 'imactivatekey' for documentation of the format. - */ - static int -im_string_to_keyval(const char *str, unsigned int *keyval, unsigned int *state) -{ - const char *mods_end; - unsigned tmp_keyval; - unsigned tmp_state = 0; - - mods_end = strrchr(str, '-'); - mods_end = (mods_end != NULL) ? mods_end + 1 : str; - - // Parse modifier keys - while (str < mods_end) - switch (*str++) - { - case '-': break; - case 'S': case 's': tmp_state |= (unsigned)GDK_SHIFT_MASK; break; - case 'L': case 'l': tmp_state |= (unsigned)GDK_LOCK_MASK; break; - case 'C': case 'c': tmp_state |= (unsigned)GDK_CONTROL_MASK;break; - case '1': tmp_state |= (unsigned)GDK_MOD1_MASK; break; - case '2': tmp_state |= (unsigned)GDK_MOD2_MASK; break; - case '3': tmp_state |= (unsigned)GDK_MOD3_MASK; break; - case '4': tmp_state |= (unsigned)GDK_MOD4_MASK; break; - case '5': tmp_state |= (unsigned)GDK_MOD5_MASK; break; - default: - return FALSE; - } - - tmp_keyval = gdk_keyval_from_name(str); - - if (tmp_keyval == 0 || tmp_keyval == GDK_VoidSymbol) - return FALSE; - - if (keyval != NULL) - *keyval = tmp_keyval; - if (state != NULL) - *state = tmp_state; - - return TRUE; -} - -/* - * Return TRUE if p_imak is valid, otherwise FALSE. As a special case, an - * empty string is also regarded as valid. - * - * Note: The numerical key value of p_imak is cached if it was valid; thus - * boldly assuming im_xim_isvalid_imactivate() will always be called whenever - * 'imak' changes. This is currently the case but not obvious -- should - * probably rename the function for clarity. - */ - int -im_xim_isvalid_imactivate(void) -{ - if (p_imak[0] == NUL) - { - im_activatekey_keyval = GDK_VoidSymbol; - im_activatekey_state = 0; - return TRUE; - } - - return im_string_to_keyval((const char *)p_imak, - &im_activatekey_keyval, - &im_activatekey_state); -} - - static void -im_synthesize_keypress(unsigned int keyval, unsigned int state) -{ - GdkEventKey *event; - - event = (GdkEventKey *)gdk_event_new(GDK_KEY_PRESS); - g_object_ref(gtk_widget_get_window(gui.drawarea)); - // unreffed by gdk_event_free() - event->window = gtk_widget_get_window(gui.drawarea); - event->send_event = TRUE; - event->time = GDK_CURRENT_TIME; - event->state = state; - event->keyval = keyval; - event->hardware_keycode = // needed for XIM - XKeysymToKeycode(GDK_WINDOW_XDISPLAY(event->window), (KeySym)keyval); - event->length = 0; - event->string = NULL; - - gtk_im_context_filter_keypress(xic, event); - - // For consistency, also send the corresponding release event. - event->type = GDK_KEY_RELEASE; - event->send_event = FALSE; - gtk_im_context_filter_keypress(xic, event); - - gdk_event_free((GdkEvent *)event); -} - - void -xim_reset(void) -{ -# ifdef FEAT_EVAL - if (USE_IMACTIVATEFUNC) - call_imactivatefunc(im_is_active); - else -# endif - if (xic != NULL) - { - gtk_im_context_reset(xic); - - if (p_imdisable) - im_shutdown(); - else - { - xim_set_focus(gui.in_focus); - - if (im_activatekey_keyval != GDK_VoidSymbol) - { - if (im_is_active) - { - g_signal_handler_block(xic, im_commit_handler_id); - im_synthesize_keypress(im_activatekey_keyval, - im_activatekey_state); - g_signal_handler_unblock(xic, im_commit_handler_id); - } - } - else - { - im_shutdown(); - xim_init(); - xim_set_focus(gui.in_focus); - } - } - } - - if (p_imst == IM_ON_THE_SPOT) - preedit_start_col = MAXCOL; - xim_has_preediting = FALSE; -} - - int -xim_queue_key_press_event(GdkEventKey *event, int down) -{ - if (down) - { - /* - * Workaround GTK2 XIM 'feature' that always converts keypad keys to - * chars., even when not part of an IM sequence (ref. feature of - * gdk/gdkkeyuni.c). - * Flag any keypad keys that might represent a single char. - * If this (on its own - i.e., not part of an IM sequence) is - * committed while we're processing one of these keys, we can ignore - * that commit and go ahead & process it ourselves. That way we can - * still distinguish keypad keys for use in mappings. - * Also add GDK_space to make <S-Space> work. - */ - switch (event->keyval) - { - case GDK_KP_Add: xim_expected_char = '+'; break; - case GDK_KP_Subtract: xim_expected_char = '-'; break; - case GDK_KP_Divide: xim_expected_char = '/'; break; - case GDK_KP_Multiply: xim_expected_char = '*'; break; - case GDK_KP_Decimal: xim_expected_char = '.'; break; - case GDK_KP_Equal: xim_expected_char = '='; break; - case GDK_KP_0: xim_expected_char = '0'; break; - case GDK_KP_1: xim_expected_char = '1'; break; - case GDK_KP_2: xim_expected_char = '2'; break; - case GDK_KP_3: xim_expected_char = '3'; break; - case GDK_KP_4: xim_expected_char = '4'; break; - case GDK_KP_5: xim_expected_char = '5'; break; - case GDK_KP_6: xim_expected_char = '6'; break; - case GDK_KP_7: xim_expected_char = '7'; break; - case GDK_KP_8: xim_expected_char = '8'; break; - case GDK_KP_9: xim_expected_char = '9'; break; - case GDK_space: xim_expected_char = ' '; break; - default: xim_expected_char = NUL; - } - xim_ignored_char = FALSE; - } - - /* - * When typing fFtT, XIM may be activated. Thus it must pass - * gtk_im_context_filter_keypress() in Normal mode. - * And while doing :sh too. - */ - if (xic != NULL && !p_imdisable - && (State & (INSERT | CMDLINE | NORMAL | EXTERNCMD)) != 0) - { - /* - * Filter 'imactivatekey' and map it to CTRL-^. This way, Vim is - * always aware of the current status of IM, and can even emulate - * the activation key for modules that don't support one. - */ - if (event->keyval == im_activatekey_keyval - && (event->state & im_activatekey_state) == im_activatekey_state) - { - unsigned int state_mask; - - // Require the state of the 3 most used modifiers to match exactly. - // Otherwise e.g. <S-C-space> would be unusable for other purposes - // if the IM activate key is <S-space>. - state_mask = im_activatekey_state; - state_mask |= ((int)GDK_SHIFT_MASK | (int)GDK_CONTROL_MASK - | (int)GDK_MOD1_MASK); - - if ((event->state & state_mask) != im_activatekey_state) - return FALSE; - - // Don't send it a second time on GDK_KEY_RELEASE. - if (event->type != GDK_KEY_PRESS) - return TRUE; - - if (map_to_exists_mode((char_u *)"", LANGMAP, FALSE)) - { - im_set_active(FALSE); - - // ":lmap" mappings exists, toggle use of mappings. - State ^= LANGMAP; - if (State & LANGMAP) - { - curbuf->b_p_iminsert = B_IMODE_NONE; - State &= ~LANGMAP; - } - else - { - curbuf->b_p_iminsert = B_IMODE_LMAP; - State |= LANGMAP; - } - return TRUE; - } - - return gtk_im_context_filter_keypress(xic, event); - } - - // Don't filter events through the IM context if IM isn't active - // right now. Unlike with GTK+ 1.2 we cannot rely on the IM module - // not doing anything before the activation key was sent. - if (im_activatekey_keyval == GDK_VoidSymbol || im_is_active) - { - int imresult = gtk_im_context_filter_keypress(xic, event); - - if (p_imst == IM_ON_THE_SPOT) - { - // 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., - // ignore it so we can use the keypad key 'raw', for mappings. - if (xim_expected_char != NUL && xim_ignored_char) - // We had a keypad key, and XIM tried to thieve it - return FALSE; - - // This is supposed to fix a problem with iBus, that space - // characters don't work in input mode. - xim_expected_char = NUL; - - // Normal processing - return imresult; - } - } - - return FALSE; -} - - int -im_get_status(void) -{ -# ifdef FEAT_EVAL - if (USE_IMSTATUSFUNC) - return call_imstatusfunc(); -# endif - return im_is_active; -} - - int -preedit_get_status(void) -{ - return preedit_is_active; -} - - int -im_is_preediting(void) -{ - return xim_has_preediting; -} - -# else // !FEAT_GUI_GTK - -static int xim_is_active = FALSE; // XIM should be active in the current - // mode -static int xim_has_focus = FALSE; // XIM is really being used for Vim -# ifdef FEAT_GUI_X11 -static XIMStyle input_style; -static int status_area_enabled = TRUE; -# endif - -/* - * Switch using XIM on/off. This is used by the code that changes "State". - * When 'imactivatefunc' is defined use that function instead. - */ - void -im_set_active(int active_arg) -{ - int active = active_arg; - - // If 'imdisable' is set, XIM is never active. - if (p_imdisable) - active = FALSE; - else if (input_style & XIMPreeditPosition) - // There is a problem in switching XIM off when preediting is used, - // and it is not clear how this can be solved. For now, keep XIM on - // all the time, like it was done in Vim 5.8. - active = TRUE; - -# if defined(FEAT_EVAL) - if (USE_IMACTIVATEFUNC) - |