diff --git a/app/tools/gimptexttool-editor.c b/app/tools/gimptexttool-editor.c index ef4bae9d30..acae5be115 100644 --- a/app/tools/gimptexttool-editor.c +++ b/app/tools/gimptexttool-editor.c @@ -95,6 +95,8 @@ static void gimp_text_tool_xy_to_iter (GimpTextTool *text_tool, gdouble y, GtkTextIter *iter); +static void gimp_text_tool_im_preedit_start (GtkIMContext *context, + GimpTextTool *text_tool); static void gimp_text_tool_im_preedit_end (GtkIMContext *context, GimpTextTool *text_tool); static void gimp_text_tool_im_preedit_changed (GtkIMContext *context, @@ -127,6 +129,9 @@ gimp_text_tool_editor_init (GimpTextTool *text_tool) text_tool->overwrite_mode = FALSE; text_tool->x_pos = -1; + g_signal_connect (text_tool->im_context, "preedit-start", + G_CALLBACK (gimp_text_tool_im_preedit_start), + text_tool); g_signal_connect (text_tool->im_context, "preedit-end", G_CALLBACK (gimp_text_tool_im_preedit_end), text_tool); @@ -535,9 +540,6 @@ gimp_text_tool_editor_key_release (GimpTextTool *text_tool, void gimp_text_tool_reset_im_context (GimpTextTool *text_tool) { - /* Cancel any ungoing preedit on reset. */ - gimp_text_tool_im_delete_preedit (text_tool); - if (text_tool->needs_im_reset) { text_tool->needs_im_reset = FALSE; @@ -545,6 +547,28 @@ gimp_text_tool_reset_im_context (GimpTextTool *text_tool) } } +void +gimp_text_tool_abort_im_context (GimpTextTool *text_tool) +{ + GimpTool *tool = GIMP_TOOL (text_tool); + GimpDisplayShell *shell = gimp_display_get_shell (tool->display); + + text_tool->needs_im_reset = TRUE; + gimp_text_tool_reset_im_context (text_tool); + + /* the following lines seem to be the only way of really getting + * rid of any ongoing preedit state, please somebody tell me + * a clean way... mitch + */ + + gtk_im_context_focus_out (text_tool->im_context); + gtk_im_context_set_client_window (text_tool->im_context, NULL); + + gtk_im_context_set_client_window (text_tool->im_context, + gtk_widget_get_window (shell->canvas)); + gtk_im_context_focus_in (text_tool->im_context); +} + void gimp_text_tool_editor_get_cursor_rect (GimpTextTool *text_tool, gboolean overwrite, @@ -1335,11 +1359,24 @@ gimp_text_tool_xy_to_iter (GimpTextTool *text_tool, gtk_text_iter_forward_char (iter); } +static void +gimp_text_tool_im_preedit_start (GtkIMContext *context, + GimpTextTool *text_tool) +{ + GIMP_LOG (TEXT_EDITING, "preedit start"); + + text_tool->preedit_active = TRUE; +} + static void gimp_text_tool_im_preedit_end (GtkIMContext *context, GimpTextTool *text_tool) { gimp_text_tool_delete_selection (text_tool); + + text_tool->preedit_active = FALSE; + + GIMP_LOG (TEXT_EDITING, "preedit end"); } static void @@ -1349,6 +1386,10 @@ gimp_text_tool_im_preedit_changed (GtkIMContext *context, GtkTextBuffer *buffer = GTK_TEXT_BUFFER (text_tool->buffer); PangoAttrList *attrs; + GIMP_LOG (TEXT_EDITING, "preedit changed"); + + gtk_text_buffer_begin_user_action (buffer); + gimp_text_tool_im_delete_preedit (text_tool); gimp_text_tool_delete_selection (text_tool); @@ -1473,6 +1514,8 @@ gimp_text_tool_im_preedit_changed (GtkIMContext *context, } pango_attr_list_unref (attrs); + + gtk_text_buffer_end_user_action (buffer); } static void diff --git a/app/tools/gimptexttool-editor.h b/app/tools/gimptexttool-editor.h index 40bae85c25..5cdf9db946 100644 --- a/app/tools/gimptexttool-editor.h +++ b/app/tools/gimptexttool-editor.h @@ -45,6 +45,7 @@ gboolean gimp_text_tool_editor_key_release (GimpTextTool *text_too GdkEventKey *kevent); void gimp_text_tool_reset_im_context (GimpTextTool *text_tool); +void gimp_text_tool_abort_im_context (GimpTextTool *text_tool); void gimp_text_tool_editor_get_cursor_rect (GimpTextTool *text_tool, gboolean overwrite, diff --git a/app/tools/gimptexttool.c b/app/tools/gimptexttool.c index 01cc9baaf9..aadab32d67 100644 --- a/app/tools/gimptexttool.c +++ b/app/tools/gimptexttool.c @@ -148,6 +148,8 @@ static void gimp_text_tool_text_changed (GimpText *text, static gboolean gimp_text_tool_apply (GimpTextTool *text_tool, gboolean push_undo); +static void gimp_text_tool_apply_list (GimpTextTool *text_tool, + GList *pspecs); static void gimp_text_tool_create_layer (GimpTextTool *text_tool, GimpText *text); @@ -366,7 +368,14 @@ gimp_text_tool_button_press (GimpTool *tool, { gimp_tool_control_activate (tool->control); - gimp_text_tool_reset_im_context (text_tool); + /* clicking anywhere while a preedit is going on aborts the + * preedit, this is ugly but at least leaves everything in + * a consistent state + */ + if (text_tool->preedit_active) + gimp_text_tool_abort_im_context (text_tool); + else + gimp_text_tool_reset_im_context (text_tool); text_tool->selecting = FALSE; @@ -1130,17 +1139,59 @@ gimp_text_tool_proxy_notify (GimpText *text, if ((pspec->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE && pspec->owner_type == GIMP_TYPE_TEXT) { - gimp_text_tool_block_drawing (text_tool); + if (text_tool->preedit_active) + { + /* if there is a preedit going on, don't queue pending + * changes to be idle-applied with undo; instead, flush the + * pending queue (happens only when preedit starts), and + * apply the changes to text_tool->text directly. Preedit + * will *always* end by removing the preedit string, and if + * the preedit was committed, it will insert the resulting + * text, which will not trigger this if() any more. + */ - text_tool->pending = g_list_append (text_tool->pending, (gpointer) pspec); + GList *list = NULL; - if (text_tool->idle_id) - g_source_remove (text_tool->idle_id); + /* if there are pending changes, apply them before applying + * preedit stuff directly (bypassing undo) + */ + if (text_tool->pending) + { + gimp_text_tool_block_drawing (text_tool); + gimp_text_tool_apply (text_tool, TRUE); + } - text_tool->idle_id = - g_idle_add_full (G_PRIORITY_LOW, - gimp_text_tool_apply_idle, text_tool, - NULL); + gimp_text_tool_block_drawing (text_tool); + + list = g_list_append (list, (gpointer) pspec); + gimp_text_tool_apply_list (text_tool, list); + g_list_free (list); + + gimp_text_tool_frame_item (text_tool); + + gimp_image_flush (gimp_item_get_image (GIMP_ITEM (text_tool->layer))); + + gimp_text_tool_unblock_drawing (text_tool); + } + else + { + /* else queue the property change for normal processing, + * including undo + */ + + gimp_text_tool_block_drawing (text_tool); + + text_tool->pending = g_list_append (text_tool->pending, + (gpointer) pspec); + + if (text_tool->idle_id) + g_source_remove (text_tool->idle_id); + + text_tool->idle_id = + g_idle_add_full (G_PRIORITY_LOW, + gimp_text_tool_apply_idle, text_tool, + NULL); + } } } @@ -1151,6 +1202,10 @@ gimp_text_tool_text_notify (GimpText *text, { g_return_if_fail (text == text_tool->text); + /* an undo cancels all preedit operations */ + if (text_tool->preedit_active) + gimp_text_tool_abort_im_context (text_tool); + gimp_text_tool_block_drawing (text_tool); if ((pspec->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE) @@ -1219,8 +1274,6 @@ gimp_text_tool_apply (GimpTextTool *text_tool, const GParamSpec *pspec = NULL; GimpImage *image; GimpTextLayer *layer; - GObject *src; - GObject *dest; GList *list; gboolean undo_group = FALSE; @@ -1299,48 +1352,11 @@ gimp_text_tool_apply (GimpTextTool *text_tool, gimp_image_undo_push_text_layer (image, NULL, layer, pspec); } - src = G_OBJECT (text_tool->proxy); - dest = G_OBJECT (text_tool->text); - - g_signal_handlers_block_by_func (dest, - gimp_text_tool_text_notify, - text_tool); - g_signal_handlers_block_by_func (dest, - gimp_text_tool_text_changed, - text_tool); - - g_object_freeze_notify (dest); - - for (; list; list = g_list_next (list)) - { - GValue value = G_VALUE_INIT; - - /* look ahead and compress changes */ - if (list->next && list->next->data == list->data) - continue; - - pspec = list->data; - - g_value_init (&value, pspec->value_type); - - g_object_get_property (src, pspec->name, &value); - g_object_set_property (dest, pspec->name, &value); - - g_value_unset (&value); - } + gimp_text_tool_apply_list (text_tool, list); g_list_free (text_tool->pending); text_tool->pending = NULL; - g_object_thaw_notify (dest); - - g_signal_handlers_unblock_by_func (dest, - gimp_text_tool_text_notify, - text_tool); - g_signal_handlers_unblock_by_func (dest, - gimp_text_tool_text_changed, - text_tool); - if (push_undo) { g_object_set (layer, "modified", FALSE, NULL); @@ -1358,6 +1374,52 @@ gimp_text_tool_apply (GimpTextTool *text_tool, return FALSE; } +static void +gimp_text_tool_apply_list (GimpTextTool *text_tool, + GList *pspecs) +{ + GObject *src = G_OBJECT (text_tool->proxy); + GObject *dest = G_OBJECT (text_tool->text); + GList *list; + + g_signal_handlers_block_by_func (dest, + gimp_text_tool_text_notify, + text_tool); + g_signal_handlers_block_by_func (dest, + gimp_text_tool_text_changed, + text_tool); + + g_object_freeze_notify (dest); + + for (list = pspecs; list; list = g_list_next (list)) + { + const GParamSpec *pspec; + GValue value = G_VALUE_INIT; + + /* look ahead and compress changes */ + if (list->next && list->next->data == list->data) + continue; + + pspec = list->data; + + g_value_init (&value, pspec->value_type); + + g_object_get_property (src, pspec->name, &value); + g_object_set_property (dest, pspec->name, &value); + + g_value_unset (&value); + } + + g_object_thaw_notify (dest); + + g_signal_handlers_unblock_by_func (dest, + gimp_text_tool_text_notify, + text_tool); + g_signal_handlers_unblock_by_func (dest, + gimp_text_tool_text_changed, + text_tool); +} + static void gimp_text_tool_create_layer (GimpTextTool *text_tool, GimpText *text) diff --git a/app/tools/gimptexttool.h b/app/tools/gimptexttool.h index 0c8a8a1a3a..054a353e92 100644 --- a/app/tools/gimptexttool.h +++ b/app/tools/gimptexttool.h @@ -77,6 +77,7 @@ struct _GimpTextTool GtkIMContext *im_context; gboolean needs_im_reset; + gboolean preedit_active; gchar *preedit_string; gint preedit_cursor; GtkTextMark *preedit_start;