gimp r27444 - in trunk: . app/actions app/tools menus



Author: mitch
Date: Mon Oct 27 16:21:03 2008
New Revision: 27444
URL: http://svn.gnome.org/viewvc/gimp?rev=27444&view=rev

Log:
2008-10-27  Michael Natterer  <mitch gimp org>

	* menus/text-tool-menu.xml
	* app/actions/text-tool-actions.c
	* app/actions/text-tool-commands.[ch]: add "Text along Path" to the
	text tool context menu.

	* app/tools/gimptextoptions.[ch]: remove the text along path
	button here.

	* app/tools/gimptexttool.c: changed accordingly.

	* app/tools/gimptexttool.[ch]: move public functions together,
	move all virtual function implementations together and put them in
	order, made the text along path function public, factor out
	gimp_text_tool_xy_to_offset() instead of duplicaing this code
	three times, remove gimp_rectangle_tool_frame_item() because it
	doesn't belong here.

	* app/tools/gimprectangletool.[ch]: add
	gimp_rectangle_tool_frame_item() here. Enselic, please process ;)



Modified:
   trunk/ChangeLog
   trunk/app/actions/text-tool-actions.c
   trunk/app/actions/text-tool-commands.c
   trunk/app/actions/text-tool-commands.h
   trunk/app/tools/gimprectangletool.c
   trunk/app/tools/gimprectangletool.h
   trunk/app/tools/gimptextoptions.c
   trunk/app/tools/gimptextoptions.h
   trunk/app/tools/gimptexttool.c
   trunk/app/tools/gimptexttool.h
   trunk/menus/text-tool-menu.xml

Modified: trunk/app/actions/text-tool-actions.c
==============================================================================
--- trunk/app/actions/text-tool-actions.c	(original)
+++ trunk/app/actions/text-tool-actions.c	Mon Oct 27 16:21:03 2008
@@ -83,7 +83,13 @@
   { "text-tool-path-from-text", GIMP_STOCK_PATH,
     N_("_Path from Text"), "",
     N_("Create a path from the outlines of the current text"),
-    G_CALLBACK (text_tool_path_from_text_callback),
+    G_CALLBACK (text_tool_path_from_text_cmd_callback),
+    NULL },
+
+  { "text-tool-text-along-path", GIMP_STOCK_PATH,
+    N_("Text along Path"), "",
+    N_("Bend the text along the currently active path"),
+    G_CALLBACK (text_tool_text_along_path_cmd_callback),
     NULL },
 
   { "text-tool-input-methods", NULL,
@@ -134,6 +140,7 @@
   GimpTextTool  *text_tool  = GIMP_TEXT_TOOL (data);
   GimpImage     *image      = GIMP_TOOL (text_tool)->display->image;
   GimpLayer     *layer;
+  GimpVectors   *vectors;
   GtkClipboard  *clipboard;
   gboolean       text_layer = FALSE;
   gboolean       text_sel   = FALSE;   /* some text is selected        */
@@ -144,6 +151,8 @@
   if (layer)
     text_layer = gimp_drawable_is_text_layer (GIMP_DRAWABLE (layer));
 
+  vectors = gimp_image_get_active_vectors (image);
+
   text_sel = gimp_text_tool_get_has_text_selection (text_tool);
 
   /*
@@ -167,4 +176,5 @@
   SET_SENSITIVE ("text-tool-clear",           text_layer);
   SET_SENSITIVE ("text-tool-load",            image);
   SET_SENSITIVE ("text-tool-path-from-text",  text_layer);
+  SET_SENSITIVE ("text-tool-text-along-path", text_layer && vectors);
 }

Modified: trunk/app/actions/text-tool-commands.c
==============================================================================
--- trunk/app/actions/text-tool-commands.c	(original)
+++ trunk/app/actions/text-tool-commands.c	Mon Oct 27 16:21:03 2008
@@ -158,8 +158,8 @@
 }
 
 void
-text_tool_path_from_text_callback (GtkAction *action,
-                                   gpointer   data)
+text_tool_path_from_text_cmd_callback (GtkAction *action,
+                                       gpointer   data)
 {
   GimpTextTool *text_tool = GIMP_TEXT_TOOL (data);
 
@@ -167,6 +167,15 @@
 }
 
 void
+text_tool_text_along_path_cmd_callback (GtkAction *action,
+                                        gpointer   data)
+{
+  GimpTextTool *text_tool = GIMP_TEXT_TOOL (data);
+
+  gimp_text_tool_create_vectors_warped (text_tool);
+}
+
+void
 text_tool_direction_cmd_callback (GtkAction *action,
                                   GtkAction *current,
                                   gpointer   data)

Modified: trunk/app/actions/text-tool-commands.h
==============================================================================
--- trunk/app/actions/text-tool-commands.h	(original)
+++ trunk/app/actions/text-tool-commands.h	Mon Oct 27 16:21:03 2008
@@ -20,23 +20,25 @@
 #define __TEXT_TOOL_COMMANDS_H__
 
 
-void   text_tool_cut_cmd_callback        (GtkAction *action,
-                                          gpointer   data);
-void   text_tool_copy_cmd_callback       (GtkAction *action,
-                                          gpointer   data);
-void   text_tool_paste_cmd_callback      (GtkAction *action,
-                                          gpointer   data);
-void   text_tool_delete_cmd_callback     (GtkAction *action,
-                                          gpointer   data);
-void   text_tool_load_cmd_callback       (GtkAction *action,
-                                          gpointer   data);
-void   text_tool_clear_cmd_callback      (GtkAction *action,
-                                          gpointer   data);
-void   text_tool_path_from_text_callback (GtkAction *action,
-                                          gpointer   data);
-void   text_tool_direction_cmd_callback  (GtkAction *action,
-                                          GtkAction *current,
-                                          gpointer   data);
+void   text_tool_cut_cmd_callback             (GtkAction *action,
+                                               gpointer   data);
+void   text_tool_copy_cmd_callback            (GtkAction *action,
+                                               gpointer   data);
+void   text_tool_paste_cmd_callback           (GtkAction *action,
+                                               gpointer   data);
+void   text_tool_delete_cmd_callback          (GtkAction *action,
+                                               gpointer   data);
+void   text_tool_load_cmd_callback            (GtkAction *action,
+                                               gpointer   data);
+void   text_tool_clear_cmd_callback           (GtkAction *action,
+                                               gpointer   data);
+void   text_tool_path_from_text_cmd_callback  (GtkAction *action,
+                                               gpointer   data);
+void   text_tool_text_along_path_cmd_callback (GtkAction *action,
+                                               gpointer   data);
+void   text_tool_direction_cmd_callback       (GtkAction *action,
+                                               GtkAction *current,
+                                               gpointer   data);
 
 
 #endif /* __TEXT_TOOL_COMMANDS_H__ */

Modified: trunk/app/tools/gimprectangletool.c
==============================================================================
--- trunk/app/tools/gimprectangletool.c	(original)
+++ trunk/app/tools/gimprectangletool.c	Mon Oct 27 16:21:03 2008
@@ -695,6 +695,58 @@
   return inside;
 }
 
+/**
+ * gimp_rectangle_tool_frame_item:
+ * @rect_tool: a #GimpRectangleTool interface
+ * @item:      a #GimpItem attached to the image on which a
+ *             rectangle is being shown.
+ *
+ * Convenience function to set the corners of the rectangle to
+ * match the bounds of the specified item.  The rectangle interface
+ * must be active (i.e., showing a rectangle), and the item must be
+ * attached to the image on which the rectangle is active.
+ **/
+void
+gimp_rectangle_tool_frame_item (GimpRectangleTool *rect_tool,
+                                GimpItem          *item)
+{
+  GimpDisplay *display = GIMP_TOOL (rect_tool)->display;
+  gint         offset_x;
+  gint         offset_y;
+  gint         width;
+  gint         height;
+
+  g_return_if_fail (GIMP_IS_ITEM (item));
+  g_return_if_fail (gimp_item_is_attached (item));
+  g_return_if_fail (display != NULL);
+  g_return_if_fail (display->image == item->image);
+
+  width  = gimp_item_width (item);
+  height = gimp_item_height (item);
+
+  gimp_item_offsets (item, &offset_x, &offset_y);
+
+  gimp_draw_tool_pause (GIMP_DRAW_TOOL (rect_tool));
+
+  gimp_rectangle_tool_set_function (rect_tool,
+                                    GIMP_RECTANGLE_TOOL_CREATING);
+
+  g_object_set (rect_tool,
+                "x1", offset_x,
+                "y1", offset_y,
+                "x2", offset_x + width,
+                "y2", offset_y + height,
+                NULL);
+
+  /* kludge to force handle sizes to update.  This call may be
+   * harmful if this function is ever moved out of the text tool code.
+   */
+  gimp_rectangle_tool_set_constraint (rect_tool,
+                                      GIMP_RECTANGLE_CONSTRAIN_NONE);
+
+  gimp_draw_tool_resume (GIMP_DRAW_TOOL (rect_tool));
+}
+
 void
 gimp_rectangle_tool_set_property (GObject      *object,
                                   guint         property_id,

Modified: trunk/app/tools/gimprectangletool.h
==============================================================================
--- trunk/app/tools/gimprectangletool.h	(original)
+++ trunk/app/tools/gimprectangletool.h	Mon Oct 27 16:21:03 2008
@@ -143,6 +143,10 @@
 gboolean    gimp_rectangle_tool_point_in_rectangle  (GimpRectangleTool       *rect_tool,
                                                      gdouble                  x,
                                                      gdouble                  y);
+void        gimp_rectangle_tool_frame_item          (GimpRectangleTool       *rect_tool,
+                                                     GimpItem                *item);
+
+
 /*  convenience functions  */
 
 void        gimp_rectangle_tool_install_properties  (GObjectClass *klass);

Modified: trunk/app/tools/gimptextoptions.c
==============================================================================
--- trunk/app/tools/gimptextoptions.c	(original)
+++ trunk/app/tools/gimptextoptions.c	Mon Oct 27 16:21:03 2008
@@ -193,8 +193,7 @@
 static void
 gimp_text_options_init (GimpTextOptions *options)
 {
-  options->size_entry           = NULL;
-  options->along_vectors_button = NULL;
+  options->size_entry = NULL;
 }
 
 static void
@@ -516,13 +515,6 @@
   gimp_table_attach_stock (GTK_TABLE (table), row++,
                            GIMP_STOCK_LETTER_SPACING, spinbutton, 1, TRUE);
 
-  button = gtk_button_new_with_label (_("Text along Path"));
-  gtk_box_pack_end (GTK_BOX (main_vbox), button, FALSE, FALSE, 0);
-  gtk_widget_set_sensitive (button, FALSE);
-  gtk_widget_show (button);
-
-  options->along_vectors_button = button;
-
   return main_vbox;
 }
 

Modified: trunk/app/tools/gimptextoptions.h
==============================================================================
--- trunk/app/tools/gimptextoptions.h	(original)
+++ trunk/app/tools/gimptextoptions.h	Mon Oct 27 16:21:03 2008
@@ -56,7 +56,6 @@
 
   /*  options gui  */
   GtkWidget             *size_entry;
-  GtkWidget             *along_vectors_button;
 };
 
 

Modified: trunk/app/tools/gimptexttool.c
==============================================================================
--- trunk/app/tools/gimptexttool.c	(original)
+++ trunk/app/tools/gimptexttool.c	Mon Oct 27 16:21:03 2008
@@ -114,6 +114,13 @@
                                                  GimpDisplay       *display,
                                                  const gchar      **ui_path);
 
+static void      gimp_text_tool_draw            (GimpDrawTool      *draw_tool);
+static void      gimp_text_tool_draw_preedit    (GimpDrawTool      *draw_tool);
+static void      gimp_text_tool_draw_selection  (GimpDrawTool      *draw_tool);
+
+static gboolean  gimp_text_tool_rectangle_change_complete
+                                                (GimpRectangleTool *rect_tool);
+
 static void      gimp_text_tool_connect         (GimpTextTool      *text_tool,
                                                  GimpTextLayer     *layer,
                                                  GimpText          *text);
@@ -129,8 +136,6 @@
 static gboolean  gimp_text_tool_idle_apply      (GimpTextTool      *text_tool);
 static void      gimp_text_tool_apply           (GimpTextTool      *text_tool);
 
