To: vim-dev@vim.org Subject: Patch 6.2.451 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 8bit ------------ Patch 6.2.451 Problem: GTK: when using XIM there are various problems, including setting 'modified' and breaking undo at the wrong moment. Solution: Add "xim_changed_while_preediting", "preedit_end_col" and im_is_preediting(). (Yasuhiro Matsumoto) Files: src/ex_getln.c, src/globals.h, src/gui_gtk.c, src/gui_gtk_x11.c, src/mbyte.c, src/misc1.c, src/proto/mbyte.pro, src/screen.c, src/undo.c *** ../vim-6.2.450/src/ex_getln.c Sun Apr 4 12:34:29 2004 --- src/ex_getln.c Wed Mar 31 19:19:16 2004 *************** *** 2115,2121 **** && xic != NULL && im_get_status() && !p_imdisable ! && preedit_start_col != MAXCOL) { int cmdpos = 0; int cmdspos; --- 2115,2121 ---- && xic != NULL && im_get_status() && !p_imdisable ! && im_is_preediting()) { int cmdpos = 0; int cmdspos; *** ../vim-6.2.450/src/globals.h Thu Apr 1 13:01:12 2004 --- src/globals.h Thu Apr 1 12:43:05 2004 *************** *** 719,725 **** --- 719,737 ---- EXTERN GdkIC *xic INIT(= NULL); EXTERN char *draw_feedback INIT(= NULL); # endif + /* + * Start and end column of the preedit area in virtual columns from the start + * of the text line. When there is no preedit area they are set to MAXCOL. + * "preedit_end_col" is needed for coloring the preedited string. Drawing the + * color between "preedit_start_col" and curpos did not work, because some XIM + * set the cursor position to the first char of the string. + */ EXTERN colnr_T preedit_start_col INIT(= MAXCOL); + EXTERN colnr_T preedit_end_col INIT(= MAXCOL); + + /* "xim_changed_while_preediting" is set when changed() can set the 'modified' + * flag even while preediting. */ + EXTERN int xim_changed_while_preediting INIT(= FALSE); # else EXTERN XIC xic INIT(= NULL); # endif *** ../vim-6.2.450/src/gui_gtk.c Thu Feb 26 18:16:35 2004 --- src/gui_gtk.c Sun Mar 28 14:39:02 2004 *************** *** 1099,1105 **** #ifdef FEAT_XIM /* cancel any preediting */ ! if (preedit_start_col != MAXCOL) xim_reset(); #endif --- 1099,1105 ---- #ifdef FEAT_XIM /* cancel any preediting */ ! if (im_is_preediting()) xim_reset(); #endif *** ../vim-6.2.450/src/gui_gtk_x11.c Sun Apr 4 12:03:03 2004 --- src/gui_gtk_x11.c Mon Apr 5 20:13:57 2004 *************** *** 1095,1100 **** --- 1095,1106 ---- if (len == 0) /* Unrecognized key */ return TRUE; + #if defined(FEAT_XIM) && defined(FEAT_GUI_GTK) && !defined(HAVE_GTK2) + /* Cancel or type backspace. For GTK2, im_commit_cb() does the same. */ + preedit_start_col = MAXCOL; + xim_changed_while_preediting = TRUE; + #endif + /* Special keys (and a few others) may have modifiers. Also when using a * double-byte encoding (can't set the 8th bit). */ if (len == -3 || key_sym == GDK_space || key_sym == GDK_Tab *************** *** 1786,1792 **** #ifdef FEAT_XIM /* cancel any preediting */ ! if (preedit_start_col != MAXCOL) xim_reset(); #endif --- 1792,1798 ---- #ifdef FEAT_XIM /* cancel any preediting */ ! if (im_is_preediting()) xim_reset(); #endif *************** *** 1834,1840 **** # ifdef FEAT_XIM /* cancel any preediting */ ! if (preedit_start_col != MAXCOL) xim_reset(); # endif --- 1840,1846 ---- # ifdef FEAT_XIM /* cancel any preediting */ ! if (im_is_preediting()) xim_reset(); # endif *** ../vim-6.2.450/src/mbyte.c Mon Apr 5 19:41:32 2004 --- src/mbyte.c Mon Apr 5 20:19:30 2004 *************** *** 3132,3137 **** --- 3132,3139 ---- #if defined(FEAT_XIM) || defined(PROTO) # ifdef FEAT_GUI_GTK + static int xim_has_preediting INIT(= FALSE); /* IM current status */ + /* * Set preedit_start_col to the current cursor position. */ *************** *** 3142,3147 **** --- 3144,3151 ---- preedit_start_col = cmdline_getvcol_cursor(); else if (curwin != 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; } # endif *************** *** 3259,3264 **** --- 3263,3283 ---- 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; + setcursor(); + out_flush(); + } + + /* * Callback invoked when the user finished preediting. * Put the final string into the input buffer. */ *************** *** 3266,3273 **** static void im_commit_cb(GtkIMContext *context, const gchar *str, gpointer data) { ! int slen = (int)STRLEN(str); ! int add_to_input = TRUE; #ifdef XIM_DEBUG xim_log("im_commit_cb(): %s\n", str); --- 3285,3296 ---- static void im_commit_cb(GtkIMContext *context, const gchar *str, gpointer data) { ! int slen = (int)STRLEN(str); ! int add_to_input = TRUE; ! int clen; ! int len = slen; ! int commit_with_preedit = TRUE; ! char_u *im_str, *p; #ifdef XIM_DEBUG xim_log("im_commit_cb(): %s\n", str); *************** *** 3277,3284 **** * committing. Call im_delete_preedit() to work around that. */ im_delete_preedit(); ! /* Indicate that preediting has finished */ ! preedit_start_col = MAXCOL; /* 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 --- 3300,3334 ---- * 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 forcely 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 = 0; ! for (p = im_str; p < im_str + len; p += (*mb_ptr2len_check)(p)) ! clen += (*mb_ptr2cells)(p); ! 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 *************** *** 3303,3308 **** --- 3353,3367 ---- 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 (gtk_main_level() > 0) gtk_main_quit(); } *************** *** 3332,3339 **** --- 3391,3405 ---- #ifdef XIM_DEBUG xim_log("im_preedit_end_cb()\n"); #endif + im_delete_preedit(); + + /* Indicate that preediting has finished */ + preedit_start_col = MAXCOL; + xim_has_preediting = FALSE; + im_is_active = FALSE; gui_update_cursor(TRUE, FALSE); + im_show_info(); } /* *************** *** 3394,3420 **** g_return_if_fail(preedit_string != NULL); /* just in case */ - /* If at the start position (after typing backspace) preedit_start_col - * must be reset. */ - if (cursor_index == 0) - preedit_start_col = MAXCOL; - /* If preedit_start_col is MAXCOL set it to the current cursor position. */ if (preedit_start_col == MAXCOL && preedit_string[0] != '\0') { /* Urgh, this breaks if the input buffer isn't empty now */ init_preedit_start_col(); } im_delete_preedit(); - str = (char_u *)preedit_string; /* * 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. */ for (p = str, i = 0; *p != NUL; p += utf_byte2len(*p), ++i) { int is_composing; --- 3460,3495 ---- 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') { + 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') + { + if (preedit_start_col == MAXCOL) + 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; *************** *** 3438,3443 **** --- 3513,3520 ---- * 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) *************** *** 3555,3560 **** --- 3632,3638 ---- g_return_if_fail(gui.drawarea->window != 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); *************** *** 3584,3589 **** --- 3662,3668 ---- im_is_active = FALSE; im_commit_handler_id = 0; preedit_start_col = MAXCOL; + xim_has_preediting = FALSE; } /* *************** *** 3726,3749 **** * want because it makes the Ami status display work reliably. */ gtk_im_context_set_use_preedit(xic, FALSE); - gtk_im_context_set_use_preedit(xic, TRUE); - xim_set_focus(gui.in_focus); ! if (im_activatekey_keyval != GDK_VoidSymbol && 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); } } preedit_start_col = MAXCOL; } int --- 3805,3839 ---- * want because it makes the Ami status display work reliably. */ gtk_im_context_set_use_preedit(xic, FALSE); ! if (p_imdisable) ! im_shutdown(); else { ! gtk_im_context_set_use_preedit(xic, TRUE); 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); + } } } preedit_start_col = MAXCOL; + xim_has_preediting = FALSE; } int *************** *** 3813,3828 **** return FALSE; /* Don't send it a second time on GDK_KEY_RELEASE. */ ! if (event->type == GDK_KEY_PRESS) ! { ! char_u ctrl_hat[] = {Ctrl_HAT}; ! add_to_input_buf(ctrl_hat, (int)sizeof(ctrl_hat)); ! if (gtk_main_level() > 0) ! gtk_main_quit(); } ! return TRUE; } /* Don't filter events through the IM context if IM isn't active --- 3903,3931 ---- 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)) ! { ! 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 *************** *** 3832,3837 **** --- 3935,3955 ---- { 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) + { + 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) *************** *** 3882,3888 **** #if defined(FEAT_GUI_GTK) || defined(PROTO) static int preedit_buf_len = 0; ! static int xim_preediting INIT(= FALSE); /* XIM in showmode() */ static int xim_input_style; #ifndef FEAT_GUI_GTK # define gboolean int --- 4000,4006 ---- #if defined(FEAT_GUI_GTK) || defined(PROTO) static int preedit_buf_len = 0; ! static int xim_can_preediting INIT(= FALSE); /* XIM in showmode() */ static int xim_input_style; #ifndef FEAT_GUI_GTK # define gboolean int *************** *** 4047,4053 **** */ if (xim_input_style & (int)GDK_IM_PREEDIT_CALLBACKS) { ! if (xim_preediting && !active) { /* Force turn off preedit state. With some IM * implementations, we cannot turn off preedit state by --- 4165,4171 ---- */ if (xim_input_style & (int)GDK_IM_PREEDIT_CALLBACKS) { ! if (xim_can_preediting && !active) { /* Force turn off preedit state. With some IM * implementations, we cannot turn off preedit state by *************** *** 4059,4067 **** xim_set_focus(FALSE); gdk_ic_destroy(xic); xim_init(); ! xim_preediting = FALSE; } ! else if (!xim_preediting && active) im_xim_send_event_imactivate(); } else --- 4177,4185 ---- xim_set_focus(FALSE); gdk_ic_destroy(xic); xim_init(); ! xim_can_preediting = FALSE; } ! else if (!xim_can_preediting && active) im_xim_send_event_imactivate(); } else *************** *** 4072,4078 **** xim_set_focus(FALSE); gdk_ic_destroy(xic); xim_init(); ! xim_preediting = FALSE; /* 2nd, when requested to activate IM, symulate this by sending * the event. --- 4190,4196 ---- xim_set_focus(FALSE); gdk_ic_destroy(xic); xim_init(); ! xim_can_preediting = FALSE; /* 2nd, when requested to activate IM, symulate this by sending * the event. *************** *** 4080,4086 **** if (active) { im_xim_send_event_imactivate(); ! xim_preediting = TRUE; } } } --- 4198,4204 ---- if (active) { im_xim_send_event_imactivate(); ! xim_can_preediting = TRUE; } } } *************** *** 4111,4117 **** active ? XIMPreeditEnable : XIMPreeditDisable, NULL); XSetICValues(pxic, XNPreeditAttributes, preedit_attr, NULL); ! xim_preediting = active; xim_is_active = active; } XFree(preedit_attr); --- 4229,4235 ---- active ? XIMPreeditEnable : XIMPreeditDisable, NULL); XSetICValues(pxic, XNPreeditAttributes, preedit_attr, NULL); ! xim_can_preediting = active; xim_is_active = active; } XFree(preedit_attr); *************** *** 4849,4855 **** #endif draw_feedback = NULL; ! xim_preediting = TRUE; gui_update_cursor(TRUE, FALSE); if (showmode() > 0) { --- 4967,4974 ---- #endif draw_feedback = NULL; ! xim_can_preediting = TRUE; ! xim_has_preediting = TRUE; gui_update_cursor(TRUE, FALSE); if (showmode() > 0) { *************** *** 4966,4971 **** --- 5085,5091 ---- #ifdef FEAT_MBYTE vim_free(buf); #endif + preedit_end_col = MAXCOL; } } if (text != NULL || draw_data->chg_length > 0) *************** *** 5032,5038 **** vim_free(draw_feedback); draw_feedback = NULL; ! xim_preediting = FALSE; gui_update_cursor(TRUE, FALSE); if (showmode() > 0) { --- 5152,5159 ---- vim_free(draw_feedback); draw_feedback = NULL; ! xim_can_preediting = FALSE; ! xim_has_preediting = FALSE; gui_update_cursor(TRUE, FALSE); if (showmode() > 0) { *************** *** 5238,5245 **** xic = NULL; } xim_is_active = FALSE; ! xim_preediting = FALSE; preedit_start_col = MAXCOL; } #endif /* FEAT_GUI_GTK */ --- 5359,5367 ---- xic = NULL; } xim_is_active = FALSE; ! xim_can_preediting = FALSE; preedit_start_col = MAXCOL; + xim_has_preediting = FALSE; } #endif /* FEAT_GUI_GTK */ *************** *** 5266,5279 **** int im_get_status() { ! #ifdef FEAT_GUI_GTK if (xim_input_style & (int)GDK_IM_PREEDIT_CALLBACKS) ! return xim_preediting; ! #endif return xim_has_focus; } # endif /* !HAVE_GTK2 */ #endif /* FEAT_XIM */ #if defined(FEAT_MBYTE) || defined(PROTO) --- 5388,5409 ---- int im_get_status() { ! # ifdef FEAT_GUI_GTK if (xim_input_style & (int)GDK_IM_PREEDIT_CALLBACKS) ! return xim_can_preediting; ! # endif return xim_has_focus; } # endif /* !HAVE_GTK2 */ + + # if defined(FEAT_GUI_GTK) || defined(PROTO) + int + im_is_preediting() + { + return xim_has_preediting; + } + # endif #endif /* FEAT_XIM */ #if defined(FEAT_MBYTE) || defined(PROTO) *** ../vim-6.2.450/src/misc1.c Sun Mar 28 13:57:52 2004 --- src/misc1.c Mon Apr 5 20:21:28 2004 *************** *** 2327,2332 **** --- 2327,2341 ---- void changed() { + #if defined(FEAT_XIM) && defined(FEAT_GUI_GTK) + /* The text of the preediting area is inserted, but this doesn't + * mean a change of the buffer yet. That is delayed until the + * text is committed. (this means preedit becomes empty) */ + if (im_is_preediting() && !xim_changed_while_preediting) + return; + xim_changed_while_preediting = FALSE; + #endif + if (!curbuf->b_changed) { int save_msg_scroll = msg_scroll; *** ../vim-6.2.450/src/proto/mbyte.pro Fri Apr 2 11:36:09 2004 --- src/proto/mbyte.pro Fri Apr 2 12:50:18 2004 *************** *** 78,83 **** --- 78,84 ---- void im_shutdown __ARGS((void)); int xim_get_status_area_height __ARGS((void)); int im_get_status __ARGS((void)); + int im_is_preediting __ARGS((void)); int convert_setup __ARGS((vimconv_T *vcp, char_u *from, char_u *to)); int convert_input __ARGS((char_u *ptr, int len, int maxlen)); int convert_input_safe __ARGS((char_u *ptr, int len, int maxlen, char_u **restp, int *restlenp)); *** ../vim-6.2.450/src/screen.c Sun Apr 4 12:06:41 2004 --- src/screen.c Mon Apr 5 20:22:46 2004 *************** *** 3745,3761 **** char_attr = extra_attr; #if defined(FEAT_XIM) && defined(FEAT_GUI_GTK) if (xic != NULL && lnum == curwin->w_cursor.lnum && (State & INSERT) - && im_get_status() && !p_imdisable ! && preedit_start_col != MAXCOL && draw_state == WL_LINE) { colnr_T tcol; ! getvcol(curwin, &(curwin->w_cursor), &tcol, NULL, NULL); if ((long)preedit_start_col <= vcol && vcol < (long)tcol) { if (feedback_old_attr < 0) --- 3745,3766 ---- char_attr = extra_attr; #if defined(FEAT_XIM) && defined(FEAT_GUI_GTK) + /* XIM don't send preedit_start and preedit_end, but they send + * preedit_changed and commit. Thus Vim can't set "im_is_active", use + * im_is_preediting() here. */ if (xic != NULL && lnum == curwin->w_cursor.lnum && (State & INSERT) && !p_imdisable ! && im_is_preediting() && draw_state == WL_LINE) { colnr_T tcol; ! if (preedit_end_col == MAXCOL) ! getvcol(curwin, &(curwin->w_cursor), &tcol, NULL, NULL); ! else ! tcol = preedit_end_col; if ((long)preedit_start_col <= vcol && vcol < (long)tcol) { if (feedback_old_attr < 0) *** ../vim-6.2.450/src/undo.c Tue Mar 9 19:49:08 2004 --- src/undo.c Mon Apr 5 20:23:45 2004 *************** *** 772,777 **** --- 772,781 ---- { if (curbuf->b_u_synced) return; /* already synced */ + #if defined(FEAT_XIM) && defined(FEAT_GUI_GTK) + if (im_is_preediting()) + return; /* XIM is busy, don't break an undo sequence */ + #endif if (p_ul < 0) curbuf->b_u_synced = TRUE; /* no entries, nothing to do */ else *** ../vim-6.2.450/src/version.c Mon Apr 5 19:51:11 2004 --- src/version.c Mon Apr 5 20:08:32 2004 *************** *** 639,640 **** --- 639,642 ---- { /* Add new patch number below this line */ + /**/ + 451, /**/ -- His head smashed in, and his heart cut out, And his liver removed, and his bowels unplugged, And his nostrils raped, and his bottom burned off, And his penis split ... and his ... "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// Sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ Project leader for A-A-P -- http://www.A-A-P.org /// \\\ Buy at Amazon and help AIDS victims -- http://ICCF.nl/click1.html ///