-static void      gimp_text_tool_create_vectors_warped
-                                                (GimpTextTool      *text_tool);
 static void      gimp_text_tool_create_layer    (GimpTextTool      *text_tool,
                                                  GimpText          *text);
 
@@ -147,19 +152,9 @@
                                                  GimpDrawable      *drawable,
                                                  gboolean           confirm);
 
-static gboolean  gimp_text_tool_rectangle_change_complete
-                                                (GimpRectangleTool *rect_tool);
-void             gimp_rectangle_tool_frame_item (GimpRectangleTool *rect_tool,
-                                                 GimpItem          *item);
-
-static void gimp_text_tool_draw                 (GimpDrawTool      *draw_tool);
-static void gimp_text_tool_draw_preedit_lines   (GimpDrawTool      *draw_tool);
-static void gimp_text_tool_draw_text_selection  (GimpDrawTool      *draw_tool);
-
 static void gimp_text_tool_update_layout        (GimpTextTool      *text_tool);
 static void gimp_text_tool_update_proxy         (GimpTextTool      *text_tool);
 
-static void gimp_text_tool_reset_im_context     (GimpTextTool      *text_tool);
 static void gimp_text_tool_enter_text           (GimpTextTool      *text_tool,
                                                  const gchar       *str);
 static void gimp_text_tool_text_buffer_changed  (GtkTextBuffer     *text_buffer,
@@ -172,8 +167,12 @@
                                                  GParamSpec        *pspec,
                                                  GimpTextTool      *text_tool);
 
-/* IM Context Callbacks
- */
+static gint gimp_text_tool_xy_to_offset         (GimpTextTool      *text_tool,
+                                                 gdouble            x,
+                                                 gdouble            y);
+
+/*  IM context utilities and callbacks  */
+static void gimp_text_tool_reset_im_context     (GimpTextTool      *text_tool);
 static void gimp_text_tool_commit_cb            (GtkIMContext      *context,
                                                  const gchar       *str,
                                                  GimpTextTool      *text_tool);
@@ -496,23 +495,10 @@
 
               if (text_tool->layout)
                 {
-                  GtkTextIter cursor, start, end;
+                  GtkTextIter cursor;
                   gint        offset;
-                  gint        trailing;
-                  gchar      *string;
 
-                  gtk_text_buffer_get_bounds (text_tool->text_buffer,
-                                              &start, &end);
-                  string = gtk_text_buffer_get_text (text_tool->text_buffer,
-                                                     &start, &end, TRUE);
-                  pango_layout_xy_to_index (text_tool->layout->layout,
-                                            x * PANGO_SCALE,
-                                            y * PANGO_SCALE,
-                                            &offset, &trailing);
-                  offset = g_utf8_pointer_to_offset (string, string + offset);
-                  offset += trailing;
-
-                  g_free (string);
+                  offset = gimp_text_tool_xy_to_offset (text_tool, x, y);
 
                   gtk_text_buffer_get_iter_at_offset (text_tool->text_buffer,
                                                       &cursor, offset);
@@ -618,24 +604,10 @@
               GimpItem   *item = GIMP_ITEM (text_tool->layer);
               gdouble     x    = coords->x - item->offset_x;
               gdouble     y    = coords->y - item->offset_y;
-              GtkTextIter cursor, start, end;
-              gint        offset, trailing;
-              gchar      *string;
-
-              gtk_text_buffer_get_bounds (text_tool->text_buffer, &start, &end);
-
-              string = gtk_text_buffer_get_text (text_tool->text_buffer,
-                                                 &start, &end, TRUE);
-
-              pango_layout_xy_to_index (text_tool->layout->layout,
-                                        x * PANGO_SCALE,
-                                        y * PANGO_SCALE,
-                                        &offset, &trailing);
+              GtkTextIter cursor;
+              gint        offset;
 
-              offset = g_utf8_pointer_to_offset (string, string + offset);
-              offset += trailing;
-
-              g_free (string);
+              offset = gimp_text_tool_xy_to_offset (text_tool, x, y);
 
               gtk_text_buffer_get_iter_at_offset (text_tool->text_buffer,
                                                   &cursor, offset);
@@ -691,26 +663,12 @@
           gdouble      x    = coords->x - item->offset_x;
           gdouble      y    = coords->y - item->offset_y;
           GtkTextIter  cursor;
-          GtkTextIter  start, end;
           GtkTextIter  old_selection_bound;
           GtkTextMark *selection_mark;
-          gint         offset, trailing;
+          gint         offset;
           gint         old_cursor_offset;
-          gchar       *string;
-
-          gtk_text_buffer_get_bounds (text_tool->text_buffer, &start, &end);
-          string = gtk_text_buffer_get_text (text_tool->text_buffer,
-                                             &start, &end, TRUE);
-
-          pango_layout_xy_to_index (text_tool->layout->layout,
-                                    x * PANGO_SCALE,
-                                    y * PANGO_SCALE,
-                                    &offset, &trailing);
-
-          offset = g_utf8_pointer_to_offset (string, string + offset);
-          offset += trailing;
 
-          g_free (string);
+          offset = gimp_text_tool_xy_to_offset (text_tool, x, y);
 
           selection_mark = gtk_text_buffer_get_selection_bound (text_tool->text_buffer);
 
@@ -1051,456 +1009,278 @@
 }
 
 static void
-gimp_text_tool_connect (GimpTextTool  *text_tool,
-                        GimpTextLayer *layer,
-                        GimpText      *text)
+gimp_text_tool_draw (GimpDrawTool *draw_tool)
 {
-  GimpTool *tool = GIMP_TOOL (text_tool);
+  GimpTextTool     *text_tool = GIMP_TEXT_TOOL (draw_tool);
+  GimpTool         *tool      = GIMP_TOOL (draw_tool);
+  GdkRectangle      cliprect;
+  gint              width, height;
+  gint              x1, x2;
+  gint              y1, y2;
+  GtkTextIter       start;
 
-  g_return_if_fail (text == NULL || (layer != NULL && layer->text == text));
+  g_object_set (text_tool,
+                "narrow-mode", TRUE,
+                NULL);
 
-  if (text_tool->text != text)
-    {
-      GimpTextOptions *options = GIMP_TEXT_TOOL_GET_OPTIONS (tool);
+  gimp_rectangle_tool_draw (draw_tool);
 
-      if (text_tool->text)
-        {
-          g_signal_handlers_disconnect_by_func (text_tool->text,
-                                                gimp_text_tool_text_notify,
-                                                text_tool);
+  if (! text_tool->layer ||
+      ! text_tool->layer->text)
+    return;
 
-          if (text_tool->pending)
-            gimp_text_tool_apply (text_tool);
+  /* There will be no layout if the function is called from the wrong place */
+  if (! text_tool->layout)
+    gimp_text_tool_update_layout (text_tool);
 
-          if (options->along_vectors_button)
-            {
-              gtk_widget_set_sensitive (options->along_vectors_button,
-                                        FALSE);
-              g_signal_handlers_disconnect_by_func (options->along_vectors_button,
-                                                    gimp_text_tool_create_vectors_warped,
-                                                    text_tool);
-            }
+  g_object_get (text_tool,
+                "x1", &x1,
+                "y1", &y1,
+                "x2", &x2,
+                "y2", &y2,
+                NULL);
 
-          g_object_unref (text_tool->text);
-          text_tool->text = NULL;
+  /* Turn on clipping for text-cursor and selections */
+  cliprect.x      = x1;
+  cliprect.width  = x2 - x1;
+  cliprect.y      = y1;
+  cliprect.height = y2 - y1;
+  gimp_draw_tool_set_clip_rect (draw_tool, &cliprect, FALSE);
 
-          g_object_set (text_tool->proxy, "text", NULL, NULL);
-        }
+  gtk_text_buffer_get_start_iter (text_tool->text_buffer, &start);
 
-      gimp_context_define_property (GIMP_CONTEXT (options),
-                                    GIMP_CONTEXT_PROP_FOREGROUND,
-                                    text != NULL);
+  if (! gtk_text_buffer_get_has_selection (text_tool->text_buffer))
+    {
+      /* If the text buffer has no selection, draw the text cursor */
 
-      if (text)
-        {
-          gimp_config_sync (G_OBJECT (text), G_OBJECT (text_tool->proxy), 0);
+      gint            cursorx;
+      GtkTextIter     cursor;
+      PangoRectangle  crect;
+      gchar          *string;
 
-          text_tool->text = g_object_ref (text);
+      gtk_text_buffer_get_iter_at_mark (text_tool->text_buffer, &cursor,
+                                        gtk_text_buffer_get_insert (text_tool->text_buffer));
 
-          g_signal_connect (text, "notify",
-                            G_CALLBACK (gimp_text_tool_text_notify),
-                            text_tool);
+      string = gtk_text_buffer_get_text (text_tool->text_buffer,
+                                         &start, &cursor, FALSE);
 
-          if (options->along_vectors_button)
-            {
-              g_signal_connect_swapped (options->along_vectors_button, "clicked",
-                                        G_CALLBACK (gimp_text_tool_create_vectors_warped),
-                                        text_tool);
-              gtk_widget_set_sensitive (options->along_vectors_button, TRUE);
-            }
-        }
-    }
+      /* Using strlen to get the byte index, not the character offset */
+      cursorx = strlen (string);
 
-  if (text_tool->layer != layer)
-    {
-      if (text_tool->layer)
-        g_signal_handlers_disconnect_by_func (text_tool->layer,
-                                              gimp_text_tool_layer_notify,
-                                              text_tool);
+      /* TODO: make cursor position itself even inside preedits! */
+      if (text_tool->preedit_len > 0)
+        cursorx += text_tool->preedit_len;
 
-      text_tool->layer = layer;
+      g_free (string);
 
-      if (layer)
-        g_signal_connect_object (text_tool->layer, "notify::modified",
-                                 G_CALLBACK (gimp_text_tool_layer_notify),
-                                 text_tool, 0);
-    }
-}
+      pango_layout_index_to_pos (text_tool->layout->layout, cursorx, &crect);
 
-static void
-gimp_text_tool_use_editor_notify (GimpTextOptions *options,
-                                  GParamSpec      *pspec,
-                                  GimpTextTool    *text_tool)
-{
-  if (options->use_editor)
-    {
-      if (text_tool->text && text_tool->text_buffer)
-        gimp_text_tool_editor (text_tool);
+      crect.x      = PANGO_PIXELS (crect.x);
+      crect.y      = PANGO_PIXELS (crect.y);
+      crect.height = PANGO_PIXELS (crect.height);
+
+      gimp_draw_tool_draw_rectangle (draw_tool, TRUE,
+                                     crect.x, crect.y,
+                                     4, crect.height,
+                                     TRUE);
+
+      if (text_tool->preedit_string && text_tool->preedit_len > 0)
+        gimp_text_tool_draw_preedit (draw_tool);
     }
   else
     {
-      if (text_tool->editor)
-        gtk_widget_destroy (text_tool->editor);
+      /* If the text buffer has a selection, highlight the
+       * selected letters*/
+
+      gimp_text_tool_draw_selection (draw_tool);
     }
-}
 
-static void
-gimp_text_tool_layer_notify (GimpTextLayer *layer,
-                             GParamSpec    *pspec,
-                             GimpTextTool  *text_tool)
-{
-  if (layer->modified)
-    gimp_text_tool_connect (text_tool, NULL, NULL);
+  /* Turn off clipping when done */
+  gimp_draw_tool_set_clip_rect (draw_tool, NULL, FALSE);
 }
 
 static void
-gimp_text_tool_proxy_notify (GimpText     *text,
-                             GParamSpec   *pspec,
-                             GimpTextTool *text_tool)
+gimp_text_tool_draw_preedit (GimpDrawTool *draw_tool)
 {
-  if (! text_tool->text)
-    return;
+  GimpTextTool    *text_tool = GIMP_TEXT_TOOL (draw_tool);
+  PangoLayout     *layout;
+  PangoLayoutIter *line_iter;
+  GtkTextIter      cursor, start;
+  gint             i;
+  gint             min, max;
+  gchar           *string;
 
-  if ((pspec->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE)
-    {
-      text_tool->pending = g_list_append (text_tool->pending, pspec);
+  gtk_text_buffer_get_selection_bounds (text_tool->text_buffer, &cursor, NULL);
+  gtk_text_buffer_get_start_iter (text_tool->text_buffer, &start);
 
-      if (text_tool->idle_id)
-        g_source_remove (text_tool->idle_id);
+  string = gtk_text_buffer_get_text (text_tool->text_buffer,
+                                     &start, &cursor, FALSE);
+  min = strlen (string);
+  g_free (string);
 
-      text_tool->idle_id =
-        g_idle_add_full (G_PRIORITY_LOW,
-                         (GSourceFunc) gimp_text_tool_idle_apply, text_tool,
-                         NULL);
-    }
-}
+  max = min + text_tool->preedit_len;
 
-static void
-gimp_text_tool_text_notify (GimpText     *text,
-                            GParamSpec   *pspec,
-                            GimpTextTool *text_tool)
-{
-  g_return_if_fail (text == text_tool->text);
+  layout = text_tool->layout->layout;
+  line_iter = pango_layout_get_iter (layout);
+  i = 0;
 
-  if ((pspec->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE)
+  do
     {
-      GValue value = { 0, };
+      gint firstline, lastline;
+      gint first_x,   last_x;
 
-      g_value_init (&value, pspec->value_type);
+      pango_layout_index_to_line_x (layout, min, 0, &firstline, &first_x);
+      pango_layout_index_to_line_x (layout, max, 0, &lastline, &last_x);
 
-      g_object_get_property (G_OBJECT (text), pspec->name, &value);
+      if (i >= firstline && i <= lastline)
+        {
+          PangoRectangle crect;
 
-      g_signal_handlers_block_by_func (text_tool->proxy,
-                                       gimp_text_tool_proxy_notify,
-                                       text_tool);
+          pango_layout_iter_get_line_extents (line_iter, NULL, &crect);
+          pango_extents_to_pixels (&crect, NULL);
 
-      g_object_set_property (G_OBJECT (text_tool->proxy), pspec->name, &value);
+          gimp_draw_tool_draw_line (draw_tool,
+                                    crect.x, crect.y + crect.height,
+                                    crect.x + crect.width,
+                                    crect.y + crect.height,
+                                    TRUE);
+          if (i == firstline)
+            {
+              PangoRectangle crect2 = crect;
 
-      g_signal_handlers_unblock_by_func (text_tool->proxy,
-                                         gimp_text_tool_proxy_notify,
-                                         text_tool);
+              crect2.width = PANGO_PIXELS (first_x) - crect.x;
+              crect2.x     = crect.x;
 
-      g_value_unset (&value);
-    }
+              gimp_draw_tool_draw_line (draw_tool,
+                                        crect2.x, crect2.y + crect2.height,
+                                        crect2.width,
+                                        crect2.y + crect2.height,
+                                        TRUE);
+            }
 
-  /* we need to redraw the rectangle if it is visible and the shape of
-     the layer has changed, because of an undo for example. */
-  if (strcmp (pspec->name, "box-width") == 0  ||
-      strcmp (pspec->name, "box-height") == 0 ||
-      text->box_mode == GIMP_TEXT_BOX_DYNAMIC)
-    {
-      GimpRectangleTool *rect_tool = GIMP_RECTANGLE_TOOL (text_tool);
+          if (i == lastline)
+            {
+              PangoRectangle crect2 = crect;
 
-      text_tool->handle_rectangle_change_complete = FALSE;
-      gimp_rectangle_tool_frame_item (rect_tool,
-                                      GIMP_ITEM (text_tool->layer));
-      text_tool->handle_rectangle_change_complete = TRUE;
+              crect2.width = crect.x + crect.width - PANGO_PIXELS (last_x);
+              crect2.x     = PANGO_PIXELS (last_x);
+
+              gimp_draw_tool_draw_line (draw_tool,
+                                        crect2.x, crect2.y + crect2.height,
+                                        crect2.x + crect2.width,
+                                        crect2.y + crect2.height,
+                                        TRUE);
+            }
+        }
+
+      i++;
     }
+  while (pango_layout_iter_next_line (line_iter));
 
-  /* if the text has changed, (probably because of an undo), we put
-   * the new text into the text buffer
-   */
-  if (strcmp (pspec->name, "text") == 0)
-    {
-      g_signal_handlers_block_by_func (text_tool->proxy,
-                                       gimp_text_tool_text_buffer_changed,
-                                       text_tool);
-
-      gtk_text_buffer_set_text (text_tool->text_buffer, text->text, -1);
-
-      g_signal_handlers_unblock_by_func (text_tool->proxy,
-                                         gimp_text_tool_text_buffer_changed,
-                                         text_tool);
-
-      /* force change of cursor and selection display */
-      gimp_text_tool_update_layout (text_tool);
-/*       gimp_text_tool_update_proxy (text_tool); */
-      return;
-    }
-}
-
-static gboolean
-gimp_text_tool_idle_apply (GimpTextTool *text_tool)
-{
-  text_tool->idle_id = 0;
-
-  gimp_text_tool_apply (text_tool);
-
-  return FALSE;
+  pango_layout_iter_free (line_iter);
 }
 
 static void
-gimp_text_tool_apply (GimpTextTool *text_tool)
+gimp_text_tool_draw_selection (GimpDrawTool *draw_tool)
 {
-  const GParamSpec *pspec = NULL;
-  GimpImage        *image;
-  GimpTextLayer    *layer;
-  GObject          *src;
-  GObject          *dest;
-  GList            *list;
-  gboolean          push_undo  = TRUE;
-  gboolean          undo_group = FALSE;
-
-  if (text_tool->idle_id)
-    {
-      g_source_remove (text_tool->idle_id);
-      text_tool->idle_id = 0;
-    }
-
-  g_return_if_fail (text_tool->text != NULL);
-  g_return_if_fail (text_tool->layer != NULL);
+  GimpTextTool    *text_tool = GIMP_TEXT_TOOL (draw_tool);
+  PangoLayout     *layout;
+  PangoLayoutIter *line_iter;
+  GtkTextIter      start;
+  GtkTextIter      sel_start, sel_end;
+  gint             i;
+  gint             min, max;
+  gchar           *string;
 
-  layer = text_tool->layer;
-  image = gimp_item_get_image (GIMP_ITEM (layer));
+  gtk_text_buffer_get_selection_bounds (text_tool->text_buffer,
+                                        &sel_start, &sel_end);
+  gtk_text_buffer_get_start_iter (text_tool->text_buffer, &start);
 
-  g_return_if_fail (layer->text == text_tool->text);
+  string = gtk_text_buffer_get_text (text_tool->text_buffer,
+                                     &start, &sel_start, FALSE);
+  min = strlen (string);
+  g_free (string);
 
-  /*  Walk over the list of changes and figure out if we are changing
-   *  a single property or need to push a full text undo.
-   */
-  for (list = text_tool->pending;
-       list && list->next && list->next->data == list->data;
-       list = list->next)
-    /* do nothing */;
+  string = gtk_text_buffer_get_text (text_tool->text_buffer,
+                                     &start, &sel_end, FALSE);
+  max = strlen (string);
+  g_free (string);
 
-  if (g_list_length (list) == 1)
-    pspec = list->data;
+  layout = text_tool->layout->layout;
+  line_iter = pango_layout_get_iter (layout);
+  i = 0;
 
-  /*  If we are changing a single property, we don't need to push
-   *  an undo if all of the following is true:
-   *   - the redo stack is empty
-   *   - the last item on the undo stack is a text undo
-   *   - the last undo changed the same text property on the same layer
-   *   - the last undo happened less than TEXT_UNDO_TIMEOUT seconds ago
+  /* Invert the selected letters by inverting all
+   * lines containing selected letters, then
+   * invert the unselected letters on these lines
+   * a second time to make them look normal
    */
-  if (pspec)
+  do
     {
-      GimpUndo *undo = gimp_image_undo_can_compress (image, GIMP_TYPE_TEXT_UNDO,
-                                                     GIMP_UNDO_TEXT_LAYER);
-
-      if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (layer))
-        {
-          GimpTextUndo *text_undo = GIMP_TEXT_UNDO (undo);
-
-          if (text_undo->pspec == pspec)
-            {
-              if (gimp_undo_get_age (undo) < TEXT_UNDO_TIMEOUT)
-                {
-                  GimpTool *tool = GIMP_TOOL (text_tool);
+      gint firstline, lastline;
+      gint first_x,   last_x;
 
-                  push_undo = FALSE;
-                  gimp_undo_reset_age (undo);
-                  gimp_undo_refresh_preview (undo,
-                                             GIMP_CONTEXT (gimp_tool_get_options (tool)));
-                }
-            }
-        }
-    }
+      pango_layout_index_to_line_x (layout, min, 0, &firstline, &first_x);
+      pango_layout_index_to_line_x (layout, max, 0, &lastline, &last_x);
 
-  if (push_undo)
-    {
-      if (layer->modified)
+      if (i >= firstline && i <= lastline)
         {
-          undo_group = TRUE;
-          gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TEXT, NULL);
-
-          gimp_image_undo_push_text_layer_modified (image, NULL, layer);
-          gimp_image_undo_push_drawable_mod (image,
-                                             NULL, GIMP_DRAWABLE (layer));
-        }
-
-      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_object_freeze_notify (dest);
-
-  for (; list; list = list->next)
-    {
-      GValue  value = { 0, };
-
-      /*  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_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);
-
-  if (push_undo)
-    {
-      g_object_set (layer, "modified", FALSE, NULL);
-
-      if (undo_group)
-        gimp_image_undo_group_end (image);
-    }
-
-  /* if we're doing dynamic text, we want to update the
-   * shape of the rectangle */
-  if (layer->text->box_mode == GIMP_TEXT_BOX_DYNAMIC)
-    {
-      text_tool->handle_rectangle_change_complete = FALSE;
-      gimp_rectangle_tool_frame_item (GIMP_RECTANGLE_TOOL (text_tool),
-                                      GIMP_ITEM (layer));
-      text_tool->handle_rectangle_change_complete = TRUE;
-    }
+          PangoRectangle crect;
 
-  gimp_image_flush (image);
-  gimp_text_tool_update_layout (text_tool);
-}
+          pango_layout_iter_get_line_extents (line_iter, NULL, &crect);
+          pango_extents_to_pixels (&crect, NULL);
 
-void
-gimp_text_tool_create_vectors (GimpTextTool *text_tool)
-{
-  GimpVectors *vectors;
+          gimp_draw_tool_draw_rectangle (draw_tool, TRUE,
+                                         crect.x, crect.y,
+                                         crect.width, crect.height,
+                                         TRUE);
+          if (i == firstline)
+            {
+              /* Twice invert all letters before the selection,
+               * making them look normal
+               */
+              PangoRectangle crect2 = crect;
 
-  if (! text_tool->text || ! text_tool->image)
-    return;
+              crect2.width = PANGO_PIXELS (first_x) - crect.x;
+              crect2.x     = crect.x;
 
-  vectors = gimp_text_vectors_new (text_tool->image, text_tool->text);
+              gimp_draw_tool_draw_rectangle (draw_tool, TRUE,
+                                             crect2.x, crect2.y,
+                                             crect2.width, crect2.height,
+                                             TRUE);
+            }
+          if (i == lastline)
+            {
+              /* Twice invert all letters after the selection,
+               * making them look normal
+               */
+              PangoRectangle crect2 = crect;
 
-  if (text_tool->layer)
-    {
-      gint x, y;
+              crect2.width = crect.x + crect.width - PANGO_PIXELS (last_x);
+              crect2.x     = PANGO_PIXELS (last_x);
 
-      gimp_item_offsets (GIMP_ITEM (text_tool->layer), &x, &y);
-      gimp_item_translate (GIMP_ITEM (vectors), x, y, FALSE);
+              gimp_draw_tool_draw_rectangle (draw_tool, TRUE,
+                                             crect2.x, crect2.y,
+                                             crect2.width, crect2.height,
+                                             TRUE);
+            }
+        }
+      i++;
     }
-
-  gimp_image_add_vectors (text_tool->image, vectors, -1, TRUE);
-
-  gimp_image_flush (text_tool->image);
-}
-
-static void
-gimp_text_tool_create_vectors_warped (GimpTextTool *text_tool)
-{
-  GimpVectors   *vectors0;
-  GimpVectors   *vectors;
-  GimpText      *text      = text_tool->text;
-  gdouble        box_height;
-
-  if (! text || ! text_tool->image || ! text_tool->layer)
-    return;
-
-  box_height = gimp_item_height (GIMP_ITEM (text_tool->layer));
-
-  vectors0 = gimp_image_get_active_vectors (text_tool->image);
-  if (! vectors0)
-    return;
-
-  vectors = gimp_text_vectors_new (text_tool->image, text_tool->text);
-
-  gimp_vectors_warp_vectors (vectors0, vectors, 0.5 * box_height);
-
-  gimp_image_add_vectors (text_tool->image, vectors, -1, TRUE);
-  gimp_image_set_active_vectors (text_tool->image, vectors);
-  gimp_item_set_visible (GIMP_ITEM (vectors), TRUE, FALSE);
-
-  gimp_image_flush (text_tool->image);
+  while (pango_layout_iter_next_line (line_iter));
+  pango_layout_iter_free (line_iter);
 }
 
-static void
-gimp_text_tool_create_layer (GimpTextTool *text_tool,
-                             GimpText     *text)
+static gboolean
+gimp_text_tool_rectangle_change_complete (GimpRectangleTool *rect_tool)
 {
-  GimpTool  *tool = GIMP_TOOL (text_tool);
-  GimpImage *image;
-  GimpLayer *layer;
-
-  if (text)
-    {
-      text = gimp_config_duplicate (GIMP_CONFIG (text));
-    }
-  else
-    {
-      gchar *str = gimp_text_tool_canvas_editor_get_text (text_tool);
-
-      g_object_set (text_tool->proxy,
-                    "text",     str,
-                    "box-mode", GIMP_TEXT_BOX_DYNAMIC,
-                    NULL);
-
-      g_free (str);
-
-      text = gimp_config_duplicate (GIMP_CONFIG (text_tool->proxy));
-    }
-
-  image = tool->display->image;
-  layer = gimp_text_layer_new (image, text);
-
-  g_object_unref (text);
-
-  if (! layer)
-    return;
-
-  gimp_text_tool_connect (text_tool, GIMP_TEXT_LAYER (layer), text);
-
-  gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TEXT,
-                               _("Add Text Layer"));
-
-  if (gimp_image_floating_sel (image))
-    {
-      g_signal_handlers_block_by_func (image,
-                                       gimp_text_tool_layer_changed,
-                                       text_tool);
-
-      floating_sel_anchor (gimp_image_floating_sel (image));
-
-      g_signal_handlers_unblock_by_func (image,
-                                         gimp_text_tool_layer_changed,
-                                         text_tool);
-    }
-
-  GIMP_ITEM (layer)->offset_x = text_tool->x1;
-  GIMP_ITEM (layer)->offset_y = text_tool->y1;
-
-  gimp_image_add_layer (image, layer, -1, TRUE);
+  GimpTextTool *text_tool = GIMP_TEXT_TOOL (rect_tool);
 
-  if (text_tool->text_box_fixed)
+  if (text_tool->handle_rectangle_change_complete)
     {
-      GimpRectangleTool *rect_tool = GIMP_RECTANGLE_TOOL (text_tool);
-      GimpItem          *item      = GIMP_ITEM (layer);
-      gint               x1, y1, x2, y2;
+      GimpText *text = text_tool->text;
+      GimpItem *item;
+      gint      x1, y1, x2, y2;
 
       g_object_get (rect_tool,
                     "x1", &x1,
@@ -1508,395 +1288,428 @@
                     "x2", &x2,
                     "y2", &y2,
                     NULL);
+
+      text_tool->text_box_fixed = TRUE;
+
+      if (! text || ! text->text || (text->text[0] == 0))
+        {
+          /*
+           * we can't set properties for the text layer, because
+           * it isn't created until some text has been inserted,
+           * so we need to make a special note that will remind
+           * us what to do when we actually create the layer
+           */
+          return TRUE;
+        }
+
       g_object_set (text_tool->proxy,
                     "box-mode",   GIMP_TEXT_BOX_FIXED,
                     "box-width",  (gdouble) (x2 - x1),
                     "box-height", (gdouble) (y2 - y1),
                     NULL);
+
+      gimp_image_undo_group_start (text_tool->image, GIMP_UNDO_GROUP_TEXT,
+                                   _("Reshape Text Layer"));
+
+      item = GIMP_ITEM (text_tool->layer);
       gimp_item_translate (item,
                            x1 - item->offset_x,
                            y1 - item->offset_y,
                            TRUE);
-    }
-  else
-    {
-      text_tool->handle_rectangle_change_complete = FALSE;
-      gimp_rectangle_tool_frame_item (GIMP_RECTANGLE_TOOL (text_tool),
-                                      GIMP_ITEM (layer));
-      text_tool->handle_rectangle_change_complete = TRUE;
+      gimp_text_tool_apply (text_tool);
+
+      gimp_image_undo_group_end (text_tool->image);
     }
 
-  gimp_image_undo_group_end (image);
+  return TRUE;
+}
 
-  gimp_image_flush (image);
 
-  gimp_text_tool_set_drawable (text_tool, GIMP_DRAWABLE (layer), FALSE);
-}
+/*  private functions  */
 
 static void
-gimp_text_tool_canvas_editor (GimpTextTool *text_tool)
+gimp_text_tool_connect (GimpTextTool  *text_tool,
+                        GimpTextLayer *layer,
+                        GimpText      *text)
 {
-  GimpTool        *tool    = GIMP_TOOL (text_tool);
-  GimpTextOptions *options = GIMP_TEXT_TOOL_GET_OPTIONS (text_tool);
+  GimpTool *tool = GIMP_TOOL (text_tool);
 
-  gtk_im_context_set_client_window (text_tool->im_context,
-                                    GIMP_DISPLAY_SHELL (tool->display->shell)->canvas->window);
+  g_return_if_fail (text == NULL || (layer != NULL && layer->text == text));
 
-  gtk_im_context_focus_in (text_tool->im_context);
+  if (text_tool->text != text)
+    {
+      GimpTextOptions *options = GIMP_TEXT_TOOL_GET_OPTIONS (tool);
 
-  gimp_text_tool_update_layout (text_tool);
+      if (text_tool->text)
+        {
+          g_signal_handlers_disconnect_by_func (text_tool->text,
+                                                gimp_text_tool_text_notify,
+                                                text_tool);
 
-  if (options->use_editor)
-    gimp_text_tool_editor (text_tool);
-}
+          if (text_tool->pending)
+            gimp_text_tool_apply (text_tool);
 
-static void
-gimp_text_tool_editor (GimpTextTool *text_tool)
-{
-  GimpTextOptions   *options = GIMP_TEXT_TOOL_GET_OPTIONS (text_tool);
-  GimpDialogFactory *dialog_factory;
-  GtkWindow         *parent  = NULL;
+          g_object_unref (text_tool->text);
+          text_tool->text = NULL;
 
-  if (text_tool->editor)
-    {
-      gtk_window_present (GTK_WINDOW (text_tool->editor));
-      return;
-    }
+          g_object_set (text_tool->proxy, "text", NULL, NULL);
+        }
 
-  dialog_factory = gimp_dialog_factory_from_name ("toplevel");
+      gimp_context_define_property (GIMP_CONTEXT (options),
+                                    GIMP_CONTEXT_PROP_FOREGROUND,
+                                    text != NULL);
 
-  if (GIMP_TOOL (text_tool)->display)
-    parent = GTK_WINDOW (GIMP_TOOL (text_tool)->display->shell);
+      if (text)
+        {
+          gimp_config_sync (G_OBJECT (text), G_OBJECT (text_tool->proxy), 0);
 
-  text_tool->editor = gimp_text_options_editor_new (parent, options,
-                                                    dialog_factory->menu_factory,
-                                                    _("GIMP Text Editor"),
-                                                    text_tool->text_buffer);
+          text_tool->text = g_object_ref (text);
 
-  g_object_add_weak_pointer (G_OBJECT (text_tool->editor),
-                             (gpointer) &text_tool->editor);
+          g_signal_connect (text, "notify",
+                            G_CALLBACK (gimp_text_tool_text_notify),
+                            text_tool);
+        }
+    }
 
-  gimp_dialog_factory_add_foreign (dialog_factory,
-                                   "gimp-text-tool-dialog",
-                                   text_tool->editor);
+  if (text_tool->layer != layer)
+    {
+      if (text_tool->layer)
+        g_signal_handlers_disconnect_by_func (text_tool->layer,
+                                              gimp_text_tool_layer_notify,
+                                              text_tool);
 
-  gtk_widget_show (text_tool->editor);
+      text_tool->layer = layer;
+
+      if (layer)
+        g_signal_connect_object (text_tool->layer, "notify::modified",
+                                 G_CALLBACK (gimp_text_tool_layer_notify),
+                                 text_tool, 0);
+    }
 }
 
-static gchar *
-gimp_text_tool_canvas_editor_get_text (GimpTextTool *text_tool)
+static void
+gimp_text_tool_use_editor_notify (GimpTextOptions *options,
+                                  GParamSpec      *pspec,
+                                  GimpTextTool    *text_tool)
 {
-  if (text_tool->text_buffer)
+  if (options->use_editor)
     {
-      GtkTextIter  start, end;
-      GtkTextIter  selstart, selend;
-      gchar       *string;
-      gchar       *fb;
-      gchar       *lb;
+      if (text_tool->text && text_tool->text_buffer)
+        gimp_text_tool_editor (text_tool);
+    }
+  else
+    {
+      if (text_tool->editor)
+        gtk_widget_destroy (text_tool->editor);
+    }
+}
 
-      gtk_text_buffer_get_bounds (text_tool->text_buffer, &start, &end);
-      gtk_text_buffer_get_selection_bounds (text_tool->text_buffer,
-                                            &selstart, &selend);
+static void
+gimp_text_tool_layer_notify (GimpTextLayer *layer,
+                             GParamSpec    *pspec,
+                             GimpTextTool  *text_tool)
+{
+  if (layer->modified)
+    gimp_text_tool_connect (text_tool, NULL, NULL);
+}
 
-      fb = gtk_text_buffer_get_text (text_tool->text_buffer,
-                                     &start, &selstart, TRUE);
-      lb = gtk_text_buffer_get_text (text_tool->text_buffer,
-                                     &selstart, &end, TRUE);
+static void
+gimp_text_tool_proxy_notify (GimpText     *text,
+                             GParamSpec   *pspec,
+                             GimpTextTool *text_tool)
+{
+  if (! text_tool->text)
+    return;
 
-      if (text_tool->preedit_string)
-        {
-          if (fb == NULL)
-            {
-              string = g_strconcat (text_tool->preedit_string, lb, NULL);
-            }
-          else
-            {
-              string = g_strconcat (fb, text_tool->preedit_string, lb, NULL);
-            }
-        }
-      else
-        {
-          string = g_strconcat (fb, lb, NULL);
-        }
+  if ((pspec->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE)
+    {
+      text_tool->pending = g_list_append (text_tool->pending, pspec);
 
-      g_free (fb);
-      g_free (lb);
+      if (text_tool->idle_id)
+        g_source_remove (text_tool->idle_id);
 
-      return string;
+      text_tool->idle_id =
+        g_idle_add_full (G_PRIORITY_LOW,
+                         (GSourceFunc) gimp_text_tool_idle_apply, text_tool,
+                         NULL);
     }
-
-  return NULL;
 }
 
-
-#define  RESPONSE_NEW 1
-
 static void
-gimp_text_tool_confirm_response (GtkWidget    *widget,
-                                 gint          response_id,
-                                 GimpTextTool *text_tool)
+gimp_text_tool_text_notify (GimpText     *text,
+                            GParamSpec   *pspec,
+                            GimpTextTool *text_tool)
 {
-  GimpTextLayer *layer = text_tool->layer;
+  g_return_if_fail (text == text_tool->text);
 
-  gtk_widget_destroy (widget);
+  if ((pspec->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE)
+    {
+      GValue value = { 0, };
 
-  if (layer && layer->text)
+      g_value_init (&value, pspec->value_type);
+
+      g_object_get_property (G_OBJECT (text), pspec->name, &value);
+
+      g_signal_handlers_block_by_func (text_tool->proxy,
+                                       gimp_text_tool_proxy_notify,
+                                       text_tool);
+
+      g_object_set_property (G_OBJECT (text_tool->proxy), pspec->name, &value);
+
+      g_signal_handlers_unblock_by_func (text_tool->proxy,
+                                         gimp_text_tool_proxy_notify,
+                                         text_tool);
+
+      g_value_unset (&value);
+    }
+
+  /* we need to redraw the rectangle if it is visible and the shape of
+     the layer has changed, because of an undo for example. */
+  if (strcmp (pspec->name, "box-width") == 0  ||
+      strcmp (pspec->name, "box-height") == 0 ||
+      text->box_mode == GIMP_TEXT_BOX_DYNAMIC)
     {
-      switch (response_id)
-        {
-        case RESPONSE_NEW:
-          gimp_text_tool_create_layer (text_tool, layer->text);
-          break;
+      GimpRectangleTool *rect_tool = GIMP_RECTANGLE_TOOL (text_tool);
 
-        case GTK_RESPONSE_ACCEPT:
-          gimp_text_tool_connect (text_tool, layer, layer->text);
+      text_tool->handle_rectangle_change_complete = FALSE;
+      gimp_rectangle_tool_frame_item (rect_tool,
+                                      GIMP_ITEM (text_tool->layer));
+      text_tool->handle_rectangle_change_complete = TRUE;
+    }
 
-          /*  cause the text layer to be rerendered  */
-          if (text_tool->proxy)
-            g_object_notify (G_OBJECT (text_tool->proxy), "text");
+  /* if the text has changed, (probably because of an undo), we put
+   * the new text into the text buffer
+   */
+  if (strcmp (pspec->name, "text") == 0)
+    {
+      g_signal_handlers_block_by_func (text_tool->proxy,
+                                       gimp_text_tool_text_buffer_changed,
+                                       text_tool);
 
-          gimp_text_tool_canvas_editor (text_tool);
-          break;
+      gtk_text_buffer_set_text (text_tool->text_buffer, text->text, -1);
 
-        default:
-          break;
-        }
+      g_signal_handlers_unblock_by_func (text_tool->proxy,
+                                         gimp_text_tool_text_buffer_changed,
+                                         text_tool);
+
+      /* force change of cursor and selection display */
+      gimp_text_tool_update_layout (text_tool);
+/*       gimp_text_tool_update_proxy (text_tool); */
+      return;
     }
 }
 
+static gboolean
+gimp_text_tool_idle_apply (GimpTextTool *text_tool)
+{
+  text_tool->idle_id = 0;
+
+  gimp_text_tool_apply (text_tool);
+
+  return FALSE;
+}
+
 static void
-gimp_text_tool_confirm_dialog (GimpTextTool *text_tool)
+gimp_text_tool_apply (GimpTextTool *text_tool)
 {
-  GimpTool  *tool = GIMP_TOOL (text_tool);
-  GtkWidget *dialog;
-  GtkWidget *vbox;
-  GtkWidget *label;
-
-  g_return_if_fail (text_tool->layer != NULL);
+  const GParamSpec *pspec = NULL;
+  GimpImage        *image;
+  GimpTextLayer    *layer;
+  GObject          *src;
+  GObject          *dest;
+  GList            *list;
+  gboolean          push_undo  = TRUE;
+  gboolean          undo_group = FALSE;
 
-  if (text_tool->confirm_dialog)
+  if (text_tool->idle_id)
     {
-      gtk_window_present (GTK_WINDOW (text_tool->confirm_dialog));
-      return;
+      g_source_remove (text_tool->idle_id);
+      text_tool->idle_id = 0;
     }
 
-  dialog = gimp_viewable_dialog_new (GIMP_VIEWABLE (text_tool->layer),
-                                     GIMP_CONTEXT (gimp_tool_get_options (tool)),
-                                     _("Confirm Text Editing"),
-                                     "gimp-text-tool-confirm",
-                                     GIMP_STOCK_TEXT_LAYER,
-                                     _("Confirm Text Editing"),
-                                     tool->display->shell,
-                                     gimp_standard_help_func, NULL,
-
-                                     _("Create _New Layer"), RESPONSE_NEW,
-                                     GTK_STOCK_CANCEL,       GTK_RESPONSE_CANCEL,
-                                     GTK_STOCK_EDIT,         GTK_RESPONSE_ACCEPT,
-
-                                     NULL);
-
-  gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
-                                           RESPONSE_NEW,
-                                           GTK_RESPONSE_OK,
-                                           GTK_RESPONSE_CANCEL,
-                                           -1);
+  g_return_if_fail (text_tool->text != NULL);
+  g_return_if_fail (text_tool->layer != NULL);
 
-  gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+  layer = text_tool->layer;
+  image = gimp_item_get_image (GIMP_ITEM (layer));
 
-  g_signal_connect (dialog, "response",
-                    G_CALLBACK (gimp_text_tool_confirm_response),
-                    text_tool);
+  g_return_if_fail (layer->text == text_tool->text);
 
-  vbox = gtk_vbox_new (FALSE, 6);
-  gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
-  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
-                      vbox, FALSE, FALSE, 0);
-  gtk_widget_show (vbox);
+  /*  Walk over the list of changes and figure out if we are changing
+   *  a single property or need to push a full text undo.
+   */
+  for (list = text_tool->pending;
+       list && list->next && list->next->data == list->data;
+       list = list->next)
+    /* do nothing */;
 
-  label = gtk_label_new (_("The layer you selected is a text layer but "
-                           "it has been modified using other tools. "
-                           "Editing the layer with the text tool will "
-                           "discard these modifications."
-                           "\n\n"
-                           "You can edit the layer or create a new "
-                           "text layer from its text attributes."));
-  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
-  gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
-  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
-  gtk_widget_show (label);
+  if (g_list_length (list) == 1)
+    pspec = list->data;
 
-  gtk_widget_show (dialog);
+  /*  If we are changing a single property, we don't need to push
+   *  an undo if all of the following is true:
+   *   - the redo stack is empty
+   *   - the last item on the undo stack is a text undo
+   *   - the last undo changed the same text property on the same layer
+   *   - the last undo happened less than TEXT_UNDO_TIMEOUT seconds ago
+   */
+  if (pspec)
+    {
+      GimpUndo *undo = gimp_image_undo_can_compress (image, GIMP_TYPE_TEXT_UNDO,
+                                                     GIMP_UNDO_TEXT_LAYER);
 
-  text_tool->confirm_dialog = dialog;
-  g_signal_connect_swapped (dialog, "destroy",
-                            G_CALLBACK (g_nullify_pointer),
-                            &text_tool->confirm_dialog);
-}
+      if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (layer))
+        {
+          GimpTextUndo *text_undo = GIMP_TEXT_UNDO (undo);
 
-static void
-gimp_text_tool_layer_changed (GimpImage    *image,
-                              GimpTextTool *text_tool)
-{
-  GimpLayer         *layer     = gimp_image_get_active_layer (image);
-  GimpRectangleTool *rect_tool = GIMP_RECTANGLE_TOOL (text_tool);
+          if (text_undo->pspec == pspec)
+            {
+              if (gimp_undo_get_age (undo) < TEXT_UNDO_TIMEOUT)
+                {
+                  GimpTool *tool = GIMP_TOOL (text_tool);
 
-  gimp_text_tool_set_drawable (text_tool, GIMP_DRAWABLE (layer), FALSE);
+                  push_undo = FALSE;
+                  gimp_undo_reset_age (undo);
+                  gimp_undo_refresh_preview (undo,
+                                             GIMP_CONTEXT (gimp_tool_get_options (tool)));
+                }
+            }
+        }
+    }
 
-  if (text_tool->layer)
+  if (push_undo)
     {
-      if (! gimp_rectangle_tool_rectangle_is_new (rect_tool))
+      if (layer->modified)
         {
-          text_tool->handle_rectangle_change_complete = FALSE;
-          gimp_rectangle_tool_frame_item (rect_tool,
-                                          GIMP_ITEM (text_tool->layer));
-          text_tool->handle_rectangle_change_complete = TRUE;
+          undo_group = TRUE;
+          gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TEXT, NULL);
+
+          gimp_image_undo_push_text_layer_modified (image, NULL, layer);
+          gimp_image_undo_push_drawable_mod (image,
+                                             NULL, GIMP_DRAWABLE (layer));
         }
+
+      gimp_image_undo_push_text_layer (image, NULL, layer, pspec);
     }
-}
 
-static void
-gimp_text_tool_set_image (GimpTextTool *text_tool,
-                          GimpImage    *image)
-{
-  if (text_tool->image == image)
-    return;
+  src  = G_OBJECT (text_tool->proxy);
+  dest = G_OBJECT (text_tool->text);
 
-  if (text_tool->image)
-    {
-      g_signal_handlers_disconnect_by_func (text_tool->image,
-                                            gimp_text_tool_layer_changed,
-                                            text_tool);
+  g_signal_handlers_block_by_func (dest,
+                                   gimp_text_tool_text_notify, text_tool);
 
-      g_object_remove_weak_pointer (G_OBJECT (text_tool->image),
-                                    (gpointer) &text_tool->image);
-      text_tool->image = NULL;
-    }
+  g_object_freeze_notify (dest);
 
-  if (image)
+  for (; list; list = list->next)
     {
-      GimpTextOptions *options = GIMP_TEXT_TOOL_GET_OPTIONS (text_tool);
-      gdouble          xres;
-      gdouble          yres;
+      GValue  value = { 0, };
 
-      gimp_image_get_resolution (image, &xres, &yres);
+      /*  look ahead and compress changes  */
+      if (list->next && list->next->data == list->data)
+        continue;
 
-      text_tool->image = image;
-      g_object_add_weak_pointer (G_OBJECT (text_tool->image),
-                                 (gpointer) &text_tool->image);
+      pspec = list->data;
 
-      g_signal_connect_object (text_tool->image, "active-layer-changed",
-                               G_CALLBACK (gimp_text_tool_layer_changed),
-                               text_tool, 0);
+      g_value_init (&value, pspec->value_type);
 
-      gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (options->size_entry), 0,
-                                      yres, FALSE);
-    }
-}
+      g_object_get_property (src,  pspec->name, &value);
+      g_object_set_property (dest, pspec->name, &value);
 
-static gboolean
-gimp_text_tool_set_drawable (GimpTextTool *text_tool,
-                             GimpDrawable *drawable,
-                             gboolean      confirm)
-{
-  GimpImage *image = NULL;
+      g_value_unset (&value);
+    }
 
-  if (text_tool->confirm_dialog)
-    gtk_widget_destroy (text_tool->confirm_dialog);
+  g_list_free (text_tool->pending);
+  text_tool->pending = NULL;
 
-  if (drawable)
-    image = gimp_item_get_image (GIMP_ITEM (drawable));
+  g_object_thaw_notify (dest);
 
-  gimp_text_tool_set_image (text_tool, image);
+  g_signal_handlers_unblock_by_func (dest,
+                                     gimp_text_tool_text_notify, text_tool);
 
-  if (GIMP_IS_TEXT_LAYER (drawable) && GIMP_TEXT_LAYER (drawable)->text)
+  if (push_undo)
     {
-      GimpTextLayer *layer = GIMP_TEXT_LAYER (drawable);
-
-      if (layer == text_tool->layer && layer->text == text_tool->text)
-        return TRUE;
+      g_object_set (layer, "modified", FALSE, NULL);
 
-      if (layer->modified)
-        {
-          if (confirm)
-            {
-              gimp_text_tool_connect (text_tool, layer, NULL);
-              gimp_text_tool_confirm_dialog (text_tool);
-              return TRUE;
-            }
-        }
-      else
-        {
-          gimp_text_tool_connect (text_tool, layer, layer->text);
-          return TRUE;
-        }
+      if (undo_group)
+        gimp_image_undo_group_end (image);
     }
 
-  gimp_text_tool_connect (text_tool, NULL, NULL);
-  text_tool->layer = NULL;
+  /* if we're doing dynamic text, we want to update the
+   * shape of the rectangle */
+  if (layer->text->box_mode == GIMP_TEXT_BOX_DYNAMIC)
+    {
+      text_tool->handle_rectangle_change_complete = FALSE;
+      gimp_rectangle_tool_frame_item (GIMP_RECTANGLE_TOOL (text_tool),
+                                      GIMP_ITEM (layer));
+      text_tool->handle_rectangle_change_complete = TRUE;
+    }
 
-  return FALSE;
+  gimp_image_flush (image);
+  gimp_text_tool_update_layout (text_tool);
 }
 
-void
-gimp_text_tool_set_layer (GimpTextTool *text_tool,
-                          GimpLayer    *layer)
+static void
+gimp_text_tool_create_layer (GimpTextTool *text_tool,
+                             GimpText     *text)
 {
-  g_return_if_fail (GIMP_IS_TEXT_TOOL (text_tool));
-  g_return_if_fail (layer == NULL || GIMP_IS_LAYER (layer));
+  GimpTool  *tool = GIMP_TOOL (text_tool);
+  GimpImage *image;
+  GimpLayer *layer;
+
+  if (text)
+    {
+      text = gimp_config_duplicate (GIMP_CONFIG (text));
+    }
+  else
+    {
+      gchar *str = gimp_text_tool_canvas_editor_get_text (text_tool);
+
+      g_object_set (text_tool->proxy,
+                    "text",     str,
+                    "box-mode", GIMP_TEXT_BOX_DYNAMIC,
+                    NULL);
 
-  if (gimp_text_tool_set_drawable (text_tool, GIMP_DRAWABLE (layer), TRUE))
-    {
-      GimpTool    *tool = GIMP_TOOL (text_tool);
-      GimpItem    *item = GIMP_ITEM (layer);
-      GimpContext *context;
-      GimpDisplay *display;
+      g_free (str);
 
-      context = gimp_get_user_context (tool->tool_info->gimp);
-      display = gimp_context_get_display (context);
+      text = gimp_config_duplicate (GIMP_CONFIG (text_tool->proxy));
+    }
 
-      if (! display || display->image != item->image)
-        {
-          GList *list;
+  image = tool->display->image;
+  layer = gimp_text_layer_new (image, text);
 
-          display = NULL;
+  g_object_unref (text);
 
-          for (list = GIMP_LIST (tool->tool_info->gimp->displays)->list;
-               list;
-               list = g_list_next (list))
-            {
-              display = list->data;
+  if (! layer)
+    return;
 
-              if (display->image == item->image)
-                {
-                  gimp_context_set_display (context, display);
-                  break;
-                }
+  gimp_text_tool_connect (text_tool, GIMP_TEXT_LAYER (layer), text);
 
-              display = NULL;
-            }
-        }
+  gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TEXT,
+                               _("Add Text Layer"));
 
-      tool->display = display;
+  if (gimp_image_floating_sel (image))
+    {
+      g_signal_handlers_block_by_func (image,
+                                       gimp_text_tool_layer_changed,
+                                       text_tool);
 
-      if (tool->display)
-        {
-          tool->drawable = GIMP_DRAWABLE (layer);
+      floating_sel_anchor (gimp_image_floating_sel (image));
 
-          gimp_text_tool_canvas_editor (text_tool);
-        }
+      g_signal_handlers_unblock_by_func (image,
+                                         gimp_text_tool_layer_changed,
+                                         text_tool);
     }
-}
 
-static gboolean
-gimp_text_tool_rectangle_change_complete (GimpRectangleTool *rect_tool)
-{
-  GimpTextTool *text_tool = GIMP_TEXT_TOOL (rect_tool);
+  GIMP_ITEM (layer)->offset_x = text_tool->x1;
+  GIMP_ITEM (layer)->offset_y = text_tool->y1;
 
-  if (text_tool->handle_rectangle_change_complete)
+  gimp_image_add_layer (image, layer, -1, TRUE);
+
+  if (text_tool->text_box_fixed)
     {
-      GimpText *text = text_tool->text;
-      GimpItem *item;
-      gint      x1, y1, x2, y2;
+      GimpRectangleTool *rect_tool = GIMP_RECTANGLE_TOOL (text_tool);
+      GimpItem          *item      = GIMP_ITEM (layer);
+      gint               x1, y1, x2, y2;
 
       g_object_get (rect_tool,
                     "x1", &x1,
@@ -1904,392 +1717,383 @@
                     "x2", &x2,
                     "y2", &y2,
                     NULL);
-
-      text_tool->text_box_fixed = TRUE;
-
-      if (! text || ! text->text || (text->text[0] == 0))
-        {
-          /*
-           * we can't set properties for the text layer, because
-           * it isn't created until some text has been inserted,
-           * so we need to make a special note that will remind
-           * us what to do when we actually create the layer
-           */
-          return TRUE;
-        }
-
       g_object_set (text_tool->proxy,
                     "box-mode",   GIMP_TEXT_BOX_FIXED,
                     "box-width",  (gdouble) (x2 - x1),
                     "box-height", (gdouble) (y2 - y1),
                     NULL);
-
-      gimp_image_undo_group_start (text_tool->image, GIMP_UNDO_GROUP_TEXT,
-                                   _("Reshape Text Layer"));
-
-      item = GIMP_ITEM (text_tool->layer);
       gimp_item_translate (item,
                            x1 - item->offset_x,
                            y1 - item->offset_y,
                            TRUE);
-      gimp_text_tool_apply (text_tool);
-
-      gimp_image_undo_group_end (text_tool->image);
+    }
+  else
+    {
+      text_tool->handle_rectangle_change_complete = FALSE;
+      gimp_rectangle_tool_frame_item (GIMP_RECTANGLE_TOOL (text_tool),
+                                      GIMP_ITEM (layer));
+      text_tool->handle_rectangle_change_complete = TRUE;
     }
 
-  return TRUE;
+  gimp_image_undo_group_end (image);
+
+  gimp_image_flush (image);
+
+  gimp_text_tool_set_drawable (text_tool, GIMP_DRAWABLE (layer), FALSE);
 }
 
-/* this doesn't belong here but this is the only place
-   it is used at the moment -- Bill
-*/
-
-/**
- * gimp_rectangle_tool_frame_item:
- * @rect_tool: a #GimpRectangleTool interface
- * @item:      a #GimpItem attached to the image on which a
- *             rectangle is being shown.
- *
- * Convenience function to set the corners of the rectangle to
- * match the bounds of the specified item.  The rectangle interface
- * must be active (i.e., showing a rectangle), and the item must be
- * attached to the image on which the rectangle is active.
- */
-void
-gimp_rectangle_tool_frame_item (GimpRectangleTool *rect_tool,
-                                GimpItem          *item)
+static void
+gimp_text_tool_canvas_editor (GimpTextTool *text_tool)
 {
-  GimpDisplay *display = GIMP_TOOL (rect_tool)->display;
-  gint         offset_x;
-  gint         offset_y;
-  gint         width;
-  gint         height;
-
-  g_return_if_fail (GIMP_IS_ITEM (item));
-  g_return_if_fail (gimp_item_is_attached (item));
-  g_return_if_fail (display != NULL);
-  g_return_if_fail (display->image == item->image);
-
-  width  = gimp_item_width (item);
-  height = gimp_item_height (item);
-
-  gimp_item_offsets (item, &offset_x, &offset_y);
-
-  gimp_draw_tool_pause (GIMP_DRAW_TOOL (rect_tool));
-
-  gimp_rectangle_tool_set_function (rect_tool,
-                                    GIMP_RECTANGLE_TOOL_CREATING);
-
-  g_object_set (rect_tool,
-                "x1", offset_x,
-                "y1", offset_y,
-                "x2", offset_x + width,
-                "y2", offset_y + height,
-                NULL);
+  GimpTool        *tool    = GIMP_TOOL (text_tool);
+  GimpTextOptions *options = GIMP_TEXT_TOOL_GET_OPTIONS (text_tool);
 
-  /* kludge to force handle sizes to update.  This call may be
-   * harmful if this function is ever moved out of the text tool code.
-   */
-  gimp_rectangle_tool_set_constraint (rect_tool,
-                                      GIMP_RECTANGLE_CONSTRAIN_NONE);
+  gtk_im_context_set_client_window (text_tool->im_context,
+                                    GIMP_DISPLAY_SHELL (tool->display->shell)->canvas->window);
+
+  gtk_im_context_focus_in (text_tool->im_context);
+
+  gimp_text_tool_update_layout (text_tool);
 
-  gimp_draw_tool_resume (GIMP_DRAW_TOOL (rect_tool));
+  if (options->use_editor)
+    gimp_text_tool_editor (text_tool);
 }
 
 static void
-gimp_text_tool_draw_preedit_lines (GimpDrawTool *draw_tool)
+gimp_text_tool_editor (GimpTextTool *text_tool)
 {
-  GimpTextTool    *text_tool = GIMP_TEXT_TOOL (draw_tool);
-  PangoLayout     *layout;
-  PangoLayoutIter *line_iter;
-  GtkTextIter      cursor, start;
-  gint             i;
-  gint             min, max;
-  gchar           *string;
+  GimpTextOptions   *options = GIMP_TEXT_TOOL_GET_OPTIONS (text_tool);
+  GimpDialogFactory *dialog_factory;
+  GtkWindow         *parent  = NULL;
 
-  gtk_text_buffer_get_selection_bounds (text_tool->text_buffer, &cursor, NULL);
-  gtk_text_buffer_get_start_iter (text_tool->text_buffer, &start);
+  if (text_tool->editor)
+    {
+      gtk_window_present (GTK_WINDOW (text_tool->editor));
+      return;
+    }
 
-  string = gtk_text_buffer_get_text (text_tool->text_buffer,
-                                     &start, &cursor, FALSE);
-  min = strlen (string);
-  g_free (string);
+  dialog_factory = gimp_dialog_factory_from_name ("toplevel");
 
-  max = min + text_tool->preedit_len;
+  if (GIMP_TOOL (text_tool)->display)
+    parent = GTK_WINDOW (GIMP_TOOL (text_tool)->display->shell);
 
-  layout = text_tool->layout->layout;
-  line_iter = pango_layout_get_iter (layout);
-  i = 0;
+  text_tool->editor = gimp_text_options_editor_new (parent, options,
+                                                    dialog_factory->menu_factory,
+                                                    _("GIMP Text Editor"),
+                                                    text_tool->text_buffer);
 
-  do
-    {
-      gint firstline, lastline;
-      gint first_x,   last_x;
+  g_object_add_weak_pointer (G_OBJECT (text_tool->editor),
+                             (gpointer) &text_tool->editor);
 
-      pango_layout_index_to_line_x (layout, min, 0, &firstline, &first_x);
-      pango_layout_index_to_line_x (layout, max, 0, &lastline, &last_x);
+  gimp_dialog_factory_add_foreign (dialog_factory,
+                                   "gimp-text-tool-dialog",
+                                   text_tool->editor);
 
-      if (i >= firstline && i <= lastline)
-        {
-          PangoRectangle crect;
+  gtk_widget_show (text_tool->editor);
+}
 
-          pango_layout_iter_get_line_extents (line_iter, NULL, &crect);
-          pango_extents_to_pixels (&crect, NULL);
+static gchar *
+gimp_text_tool_canvas_editor_get_text (GimpTextTool *text_tool)
+{
+  if (text_tool->text_buffer)
+    {
+      GtkTextIter  start, end;
+      GtkTextIter  selstart, selend;
+      gchar       *string;
+      gchar       *fb;
+      gchar       *lb;
 
-          gimp_draw_tool_draw_line (draw_tool,
-                                    crect.x, crect.y + crect.height,
-                                    crect.x + crect.width,
-                                    crect.y + crect.height,
-                                    TRUE);
-          if (i == firstline)
-            {
-              PangoRectangle crect2 = crect;
+      gtk_text_buffer_get_bounds (text_tool->text_buffer, &start, &end);
+      gtk_text_buffer_get_selection_bounds (text_tool->text_buffer,
+                                            &selstart, &selend);
 
-              crect2.width = PANGO_PIXELS (first_x) - crect.x;
-              crect2.x     = crect.x;
+      fb = gtk_text_buffer_get_text (text_tool->text_buffer,
+                                     &start, &selstart, TRUE);
+      lb = gtk_text_buffer_get_text (text_tool->text_buffer,
+                                     &selstart, &end, TRUE);
 
-              gimp_draw_tool_draw_line (draw_tool,
-                                        crect2.x, crect2.y + crect2.height,
-                                        crect2.width,
-                                        crect2.y + crect2.height,
-                                        TRUE);
+      if (text_tool->preedit_string)
+        {
+          if (fb == NULL)
+            {
+              string = g_strconcat (text_tool->preedit_string, lb, NULL);
             }
-          if (i == lastline)
+          else
             {
-              PangoRectangle crect2 = crect;
+              string = g_strconcat (fb, text_tool->preedit_string, lb, NULL);
+            }
+        }
+      else
+        {
+          string = g_strconcat (fb, lb, NULL);
+        }
+
+      g_free (fb);
+      g_free (lb);
+
+      return string;
+    }
+
+  return NULL;
+}
+
+
+#define  RESPONSE_NEW 1
+
+static void
+gimp_text_tool_confirm_response (GtkWidget    *widget,
+                                 gint          response_id,
+                                 GimpTextTool *text_tool)
+{
+  GimpTextLayer *layer = text_tool->layer;
+
+  gtk_widget_destroy (widget);
+
+  if (layer && layer->text)
+    {
+      switch (response_id)
+        {
+        case RESPONSE_NEW:
+          gimp_text_tool_create_layer (text_tool, layer->text);
+          break;
+
+        case GTK_RESPONSE_ACCEPT:
+          gimp_text_tool_connect (text_tool, layer, layer->text);
+
+          /*  cause the text layer to be rerendered  */
+          if (text_tool->proxy)
+            g_object_notify (G_OBJECT (text_tool->proxy), "text");
 
-              crect2.width = crect.x + crect.width - PANGO_PIXELS (last_x);
-              crect2.x     = PANGO_PIXELS (last_x);
+          gimp_text_tool_canvas_editor (text_tool);
+          break;
 
-              gimp_draw_tool_draw_line (draw_tool,
-                                        crect2.x, crect2.y + crect2.height,
-                                        crect2.x + crect2.width,
-                                        crect2.y + crect2.height,
-                                        TRUE);
-            }
+        default:
+          break;
         }
-
-      i++;
     }
-  while (pango_layout_iter_next_line (line_iter));
-
-  pango_layout_iter_free (line_iter);
 }
 
 static void
-gimp_text_tool_draw_text_selection (GimpDrawTool *draw_tool)
+gimp_text_tool_confirm_dialog (GimpTextTool *text_tool)
 {
-  GimpTextTool    *text_tool = GIMP_TEXT_TOOL (draw_tool);
-  PangoLayout     *layout;
-  PangoLayoutIter *line_iter;
-  GtkTextIter      start;
-  GtkTextIter      sel_start, sel_end;
-  gint             i;
-  gint             min, max;
-  gchar           *string;
-
-  gtk_text_buffer_get_selection_bounds (text_tool->text_buffer,
-                                        &sel_start, &sel_end);
-  gtk_text_buffer_get_start_iter (text_tool->text_buffer, &start);
+  GimpTool  *tool = GIMP_TOOL (text_tool);
+  GtkWidget *dialog;
+  GtkWidget *vbox;
+  GtkWidget *label;
 
-  string = gtk_text_buffer_get_text (text_tool->text_buffer,
-                                     &start, &sel_start, FALSE);
-  min = strlen (string);
-  g_free (string);
+  g_return_if_fail (text_tool->layer != NULL);
 
-  string = gtk_text_buffer_get_text (text_tool->text_buffer,
-                                     &start, &sel_end, FALSE);
-  max = strlen (string);
-  g_free (string);
+  if (text_tool->confirm_dialog)
+    {
+      gtk_window_present (GTK_WINDOW (text_tool->confirm_dialog));
+      return;
+    }
 
-  layout = text_tool->layout->layout;
-  line_iter = pango_layout_get_iter (layout);
-  i = 0;
+  dialog = gimp_viewable_dialog_new (GIMP_VIEWABLE (text_tool->layer),
+                                     GIMP_CONTEXT (gimp_tool_get_options (tool)),
+                                     _("Confirm Text Editing"),
+                                     "gimp-text-tool-confirm",
+                                     GIMP_STOCK_TEXT_LAYER,
+                                     _("Confirm Text Editing"),
+                                     tool->display->shell,
+                                     gimp_standard_help_func, NULL,
 
-  /* Invert the selected letters by inverting all
-   * lines containing selected letters, then
-   * invert the unselected letters on these lines
-   * a second time to make them look normal
-   */
-  do
-    {
-      gint firstline, lastline;
-      gint first_x,   last_x;
+                                     _("Create _New Layer"), RESPONSE_NEW,
+                                     GTK_STOCK_CANCEL,       GTK_RESPONSE_CANCEL,
+                                     GTK_STOCK_EDIT,         GTK_RESPONSE_ACCEPT,
 
-      pango_layout_index_to_line_x (layout, min, 0, &firstline, &first_x);
-      pango_layout_index_to_line_x (layout, max, 0, &lastline, &last_x);
+                                     NULL);
 
-      if (i >= firstline && i <= lastline)
-        {
-          PangoRectangle crect;
+  gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+                                           RESPONSE_NEW,
+                                           GTK_RESPONSE_OK,
+                                           GTK_RESPONSE_CANCEL,
+                                           -1);
 
-          pango_layout_iter_get_line_extents (line_iter, NULL, &crect);
-          pango_extents_to_pixels (&crect, NULL);
+  gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
 
-          gimp_draw_tool_draw_rectangle (draw_tool, TRUE,
-                                         crect.x, crect.y,
-                                         crect.width, crect.height,
-                                         TRUE);
-          if (i == firstline)
-            {
-              /* Twice invert all letters before the selection,
-               * making them look normal
-               */
-              PangoRectangle crect2 = crect;
+  g_signal_connect (dialog, "response",
+                    G_CALLBACK (gimp_text_tool_confirm_response),
+                    text_tool);
 
-              crect2.width = PANGO_PIXELS (first_x) - crect.x;
-              crect2.x     = crect.x;
+  vbox = gtk_vbox_new (FALSE, 6);
+  gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
+                      vbox, FALSE, FALSE, 0);
+  gtk_widget_show (vbox);
 
-              gimp_draw_tool_draw_rectangle (draw_tool, TRUE,
-                                             crect2.x, crect2.y,
-                                             crect2.width, crect2.height,
-                                             TRUE);
-            }
-          if (i == lastline)
-            {
-              /* Twice invert all letters after the selection,
-               * making them look normal
-               */
-              PangoRectangle crect2 = crect;
+  label = gtk_label_new (_("The layer you selected is a text layer but "
+                           "it has been modified using other tools. "
+                           "Editing the layer with the text tool will "
+                           "discard these modifications."
+                           "\n\n"
+                           "You can edit the layer or create a new "
+                           "text layer from its text attributes."));
+  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+  gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+  gtk_widget_show (label);
 
-              crect2.width = crect.x + crect.width - PANGO_PIXELS (last_x);
-              crect2.x     = PANGO_PIXELS (last_x);
+  gtk_widget_show (dialog);
 
-              gimp_draw_tool_draw_rectangle (draw_tool, TRUE,
-                                             crect2.x, crect2.y,
-                                             crect2.width, crect2.height,
-                                             TRUE);
-            }
-        }
-      i++;
-    }
-  while (pango_layout_iter_next_line (line_iter));
-  pango_layout_iter_free (line_iter);
+  text_tool->confirm_dialog = dialog;
+  g_signal_connect_swapped (dialog, "destroy",
+                            G_CALLBACK (g_nullify_pointer),
+                            &text_tool->confirm_dialog);
 }
 
 static void
-gimp_text_tool_draw (GimpDrawTool *draw_tool)
+gimp_text_tool_layer_changed (GimpImage    *image,
+                              GimpTextTool *text_tool)
 {
-  GimpTextTool     *text_tool = GIMP_TEXT_TOOL (draw_tool);
-  GimpTool         *tool      = GIMP_TOOL (draw_tool);
-  GdkRectangle      cliprect;
-  gint              width, height;
-  gint              x1, x2;
-  gint              y1, y2;
-  GtkTextIter       start;
+  GimpLayer         *layer     = gimp_image_get_active_layer (image);
+  GimpRectangleTool *rect_tool = GIMP_RECTANGLE_TOOL (text_tool);
 
-  g_object_set (text_tool,
-                "narrow-mode", TRUE,
-                NULL);
+  gimp_text_tool_set_drawable (text_tool, GIMP_DRAWABLE (layer), FALSE);
 
-  gimp_rectangle_tool_draw (draw_tool);
+  if (text_tool->layer)
+    {
+      if (! gimp_rectangle_tool_rectangle_is_new (rect_tool))
+        {
+          text_tool->handle_rectangle_change_complete = FALSE;
+          gimp_rectangle_tool_frame_item (rect_tool,
+                                          GIMP_ITEM (text_tool->layer));
+          text_tool->handle_rectangle_change_complete = TRUE;
+        }
+    }
+}
 
-  if (! text_tool->layer ||
-      ! text_tool->layer->text)
+static void
+gimp_text_tool_set_image (GimpTextTool *text_tool,
+                          GimpImage    *image)
+{
+  if (text_tool->image == image)
     return;
 
-  /* There will be no layout if the function is called from the wrong place */
-  if (! text_tool->layout)
-    gimp_text_tool_update_layout (text_tool);
-
-  g_object_get (text_tool,
-                "x1", &x1,
-                "y1", &y1,
-                "x2", &x2,
-                "y2", &y2,
-                NULL);
-
-  /* Turn on clipping for text-cursor and selections */
-  cliprect.x      = x1;
-  cliprect.width  = x2 - x1;
-  cliprect.y      = y1;
-  cliprect.height = y2 - y1;
-  gimp_draw_tool_set_clip_rect (draw_tool, &cliprect, FALSE);
-
-  gtk_text_buffer_get_start_iter (text_tool->text_buffer, &start);
-
-  if (! gtk_text_buffer_get_has_selection (text_tool->text_buffer))
+  if (text_tool->image)
     {
-      /* If the text buffer has no selection, draw the text cursor */
+      g_signal_handlers_disconnect_by_func (text_tool->image,
+                                            gimp_text_tool_layer_changed,
+                                            text_tool);
 
-      gint            cursorx;
-      GtkTextIter     cursor;
-      PangoRectangle  crect;
-      gchar          *string;
+      g_object_remove_weak_pointer (G_OBJECT (text_tool->image),
+                                    (gpointer) &text_tool->image);
+      text_tool->image = NULL;
+    }
 
-      gtk_text_buffer_get_iter_at_mark (text_tool->text_buffer, &cursor,
-                                        gtk_text_buffer_get_insert (text_tool->text_buffer));
+  if (image)
+    {
+      GimpTextOptions *options = GIMP_TEXT_TOOL_GET_OPTIONS (text_tool);
+      gdouble          xres;
+      gdouble          yres;
 
-      string = gtk_text_buffer_get_text (text_tool->text_buffer,
-                                         &start, &cursor, FALSE);
+      gimp_image_get_resolution (image, &xres, &yres);
 
-      /* Using strlen to get the byte index, not the character offset */
-      cursorx = strlen (string);
+      text_tool->image = image;
+      g_object_add_weak_pointer (G_OBJECT (text_tool->image),
+                                 (gpointer) &text_tool->image);
 
-      /* TODO: make cursor position itself even inside preedits! */
-      if (text_tool->preedit_len > 0)
-        cursorx += text_tool->preedit_len;
+      g_signal_connect_object (text_tool->image, "active-layer-changed",
+                               G_CALLBACK (gimp_text_tool_layer_changed),
+                               text_tool, 0);
 
-      g_free (string);
+      gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (options->size_entry), 0,
+                                      yres, FALSE);
+    }
+}
 
-      pango_layout_index_to_pos (text_tool->layout->layout, cursorx, &crect);
+static gboolean
+gimp_text_tool_set_drawable (GimpTextTool *text_tool,
+                             GimpDrawable *drawable,
+                             gboolean      confirm)
+{
+  GimpImage *image = NULL;
 
-      crect.x      = PANGO_PIXELS (crect.x);
-      crect.y      = PANGO_PIXELS (crect.y);
-      crect.height = PANGO_PIXELS (crect.height);
+  if (text_tool->confirm_dialog)
+    gtk_widget_destroy (text_tool->confirm_dialog);
 
-      gimp_draw_tool_draw_rectangle (draw_tool, TRUE,
-                                     crect.x, crect.y,
-                                     4, crect.height,
-                                     TRUE);
+  if (drawable)
+    image = gimp_item_get_image (GIMP_ITEM (drawable));
 
-      if (text_tool->preedit_string && text_tool->preedit_len > 0)
-        gimp_text_tool_draw_preedit_lines (draw_tool);
-    }
-  else
+  gimp_text_tool_set_image (text_tool, image);
+
+  if (GIMP_IS_TEXT_LAYER (drawable) && GIMP_TEXT_LAYER (drawable)->text)
     {
-      /* If the text buffer has a selection, highlight the
-       * selected letters*/
+      GimpTextLayer *layer = GIMP_TEXT_LAYER (drawable);
+
+      if (layer == text_tool->layer && layer->text == text_tool->text)
+        return TRUE;
 
-      gimp_text_tool_draw_text_selection (draw_tool);
+      if (layer->modified)
+        {
+          if (confirm)
+            {
+              gimp_text_tool_connect (text_tool, layer, NULL);
+              gimp_text_tool_confirm_dialog (text_tool);
+              return TRUE;
+            }
+        }
+      else
+        {
+          gimp_text_tool_connect (text_tool, layer, layer->text);
+          return TRUE;
+        }
     }
 
-  /* Turn off clipping when done */
-  gimp_draw_tool_set_clip_rect (draw_tool, NULL, FALSE);
-}
+  gimp_text_tool_connect (text_tool, NULL, NULL);
+  text_tool->layer = NULL;
 
-static void
-gimp_text_tool_commit_cb (GtkIMContext *context,
-                          const gchar  *str,
-                          GimpTextTool *text_tool)
-{
-  gimp_text_tool_enter_text (text_tool, str);
+  return FALSE;
 }
 
-static void
-gimp_text_tool_reset_im_context (GimpTextTool *text_tool)
+void
+gimp_text_tool_set_layer (GimpTextTool *text_tool,
+                          GimpLayer    *layer)
 {
-  if (text_tool->needs_im_reset)
+  g_return_if_fail (GIMP_IS_TEXT_TOOL (text_tool));
+  g_return_if_fail (layer == NULL || GIMP_IS_LAYER (layer));
+
+  if (gimp_text_tool_set_drawable (text_tool, GIMP_DRAWABLE (layer), TRUE))
     {
-      text_tool->needs_im_reset = FALSE;
-      gtk_im_context_reset (text_tool->im_context);
-    }
-}
+      GimpTool    *tool = GIMP_TOOL (text_tool);
+      GimpItem    *item = GIMP_ITEM (layer);
+      GimpContext *context;
+      GimpDisplay *display;
 
-static void
-gimp_text_tool_preedit_changed_cb (GtkIMContext *context,
-                                   GimpTextTool *text_tool)
-{
-  if (text_tool->preedit_string)
-    g_free (text_tool->preedit_string);
+      context = gimp_get_user_context (tool->tool_info->gimp);
+      display = gimp_context_get_display (context);
 
-  gtk_im_context_get_preedit_string (context,
-                                     &text_tool->preedit_string, NULL,
-                                     &text_tool->preedit_cursor);
+      if (! display || display->image != item->image)
+        {
+          GList *list;
 
-  text_tool->preedit_len = strlen (text_tool->preedit_string);
+          display = NULL;
 
-  gimp_text_tool_update_proxy (text_tool);
+          for (list = GIMP_LIST (tool->tool_info->gimp->displays)->list;
+               list;
+               list = g_list_next (list))
+            {
+              display = list->data;
+
+              if (display->image == item->image)
+                {
+                  gimp_context_set_display (context, display);
+                  break;
+                }
+
+              display = NULL;
+            }
+        }
+
+      tool->display = display;
+
+      if (tool->display)
+        {
+          tool->drawable = GIMP_DRAWABLE (layer);
+
+          gimp_text_tool_canvas_editor (text_tool);
+        }
+    }
 }
 
 static void
@@ -2311,34 +2115,6 @@
     }
 }
 
-void
-gimp_text_tool_delete_text (GimpTextTool *text_tool,
-                            gboolean      backspace)
-{
-  GtkTextIter cursor;
-
-  gtk_text_buffer_get_iter_at_mark (text_tool->text_buffer,
-                                    &cursor,
-                                    gtk_text_buffer_get_insert (text_tool->text_buffer));
-
-  if (gtk_text_buffer_get_has_selection (text_tool->text_buffer))
-    {
-      gtk_text_buffer_delete_selection (text_tool->text_buffer, TRUE, TRUE);
-    }
-  else if (backspace)
-    {
-      gtk_text_buffer_backspace (text_tool->text_buffer, &cursor, TRUE, TRUE);
-    }
-  else
-    {
-      GtkTextIter end = cursor;
-
-      gtk_text_iter_forward_cursor_positions (&end, 1);
-      gtk_text_buffer_delete_interactive (text_tool->text_buffer,
-                                          &cursor, &end, TRUE);
-    }
-}
-
 static void
 gimp_text_tool_enter_text (GimpTextTool *text_tool,
                            const gchar  *str)
@@ -2387,6 +2163,73 @@
   text_tool->layout = gimp_text_layout_new (text_tool->layer->text, image);
 }
 
+static gint
+gimp_text_tool_xy_to_offset (GimpTextTool *text_tool,
+                             gdouble       x,
+                             gdouble       y)
+{
+  GtkTextIter start, end;
+  gchar      *string;
+  gint        offset;
+  gint        trailing;
+
+  gtk_text_buffer_get_bounds (text_tool->text_buffer, &start, &end);
+  string = gtk_text_buffer_get_text (text_tool->text_buffer,
+                                     &start, &end, TRUE);
+
+  pango_layout_xy_to_index (text_tool->layout->layout,
+                            x * PANGO_SCALE,
+                            y * PANGO_SCALE,
+                            &offset, &trailing);
+
+  offset = g_utf8_pointer_to_offset (string, string + offset);
+  offset += trailing;
+
+  g_free (string);
+
+  return offset;
+}
+
+
+/*  IM context utilities and callbacks  */
+
+static void
+gimp_text_tool_reset_im_context (GimpTextTool *text_tool)
+{
+  if (text_tool->needs_im_reset)
+    {
+      text_tool->needs_im_reset = FALSE;
+      gtk_im_context_reset (text_tool->im_context);
+    }
+}
+
+static void
+gimp_text_tool_commit_cb (GtkIMContext *context,
+                          const gchar  *str,
+                          GimpTextTool *text_tool)
+{
+  gimp_text_tool_enter_text (text_tool, str);
+}
+
+static void
+gimp_text_tool_preedit_changed_cb (GtkIMContext *context,
+                                   GimpTextTool *text_tool)
+{
+  if (text_tool->preedit_string)
+    g_free (text_tool->preedit_string);
+
+  gtk_im_context_get_preedit_string (context,
+                                     &text_tool->preedit_string, NULL,
+                                     &text_tool->preedit_cursor);
+
+  text_tool->preedit_len = strlen (text_tool->preedit_string);
+
+  gimp_text_tool_update_proxy (text_tool);
+}
+
+
+/*  public functions  */
+
 gboolean
 gimp_text_tool_get_has_text_selection (GimpTextTool *text_tool)
 {
@@ -2397,6 +2240,34 @@
 }
 
 void
+gimp_text_tool_delete_text (GimpTextTool *text_tool,
+                            gboolean      backspace)
+{
+  GtkTextIter cursor;
+
+  gtk_text_buffer_get_iter_at_mark (text_tool->text_buffer,
+                                    &cursor,
+                                    gtk_text_buffer_get_insert (text_tool->text_buffer));
+
+  if (gtk_text_buffer_get_has_selection (text_tool->text_buffer))
+    {
+      gtk_text_buffer_delete_selection (text_tool->text_buffer, TRUE, TRUE);
+    }
+  else if (backspace)
+    {
+      gtk_text_buffer_backspace (text_tool->text_buffer, &cursor, TRUE, TRUE);
+    }
+  else
+    {
+      GtkTextIter end = cursor;
+
+      gtk_text_iter_forward_cursor_positions (&end, 1);
+      gtk_text_buffer_delete_interactive (text_tool->text_buffer,
+                                          &cursor, &end, TRUE);
+    }
+}
+
+void
 gimp_text_tool_clipboard_cut (GimpTextTool *text_tool)
 {
   GimpTool     *tool = GIMP_TOOL (text_tool);
@@ -2436,3 +2307,57 @@
 
   gtk_text_buffer_paste_clipboard (text_tool->text_buffer, clipboard, NULL, TRUE);
 }
+
+void
+gimp_text_tool_create_vectors (GimpTextTool *text_tool)
+{
+  GimpVectors *vectors;
+
+  g_return_if_fail (GIMP_IS_TEXT_TOOL (text_tool));
+
+  if (! text_tool->text || ! text_tool->image)
+    return;
+
+  vectors = gimp_text_vectors_new (text_tool->image, text_tool->text);
+
+  if (text_tool->layer)
+    {
+      gint x, y;
+
+      gimp_item_offsets (GIMP_ITEM (text_tool->layer), &x, &y);
+      gimp_item_translate (GIMP_ITEM (vectors), x, y, FALSE);
+    }
+
+  gimp_image_add_vectors (text_tool->image, vectors, -1, TRUE);
+
+  gimp_image_flush (text_tool->image);
+}
+
+void
+gimp_text_tool_create_vectors_warped (GimpTextTool *text_tool)
+{
+  GimpVectors   *vectors0;
+  GimpVectors   *vectors;
+  gdouble        box_height;
+
+  g_return_if_fail (GIMP_IS_TEXT_TOOL (text_tool));
+
+  if (! text_tool->text || ! text_tool->image || ! text_tool->layer)
+    return;
+
+  box_height = gimp_item_height (GIMP_ITEM (text_tool->layer));
+
+  vectors0 = gimp_image_get_active_vectors (text_tool->image);
+  if (! vectors0)
+    return;
+
+  vectors = gimp_text_vectors_new (text_tool->image, text_tool->text);
+
+  gimp_vectors_warp_vectors (vectors0, vectors, 0.5 * box_height);
+
+  gimp_image_add_vectors (text_tool->image, vectors, -1, TRUE);
+  gimp_image_set_active_vectors (text_tool->image, vectors);
+  gimp_item_set_visible (GIMP_ITEM (vectors), TRUE, FALSE);
+
+  gimp_image_flush (text_tool->image);
+}

Modified: trunk/app/tools/gimptexttool.h
==============================================================================
--- trunk/app/tools/gimptexttool.h	(original)
+++ trunk/app/tools/gimptexttool.h	Mon Oct 27 16:21:03 2008
@@ -86,6 +86,8 @@
 void       gimp_text_tool_set_layer              (GimpTextTool *text_tool,
                                                   GimpLayer    *layer);
 
+gboolean   gimp_text_tool_get_has_text_selection (GimpTextTool *text_tool);
+
 void       gimp_text_tool_delete_text            (GimpTextTool *text_tool,
                                                   gboolean      backspace);
 void       gimp_text_tool_clipboard_cut          (GimpTextTool *text_tool);
@@ -94,8 +96,7 @@
 void       gimp_text_tool_clipboard_paste        (GimpTextTool *text_tool,
                                                   gboolean      use_clipboard);
 
-gboolean   gimp_text_tool_get_has_text_selection (GimpTextTool *text_tool);
 void       gimp_text_tool_create_vectors         (GimpTextTool *text_tool);
-
+void       gimp_text_tool_create_vectors_warped  (GimpTextTool *text_tool);
 
 #endif /* __GIMP_TEXT_TOOL_H__ */

Modified: trunk/menus/text-tool-menu.xml
==============================================================================
--- trunk/menus/text-tool-menu.xml	(original)
+++ trunk/menus/text-tool-menu.xml	Mon Oct 27 16:21:03 2008
@@ -12,6 +12,7 @@
     <menuitem action="text-tool-clear" />
     <separator />
     <menuitem action="text-tool-path-from-text" />
+    <menuitem action="text-tool-text-along-path" />
     <separator />
     <menuitem action="text-tool-direction-ltr" />
     <menuitem action="text-tool-direction-rtl" />



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]