[gimp] app: split the text editor code out to a separate file



commit 8f27ec48c6a0e1e7f6a25b1893561a6b64776348
Author: Michael Natterer <mitch gimp org>
Date:   Wed Feb 17 21:23:15 2010 +0100

    app: split the text editor code out to a separate file

 app/tools/Makefile.am           |    2 +
 app/tools/gimptexttool-editor.c |  833 +++++++++++++++++++++++++++++++++++++++
 app/tools/gimptexttool-editor.h |   38 ++
 app/tools/gimptexttool.c        |  781 +------------------------------------
 app/tools/gimptexttool.h        |    5 +
 5 files changed, 889 insertions(+), 770 deletions(-)
---
diff --git a/app/tools/Makefile.am b/app/tools/Makefile.am
index b35855d..57a1f55 100644
--- a/app/tools/Makefile.am
+++ b/app/tools/Makefile.am
@@ -170,6 +170,8 @@ libapptools_a_sources = \
 	gimptextoptions.h		\
 	gimptexttool.c			\
 	gimptexttool.h			\
+	gimptexttool-editor.c		\
+	gimptexttool-editor.h		\
 	gimpthresholdtool.c		\
 	gimpthresholdtool.h		\
 	gimptool.c			\
diff --git a/app/tools/gimptexttool-editor.c b/app/tools/gimptexttool-editor.c
new file mode 100644
index 0000000..dbdefdf
--- /dev/null
+++ b/app/tools/gimptexttool-editor.c
@@ -0,0 +1,833 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpTextTool
+ * Copyright (C) 2002-2004  Sven Neumann <sven gimp org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "tools-types.h"
+
+#include "text/gimptext.h"
+#include "text/gimptextlayout.h"
+
+#include "widgets/gimpdialogfactory.h"
+#include "widgets/gimptexteditor.h"
+#include "widgets/gimptextproxy.h"
+
+#include "display/gimpdisplay.h"
+#include "display/gimpdisplayshell.h"
+
+#include "gimprectangletool.h"
+#include "gimptextoptions.h"
+#include "gimptexttool.h"
+#include "gimptexttool-editor.h"
+
+#include "gimp-intl.h"
+
+
+/*  local function prototypes  */
+
+static void   gimp_text_tool_ensure_proxy       (GimpTextTool    *text_tool);
+static void   gimp_text_tool_move_cursor        (GimpTextTool    *text_tool,
+                                                 GtkMovementStep  step,
+                                                 gint             count,
+                                                 gboolean         extend_selection);
+static void   gimp_text_tool_insert_at_cursor   (GimpTextTool    *text_tool,
+                                                 const gchar     *str);
+static void   gimp_text_tool_delete_from_cursor (GimpTextTool    *text_tool,
+                                                 GtkDeleteType    type,
+                                                 gint             count);
+static void   gimp_text_tool_backspace          (GimpTextTool    *text_tool);
+static void   gimp_text_tool_toggle_overwrite   (GimpTextTool    *text_tool);
+static void   gimp_text_tool_select_all         (GimpTextTool    *text_tool,
+                                                 gboolean         select);
+static void   gimp_text_tool_options_notify     (GimpTextOptions *options,
+                                                 GParamSpec      *pspec,
+                                                 GimpTextTool    *text_tool);
+static void   gimp_text_tool_editor_dialog      (GimpTextTool    *text_tool);
+static void   gimp_text_tool_enter_text         (GimpTextTool    *text_tool,
+                                                 const gchar     *str);
+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);
+static void   gimp_text_tool_preedit_changed_cb (GtkIMContext    *context,
+                                                 GimpTextTool    *text_tool);
+
+
+/*  public functions  */
+
+void
+gimp_text_tool_editor_init (GimpTextTool *text_tool)
+{
+  text_tool->im_context = gtk_im_multicontext_new ();
+
+  text_tool->preedit_string = NULL;
+  text_tool->overwrite_mode = FALSE;
+
+  text_tool->x_pos = -1;
+
+  g_signal_connect (text_tool->im_context, "commit",
+                    G_CALLBACK (gimp_text_tool_commit_cb),
+                    text_tool);
+  g_signal_connect (text_tool->im_context, "preedit-changed",
+                    G_CALLBACK (gimp_text_tool_preedit_changed_cb),
+                    text_tool);
+
+}
+
+void
+gimp_text_tool_editor_finalize (GimpTextTool *text_tool)
+{
+  if (text_tool->im_context)
+    {
+      g_object_unref (text_tool->im_context);
+      text_tool->im_context = NULL;
+    }
+}
+
+void
+gimp_text_tool_editor_start (GimpTextTool *text_tool)
+{
+  GimpTool         *tool    = GIMP_TOOL (text_tool);
+  GimpTextOptions  *options = GIMP_TEXT_TOOL_GET_OPTIONS (text_tool);
+  GimpDisplayShell *shell   = gimp_display_get_shell (tool->display);
+
+  gtk_im_context_set_client_window (text_tool->im_context,
+                                    gtk_widget_get_window (shell->canvas));
+
+  gtk_im_context_focus_in (text_tool->im_context);
+
+  if (text_tool->text)
+    gtk_text_buffer_set_text (text_tool->text_buffer,
+                              text_tool->text->text, -1);
+  else
+    gtk_text_buffer_set_text (text_tool->text_buffer, "", -1);
+
+  gimp_text_tool_update_layout (text_tool);
+
+  if (options->use_editor)
+    gimp_text_tool_editor_dialog (text_tool);
+
+  g_signal_connect (options, "notify::use-editor",
+                    G_CALLBACK (gimp_text_tool_options_notify),
+                    text_tool);
+}
+
+void
+gimp_text_tool_editor_halt (GimpTextTool *text_tool)
+{
+  GimpTextOptions *options = GIMP_TEXT_TOOL_GET_OPTIONS (text_tool);
+
+  g_signal_handlers_disconnect_by_func (options,
+                                        gimp_text_tool_options_notify,
+                                        text_tool);
+
+  if (text_tool->editor)
+    gtk_widget_destroy (text_tool->editor);
+
+  if (text_tool->proxy_text_view)
+    {
+      gtk_widget_destroy (text_tool->offscreen_window);
+      text_tool->offscreen_window = NULL;
+      text_tool->proxy_text_view = NULL;
+    }
+
+  gtk_im_context_focus_out (text_tool->im_context);
+
+  gtk_im_context_set_client_window (text_tool->im_context, NULL);
+}
+
+gboolean
+gimp_text_tool_editor_key_press (GimpTextTool *text_tool,
+                                 GdkEventKey  *kevent,
+                                 GimpDisplay  *display)
+{
+  GtkTextBuffer *buffer = text_tool->text_buffer;
+  GtkTextIter    cursor;
+  GtkTextIter    selection;
+  gint           x_pos  = -1;
+  gboolean       retval = TRUE;
+
+  if (gtk_im_context_filter_keypress (text_tool->im_context, kevent))
+    {
+      text_tool->needs_im_reset = TRUE;
+      text_tool->x_pos          = -1;
+
+      return TRUE;
+    }
+
+  gimp_text_tool_ensure_proxy (text_tool);
+
+  if (gtk_bindings_activate_event (GTK_OBJECT (text_tool->proxy_text_view),
+                                   kevent))
+    {
+      g_printerr ("binding handled event!\n");
+      return TRUE;
+    }
+
+  gtk_text_buffer_get_iter_at_mark (buffer, &cursor,
+                                    gtk_text_buffer_get_insert (buffer));
+  gtk_text_buffer_get_iter_at_mark (buffer, &selection,
+                                    gtk_text_buffer_get_selection_bound (buffer));
+
+  gimp_draw_tool_pause (GIMP_DRAW_TOOL (text_tool));
+
+  switch (kevent->keyval)
+    {
+    case GDK_Return:
+    case GDK_KP_Enter:
+    case GDK_ISO_Enter:
+      gimp_text_tool_enter_text (text_tool, "\n");
+      gimp_text_tool_reset_im_context (text_tool);
+      gimp_text_tool_update_layout (text_tool);
+      break;
+
+    case GDK_Tab:
+    case GDK_KP_Tab:
+    case GDK_ISO_Left_Tab:
+      gimp_text_tool_enter_text (text_tool, "\t");
+      gimp_text_tool_reset_im_context (text_tool);
+      gimp_text_tool_update_layout (text_tool);
+      break;
+
+    case GDK_Escape:
+      gimp_rectangle_tool_cancel (GIMP_RECTANGLE_TOOL (text_tool));
+      gimp_tool_control (GIMP_TOOL (text_tool), GIMP_TOOL_ACTION_HALT,
+                         GIMP_TOOL (text_tool)->display);
+      break;
+
+    default:
+      retval = FALSE;
+    }
+
+  text_tool->x_pos = x_pos;
+
+  gimp_draw_tool_resume (GIMP_DRAW_TOOL (text_tool));
+
+  return retval;
+}
+
+static void
+gimp_text_tool_ensure_proxy (GimpTextTool *text_tool)
+{
+  GimpTool         *tool  = GIMP_TOOL (text_tool);
+  GimpDisplayShell *shell = gimp_display_get_shell (tool->display);
+
+  if (text_tool->offscreen_window &&
+      gtk_widget_get_screen (text_tool->offscreen_window) !=
+      gtk_widget_get_screen (GTK_WIDGET (shell)))
+    {
+      gtk_window_set_screen (GTK_WINDOW (text_tool->offscreen_window),
+                             gtk_widget_get_screen (GTK_WIDGET (shell)));
+      gtk_window_move (GTK_WINDOW (text_tool->offscreen_window), -200, -200);
+    }
+  else if (! text_tool->offscreen_window)
+    {
+      text_tool->offscreen_window = gtk_window_new (GTK_WINDOW_POPUP);
+      gtk_window_set_screen (GTK_WINDOW (text_tool->offscreen_window),
+                             gtk_widget_get_screen (GTK_WIDGET (shell)));
+      gtk_window_move (GTK_WINDOW (text_tool->offscreen_window), -200, -200);
+      gtk_widget_show (text_tool->offscreen_window);
+
+      text_tool->proxy_text_view = gimp_text_proxy_new ();
+      gtk_container_add (GTK_CONTAINER (text_tool->offscreen_window),
+                         text_tool->proxy_text_view);
+      gtk_widget_show (text_tool->proxy_text_view);
+
+      g_signal_connect_swapped (text_tool->proxy_text_view, "move-cursor",
+                                G_CALLBACK (gimp_text_tool_move_cursor),
+                                text_tool);
+      g_signal_connect_swapped (text_tool->proxy_text_view, "insert-at-cursor",
+                                G_CALLBACK (gimp_text_tool_insert_at_cursor),
+                                text_tool);
+      g_signal_connect_swapped (text_tool->proxy_text_view, "delete-from-cursor",
+                                G_CALLBACK (gimp_text_tool_delete_from_cursor),
+                                text_tool);
+      g_signal_connect_swapped (text_tool->proxy_text_view, "backspace",
+                                G_CALLBACK (gimp_text_tool_backspace),
+                                text_tool);
+      g_signal_connect_swapped (text_tool->proxy_text_view, "cut-clipboard",
+                                G_CALLBACK (gimp_text_tool_cut_clipboard),
+                                text_tool);
+      g_signal_connect_swapped (text_tool->proxy_text_view, "copy-clipboard",
+                                G_CALLBACK (gimp_text_tool_copy_clipboard),
+                                text_tool);
+      g_signal_connect_swapped (text_tool->proxy_text_view, "paste-clipboard",
+                                G_CALLBACK (gimp_text_tool_paste_clipboard),
+                                text_tool);
+      g_signal_connect_swapped (text_tool->proxy_text_view, "toggle-overwrite",
+                                G_CALLBACK (gimp_text_tool_toggle_overwrite),
+                                text_tool);
+      g_signal_connect_swapped (text_tool->proxy_text_view, "select-all",
+                                G_CALLBACK (gimp_text_tool_select_all),
+                                text_tool);
+    }
+}
+
+static void
+gimp_text_tool_move_cursor (GimpTextTool    *text_tool,
+                            GtkMovementStep  step,
+                            gint             count,
+                            gboolean         extend_selection)
+{
+  GtkTextBuffer *buffer = text_tool->text_buffer;
+  GtkTextIter    cursor;
+  GtkTextIter    selection;
+  GtkTextIter   *sel_start;
+  gboolean       cancel_selection = FALSE;
+  gint           x_pos  = -1;
+
+  g_printerr ("%s: %s count = %d, select = %s\n",
+              G_STRFUNC,
+              g_enum_get_value (g_type_class_ref (GTK_TYPE_MOVEMENT_STEP),
+                                step)->value_name,
+              count,
+              extend_selection ? "TRUE" : "FALSE");
+
+  gtk_text_buffer_get_iter_at_mark (buffer, &cursor,
+                                    gtk_text_buffer_get_insert (buffer));
+  gtk_text_buffer_get_iter_at_mark (buffer, &selection,
+                                    gtk_text_buffer_get_selection_bound (buffer));
+
+  if (extend_selection)
+    {
+      sel_start = &selection;
+    }
+  else
+    {
+      /*  when there is a selection, moving the cursor without
+       *  extending it should move the cursor to the end of the
+       *  selection that is in moving direction
+       */
+      if (count > 0)
+        gtk_text_iter_order (&selection, &cursor);
+      else
+        gtk_text_iter_order (&cursor, &selection);
+
+      sel_start = &cursor;
+
+      /* if we actually have a selection, just move *to* the beginning/end
+       * of the selection and not *from* there on LOGICAL_POSITIONS
+       * and VISUAL_POSITIONS movement
+       */
+      if (! gtk_text_iter_equal (&cursor, &selection))
+        cancel_selection = TRUE;
+    }
+
+  switch (step)
+    {
+    case GTK_MOVEMENT_LOGICAL_POSITIONS:
+      if (! cancel_selection)
+        gtk_text_iter_forward_visible_cursor_positions (&cursor, count);
+      break;
+
+    case GTK_MOVEMENT_VISUAL_POSITIONS:
+      if (! cancel_selection)
+        {
+          if (count < 0)
+            gtk_text_iter_backward_cursor_position (&cursor);
+          else if (count > 0)
+            gtk_text_iter_forward_cursor_position (&cursor);
+        }
+      break;
+
+    case GTK_MOVEMENT_WORDS:
+      if (count < 0)
+        {
+          gtk_text_iter_backward_visible_word_starts (&cursor, -count);
+        }
+      else if (count > 0)
+        {
+	  if (! gtk_text_iter_forward_visible_word_ends (&cursor, count))
+	    gtk_text_iter_forward_to_line_end (&cursor);
+        }
+      break;
+
+    case GTK_MOVEMENT_DISPLAY_LINES:
+      {
+        GtkTextIter      start;
+        GtkTextIter      end;
+        gchar           *string;
+        gint             cursor_index;
+        PangoLayout     *layout;
+        PangoLayoutLine *layout_line;
+        PangoLayoutIter *layout_iter;
+        PangoRectangle   logical;
+        gint             line;
+        gint             trailing;
+        gint             i;
+
+        gtk_text_buffer_get_bounds (buffer, &start, &end);
+
+        string = gtk_text_buffer_get_text (buffer, &start, &cursor, FALSE);
+        cursor_index = strlen (string);
+        g_free (string);
+
+        layout = gimp_text_layout_get_pango_layout (text_tool->layout);
+
+        pango_layout_index_to_line_x (layout, cursor_index, FALSE,
+                                      &line, &x_pos);
+
+        layout_iter = pango_layout_get_iter (layout);
+        for (i = 0; i < line; i++)
+          pango_layout_iter_next_line (layout_iter);
+
+        pango_layout_iter_get_line_extents (layout_iter, NULL, &logical);
+
+        pango_layout_iter_free (layout_iter);
+
+        /*  try to go to the remembered x_pos if it exists *and* we are at
+         *  the beginning or at the end of the current line
+         */
+        if (text_tool->x_pos != -1 && (x_pos <= logical.x ||
+                                       x_pos >= logical.width))
+          x_pos = text_tool->x_pos;
+
+        line += count;
+
+        if (line < 0)
+          {
+            cursor = start;
+            break;
+          }
+        else if (line >= pango_layout_get_line_count (layout))
+          {
+            cursor = end;
+            break;
+          }
+
+        layout_iter = pango_layout_get_iter (layout);
+        for (i = 0; i < line; i++)
+          pango_layout_iter_next_line (layout_iter);
+
+        layout_line = pango_layout_iter_get_line_readonly (layout_iter);
+        pango_layout_iter_get_line_extents (layout_iter, NULL, &logical);
+
+        pango_layout_iter_free (layout_iter);
+
+        pango_layout_line_x_to_index (layout_line, x_pos,
+                                      &cursor_index, &trailing);
+
+        string = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+
+        string[cursor_index] = '\0';
+
+        gtk_text_buffer_get_iter_at_offset (buffer, &cursor,
+                                            g_utf8_strlen (string, -1));
+
+        g_free (string);
+
+        while (trailing--)
+          gtk_text_iter_forward_char (&cursor);
+      }
+      break;
+
+    case GTK_MOVEMENT_PAGES: /* well... */
+    case GTK_MOVEMENT_BUFFER_ENDS:
+      if (count < 0)
+        {
+          gtk_text_buffer_get_start_iter (buffer, &cursor);
+        }
+      else if (count > 0)
+        {
+          gtk_text_buffer_get_end_iter (buffer, &cursor);
+        }
+      break;
+
+    case GTK_MOVEMENT_PARAGRAPH_ENDS:
+      if (count < 0)
+        {
+          gtk_text_iter_set_line_offset (&cursor, 0);
+        }
+      else if (count > 0)
+        {
+          if (! gtk_text_iter_ends_line (&cursor))
+            gtk_text_iter_forward_to_line_end (&cursor);
+        }
+      break;
+
+    case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
+      if (count < 0)
+        {
+          gtk_text_iter_set_line_offset (&cursor, 0);
+        }
+      else if (count > 0)
+        {
+          if (! gtk_text_iter_ends_line (&cursor))
+            gtk_text_iter_forward_to_line_end (&cursor);
+        }
+      break;
+
+    default:
+      return;
+    }
+
+  text_tool->x_pos = x_pos;
+
+  gimp_draw_tool_pause (GIMP_DRAW_TOOL (text_tool));
+
+  gtk_text_buffer_select_range (buffer, &cursor, sel_start);
+
+  gimp_draw_tool_resume (GIMP_DRAW_TOOL (text_tool));
+}
+
+static void
+gimp_text_tool_insert_at_cursor (GimpTextTool *text_tool,
+                                 const gchar  *str)
+{
+  gtk_text_buffer_insert_interactive_at_cursor (text_tool->text_buffer,
+                                                str, -1, TRUE);
+}
+
+static gboolean
+is_whitespace (gunichar ch,
+               gpointer user_data)
+{
+  return (ch == ' ' || ch == '\t');
+}
+
+static gboolean
+is_not_whitespace (gunichar ch,
+                   gpointer user_data)
+{
+  return ! is_whitespace (ch, user_data);
+}
+
+static gboolean
+find_whitepace_region (const GtkTextIter *center,
+                       GtkTextIter       *start,
+                       GtkTextIter       *end)
+{
+  *start = *center;
+  *end   = *center;
+
+  if (gtk_text_iter_backward_find_char (start, is_not_whitespace, NULL, NULL))
+    gtk_text_iter_forward_char (start); /* we want the first whitespace... */
+
+  if (is_whitespace (gtk_text_iter_get_char (end), NULL))
+    gtk_text_iter_forward_find_char (end, is_not_whitespace, NULL, NULL);
+
+  return ! gtk_text_iter_equal (start, end);
+}
+
+static void
+gimp_text_tool_delete_from_cursor (GimpTextTool  *text_tool,
+                                   GtkDeleteType  type,
+                                   gint           count)
+{
+  GtkTextBuffer *buffer = text_tool->text_buffer;
+  GtkTextIter    cursor;
+  GtkTextIter    end;
+
+  g_printerr ("%s: %s count = %d\n",
+              G_STRFUNC,
+              g_enum_get_value (g_type_class_ref (GTK_TYPE_DELETE_TYPE),
+                                type)->value_name,
+              count);
+
+  gtk_text_buffer_get_iter_at_mark (buffer, &cursor,
+                                    gtk_text_buffer_get_insert (buffer));
+  end = cursor;
+
+  switch (type)
+    {
+    case GTK_DELETE_CHARS:
+      if (gtk_text_buffer_get_has_selection (buffer))
+        {
+          gtk_text_buffer_delete_selection (buffer, TRUE, TRUE);
+          return;
+        }
+      else
+        {
+          gtk_text_iter_forward_cursor_positions (&end, count);
+        }
+      break;
+
+    case GTK_DELETE_WORD_ENDS:
+      if (count < 0)
+        {
+          if (! gtk_text_iter_starts_word (&cursor))
+            gtk_text_iter_backward_visible_word_starts (&cursor, 1);
+        }
+      else if (count > 0)
+        {
+          if (! gtk_text_iter_ends_word (&end) &&
+              ! gtk_text_iter_forward_visible_word_ends (&end, 1))
+            gtk_text_iter_forward_to_line_end (&end);
+        }
+      break;
+
+    case GTK_DELETE_WORDS:
+      if (! gtk_text_iter_starts_word (&cursor))
+        gtk_text_iter_backward_visible_word_starts (&cursor, 1);
+
+      if (! gtk_text_iter_ends_word (&end) &&
+          ! gtk_text_iter_forward_visible_word_ends (&end, 1))
+        gtk_text_iter_forward_to_line_end (&end);
+      break;
+
+    case GTK_DELETE_DISPLAY_LINES:
+      break;
+
+    case GTK_DELETE_DISPLAY_LINE_ENDS:
+      break;
+
+    case GTK_DELETE_PARAGRAPH_ENDS:
+      if (count < 0)
+        {
+          gtk_text_iter_set_line_offset (&cursor, 0);
+        }
+      else if (count > 0)
+        {
+          if (! gtk_text_iter_ends_line (&end))
+            gtk_text_iter_forward_to_line_end (&end);
+          else
+            gtk_text_iter_forward_cursor_positions (&end, 1);
+        }
+      break;
+
+    case GTK_DELETE_PARAGRAPHS:
+      break;
+
+    case GTK_DELETE_WHITESPACE:
+      find_whitepace_region (&cursor, &cursor, &end);
+      break;
+    }
+
+  if (! gtk_text_iter_equal (&cursor, &end))
+    {
+      gtk_text_buffer_delete_interactive (buffer, &cursor, &end, TRUE);
+    }
+}
+
+static void
+gimp_text_tool_backspace (GimpTextTool *text_tool)
+{
+  GtkTextBuffer *buffer = text_tool->text_buffer;
+
+  if (gtk_text_buffer_get_has_selection (buffer))
+    {
+      gtk_text_buffer_delete_selection (buffer, TRUE, TRUE);
+    }
+  else
+    {
+      GtkTextIter cursor;
+
+      gtk_text_buffer_get_iter_at_mark (buffer, &cursor,
+                                        gtk_text_buffer_get_insert (buffer));
+
+      gtk_text_buffer_backspace (buffer, &cursor, TRUE, TRUE);
+    }
+}
+
+static void
+gimp_text_tool_toggle_overwrite (GimpTextTool *text_tool)
+{
+  gimp_draw_tool_pause (GIMP_DRAW_TOOL (text_tool));
+
+  text_tool->overwrite_mode = ! text_tool->overwrite_mode;
+
+  gimp_draw_tool_resume (GIMP_DRAW_TOOL (text_tool));
+}
+
+static void
+gimp_text_tool_select_all (GimpTextTool *text_tool,
+                           gboolean      select)
+{
+  GtkTextBuffer *buffer = text_tool->text_buffer;
+
+  gimp_draw_tool_pause (GIMP_DRAW_TOOL (text_tool));
+
+  if (select)
+    {
+      GtkTextIter start, end;
+
+      gtk_text_buffer_get_bounds (buffer, &start, &end);
+      gtk_text_buffer_select_range (buffer, &start, &end);
+    }
+  else
+    {
+      GtkTextIter cursor;
+
+      gtk_text_buffer_get_iter_at_mark (buffer, &cursor,
+					gtk_text_buffer_get_insert (buffer));
+      gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &cursor);
+    }
+
+  gimp_draw_tool_resume (GIMP_DRAW_TOOL (text_tool));
+}
+
+static void
+gimp_text_tool_options_notify (GimpTextOptions *options,
+                               GParamSpec      *pspec,
+                               GimpTextTool    *text_tool)
+{
+  const gchar *param_name = g_param_spec_get_name (pspec);
+
+  if (! strcmp (param_name, "use-editor"))
+    {
+      if (options->use_editor)
+        {
+          if (text_tool->text)
+            gimp_text_tool_editor_dialog (text_tool);
+        }
+      else
+        {
+          if (text_tool->editor)
+            gtk_widget_destroy (text_tool->editor);
+        }
+    }
+}
+
+static void
+gimp_text_tool_editor_dialog (GimpTextTool *text_tool)
+{
+  GimpTool          *tool    = GIMP_TOOL (text_tool);
+  GimpTextOptions   *options = GIMP_TEXT_TOOL_GET_OPTIONS (text_tool);
+  GimpDialogFactory *dialog_factory;
+  GtkWindow         *parent  = NULL;
+
+  if (text_tool->editor)
+    {
+      gtk_window_present (GTK_WINDOW (text_tool->editor));
+      return;
+    }
+
+  dialog_factory = gimp_dialog_factory_from_name ("toplevel");
+
+  if (tool->display)
+    {
+      GimpDisplayShell *shell = gimp_display_get_shell (tool->display);
+
+      parent = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (shell)));
+    }
+
+  text_tool->editor = gimp_text_options_editor_new (parent, options,
+                                                    gimp_dialog_factory_get_menu_factory (dialog_factory),
+                                                    _("GIMP Text Editor"),
+                                                    text_tool->text_buffer);
+
+  g_object_add_weak_pointer (G_OBJECT (text_tool->editor),
+                             (gpointer) &text_tool->editor);
+
+  gimp_dialog_factory_add_foreign (dialog_factory,
+                                   "gimp-text-tool-dialog",
+                                   text_tool->editor);
+
+  gtk_widget_show (text_tool->editor);
+}
+
+gchar *
+gimp_text_tool_editor_get_text (GimpTextTool *text_tool)
+{
+  GtkTextBuffer *buffer = text_tool->text_buffer;
+  GtkTextIter    start, end;
+  GtkTextIter    selstart, selend;
+  gchar         *string;
+  gchar         *fb;
+  gchar         *lb;
+
+  gtk_text_buffer_get_bounds (buffer, &start, &end);
+  gtk_text_buffer_get_selection_bounds (buffer, &selstart, &selend);
+
+  fb = gtk_text_buffer_get_text (buffer, &start, &selstart, TRUE);
+  lb = gtk_text_buffer_get_text (buffer, &selstart, &end, TRUE);
+
+  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);
+    }
+
+  g_free (fb);
+  g_free (lb);
+
+  return string;
+}
+
+static void
+gimp_text_tool_enter_text (GimpTextTool *text_tool,
+                           const gchar  *str)
+{
+  GtkTextBuffer *buffer = text_tool->text_buffer;
+  gboolean       had_selection;
+
+  had_selection = gtk_text_buffer_get_has_selection (buffer);
+
+  gimp_text_tool_delete_selection (text_tool);
+
+  if (! had_selection && text_tool->overwrite_mode && strcmp (str, "\n"))
+    {
+      GtkTextIter cursor;
+
+      gtk_text_buffer_get_iter_at_mark (buffer, &cursor,
+                                        gtk_text_buffer_get_insert (buffer));
+
+      if (! gtk_text_iter_ends_line (&cursor))
+        gimp_text_tool_delete_from_cursor (text_tool, GTK_DELETE_CHARS, 1);
+    }
+
+  gtk_text_buffer_insert_at_cursor (buffer, str, -1);
+}
+
+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);
+}
diff --git a/app/tools/gimptexttool-editor.h b/app/tools/gimptexttool-editor.h
new file mode 100644
index 0000000..28aaad2
--- /dev/null
+++ b/app/tools/gimptexttool-editor.h
@@ -0,0 +1,38 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpTextTool
+ * Copyright (C) 2002-2004  Sven Neumann <sven gimp org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_TEXT_TOOL_EDITOR_H__
+#define __GIMP_TEXT_TOOL_EDITOR_H__
+
+
+void       gimp_text_tool_editor_init      (GimpTextTool *text_tool);
+void       gimp_text_tool_editor_finalize  (GimpTextTool *text_tool);
+
+void       gimp_text_tool_editor_start     (GimpTextTool *text_tool);
+void       gimp_text_tool_editor_halt      (GimpTextTool *text_tool);
+
+gboolean   gimp_text_tool_editor_key_press (GimpTextTool *text_tool,
+                                            GdkEventKey  *kevent,
+                                            GimpDisplay  *display);
+
+gchar    * gimp_text_tool_editor_get_text  (GimpTextTool *text_tool);
+
+
+#endif /* __GIMP_TEXT_TOOL_EDITOR_H__ */
diff --git a/app/tools/gimptexttool.c b/app/tools/gimptexttool.c
index 8f4ac68..823b5e2 100644
--- a/app/tools/gimptexttool.c
+++ b/app/tools/gimptexttool.c
@@ -22,7 +22,6 @@
 
 #include <gegl.h>
 #include <gtk/gtk.h>
-#include <gdk/gdkkeysyms.h>
 
 #include "libgimpconfig/gimpconfig.h"
 #include "libgimpwidgets/gimpwidgets.h"
@@ -49,19 +48,16 @@
 #include "widgets/gimpdialogfactory.h"
 #include "widgets/gimphelp-ids.h"
 #include "widgets/gimpmenufactory.h"
-#include "widgets/gimptexteditor.h"
-#include "widgets/gimptextproxy.h"
 #include "widgets/gimpuimanager.h"
 #include "widgets/gimpviewabledialog.h"
 
 #include "display/gimpdisplay.h"
 #include "display/gimpdisplayshell.h"
 
-#include "gimpeditselectiontool.h"
 #include "gimprectangletool.h"
-#include "gimprectangleoptions.h"
 #include "gimptextoptions.h"
 #include "gimptexttool.h"
+#include "gimptexttool-editor.h"
 #include "gimptoolcontrol.h"
 
 #include "gimp-intl.h"
@@ -133,28 +129,10 @@ static gboolean  gimp_text_tool_rectangle_change_complete
 
 static void      gimp_text_tool_halt            (GimpTextTool      *text_tool);
 
-static void      gimp_text_tool_ensure_proxy    (GimpTextTool      *text_tool);
-static void      gimp_text_tool_move_cursor     (GimpTextTool      *text_tool,
-                                                 GtkMovementStep    step,
-                                                 gint               count,
-                                                 gboolean           extend_selection);
-static void     gimp_text_tool_insert_at_cursor (GimpTextTool      *text_tool,
-                                                 const gchar       *str);
-static void   gimp_text_tool_delete_from_cursor (GimpTextTool      *text_tool,
-                                                 GtkDeleteType      type,
-                                                 gint               count);
-static void      gimp_text_tool_backspace       (GimpTextTool      *text_tool);
-static void     gimp_text_tool_toggle_overwrite (GimpTextTool      *text_tool);
-static void      gimp_text_tool_select_all      (GimpTextTool      *text_tool,
-                                                 gboolean           select);
-
 static void      gimp_text_tool_connect         (GimpTextTool      *text_tool,
                                                  GimpTextLayer     *layer,
                                                  GimpText          *text);
 
-static void      gimp_text_tool_options_notify  (GimpTextOptions   *options,
-                                                 GParamSpec        *pspec,
-                                                 GimpTextTool      *text_tool);
 static void      gimp_text_tool_layer_notify    (GimpTextLayer     *layer,
                                                  GParamSpec        *pspec,
                                                  GimpTextTool      *text_tool);
@@ -170,10 +148,6 @@ static void      gimp_text_tool_apply           (GimpTextTool      *text_tool);
 static void      gimp_text_tool_create_layer    (GimpTextTool      *text_tool,
                                                  GimpText          *text);
 
-static void      gimp_text_tool_editor_dialog   (GimpTextTool      *text_tool);
-static void      gimp_text_tool_editor          (GimpTextTool      *text_tool);
-static gchar   * gimp_text_tool_editor_get_text (GimpTextTool      *text_tool);
-
 static void      gimp_text_tool_layer_changed   (GimpImage         *image,
                                                  GimpTextTool      *text_tool);
 static void      gimp_text_tool_set_image       (GimpTextTool      *text_tool,
@@ -182,11 +156,6 @@ static gboolean  gimp_text_tool_set_drawable    (GimpTextTool      *text_tool,
                                                  GimpDrawable      *drawable,
                                                  gboolean           confirm);
 
-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_enter_text      (GimpTextTool      *text_tool,
-                                                 const gchar       *str);
 static void gimp_text_tool_text_buffer_changed  (GtkTextBuffer     *text_buffer,
                                                  GimpTextTool      *text_tool);
 static void gimp_text_tool_text_buffer_mark_set (GtkTextBuffer     *text_buffer,
@@ -198,14 +167,6 @@ 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);
-static void gimp_text_tool_preedit_changed_cb   (GtkIMContext      *context,
-                                                 GimpTextTool      *text_tool);
-
 
 G_DEFINE_TYPE_WITH_CODE (GimpTextTool, gimp_text_tool,
                          GIMP_TYPE_DRAW_TOOL,
@@ -293,16 +254,7 @@ gimp_text_tool_init (GimpTextTool *text_tool)
                     G_CALLBACK (gimp_text_tool_text_buffer_mark_set),
                     text_tool);
 
-  text_tool->im_context = gtk_im_multicontext_new ();
-
-  text_tool->preedit_string = NULL;
-
-  g_signal_connect (text_tool->im_context, "commit",
-                    G_CALLBACK (gimp_text_tool_commit_cb),
-                    text_tool);
-  g_signal_connect (text_tool->im_context, "preedit-changed",
-                    G_CALLBACK (gimp_text_tool_preedit_changed_cb),
-                    text_tool);
+  gimp_text_tool_editor_init (text_tool);
 
   gimp_tool_control_set_scroll_lock          (tool->control, TRUE);
   gimp_tool_control_set_wants_double_click   (tool->control, TRUE);
@@ -346,10 +298,6 @@ gimp_text_tool_constructor (GType                  type,
                            G_CALLBACK (gimp_text_tool_proxy_notify),
                            text_tool, 0);
 
-  g_signal_connect_object (options, "notify::use-editor",
-                           G_CALLBACK (gimp_text_tool_options_notify),
-                           text_tool, 0);
-
   g_object_set (options,
                 "highlight", FALSE,
                 NULL);
@@ -390,11 +338,7 @@ gimp_text_tool_finalize (GObject *object)
       text_tool->text_buffer = NULL;
     }
 
-  if (text_tool->im_context)
-    {
-      g_object_unref (text_tool->im_context);
-      text_tool->im_context = NULL;
-    }
+  gimp_text_tool_editor_finalize (text_tool);
 
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
@@ -517,7 +461,7 @@ gimp_text_tool_button_press (GimpTool            *tool,
                   /* enable keyboard-handling for the text */
                   if (text_tool->text && text_tool->text != text)
                     {
-                      gimp_text_tool_editor (text_tool);
+                      gimp_text_tool_editor_start (text_tool);
                     }
                 }
 
@@ -578,7 +522,7 @@ gimp_text_tool_button_press (GimpTool            *tool,
   text_tool->text_box_fixed = FALSE;
 
   gimp_text_tool_connect (text_tool, NULL, NULL);
-  gimp_text_tool_editor (text_tool);
+  gimp_text_tool_editor_start (text_tool);
 
   gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
 }
@@ -791,71 +735,11 @@ gimp_text_tool_key_press (GimpTool    *tool,
                           GimpDisplay *display)
 {
   GimpTextTool  *text_tool = GIMP_TEXT_TOOL (tool);
-  GtkTextBuffer *buffer    = text_tool->text_buffer;
-  GtkTextIter    cursor;
-  GtkTextIter    selection;
-  gint           x_pos  = -1;
-  gboolean       retval = TRUE;
 
   if (display != tool->display)
     return FALSE;
 
-  if (gtk_im_context_filter_keypress (text_tool->im_context, kevent))
-    {
-      text_tool->needs_im_reset = TRUE;
-      text_tool->x_pos          = -1;
-
-      return TRUE;
-    }
-
-  gimp_text_tool_ensure_proxy (text_tool);
-
-  if (gtk_bindings_activate_event (GTK_OBJECT (text_tool->proxy_text_view),
-                                   kevent))
-    {
-      g_printerr ("binding handled event!\n");
-      return TRUE;
-    }
-
-  gtk_text_buffer_get_iter_at_mark (buffer, &cursor,
-                                    gtk_text_buffer_get_insert (buffer));
-  gtk_text_buffer_get_iter_at_mark (buffer, &selection,
-                                    gtk_text_buffer_get_selection_bound (buffer));
-
-  gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
-
-  switch (kevent->keyval)
-    {
-    case GDK_Return:
-    case GDK_KP_Enter:
-    case GDK_ISO_Enter:
-      gimp_text_tool_enter_text (text_tool, "\n");
-      gimp_text_tool_reset_im_context (text_tool);
-      gimp_text_tool_update_layout (text_tool);
-      break;
-
-    case GDK_Tab:
-    case GDK_KP_Tab:
-    case GDK_ISO_Left_Tab:
-      gimp_text_tool_enter_text (text_tool, "\t");
-      gimp_text_tool_reset_im_context (text_tool);
-      gimp_text_tool_update_layout (text_tool);
-      break;
-
-    case GDK_Escape:
-      gimp_rectangle_tool_cancel (GIMP_RECTANGLE_TOOL (tool));
-      gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display);
-      break;
-
-    default:
-      retval = FALSE;
-    }
-
-  text_tool->x_pos = x_pos;
-
-  gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
-
-  return retval;
+  return gimp_text_tool_editor_key_press (text_tool, kevent, display);
 }
 
 static void
@@ -1354,469 +1238,7 @@ gimp_text_tool_halt (GimpTextTool *text_tool)
 {
   gimp_text_tool_set_drawable (text_tool, NULL, FALSE);
 
-  if (text_tool->editor)
-    gtk_widget_destroy (text_tool->editor);
-
-  if (text_tool->proxy_text_view)
-    {
-      gtk_widget_destroy (text_tool->offscreen_window);
-      text_tool->offscreen_window = NULL;
-      text_tool->proxy_text_view = NULL;
-    }
-
-  gtk_im_context_focus_out (text_tool->im_context);
-
-  gtk_im_context_set_client_window (text_tool->im_context, NULL);
-}
-
-static void
-gimp_text_tool_ensure_proxy (GimpTextTool *text_tool)
-{
-  GimpTool         *tool  = GIMP_TOOL (text_tool);
-  GimpDisplayShell *shell = gimp_display_get_shell (tool->display);
-
-  if (text_tool->offscreen_window &&
-      gtk_widget_get_screen (text_tool->offscreen_window) !=
-      gtk_widget_get_screen (GTK_WIDGET (shell)))
-    {
-      gtk_window_set_screen (GTK_WINDOW (text_tool->offscreen_window),
-                             gtk_widget_get_screen (GTK_WIDGET (shell)));
-      gtk_window_move (GTK_WINDOW (text_tool->offscreen_window), -200, -200);
-    }
-  else if (! text_tool->offscreen_window)
-    {
-      text_tool->offscreen_window = gtk_window_new (GTK_WINDOW_POPUP);
-      gtk_window_set_screen (GTK_WINDOW (text_tool->offscreen_window),
-                             gtk_widget_get_screen (GTK_WIDGET (shell)));
-      gtk_window_move (GTK_WINDOW (text_tool->offscreen_window), -200, -200);
-      gtk_widget_show (text_tool->offscreen_window);
-
-      text_tool->proxy_text_view = gimp_text_proxy_new ();
-      gtk_container_add (GTK_CONTAINER (text_tool->offscreen_window),
-                         text_tool->proxy_text_view);
-      gtk_widget_show (text_tool->proxy_text_view);
-
-      g_signal_connect_swapped (text_tool->proxy_text_view, "move-cursor",
-                                G_CALLBACK (gimp_text_tool_move_cursor),
-                                text_tool);
-      g_signal_connect_swapped (text_tool->proxy_text_view, "insert-at-cursor",
-                                G_CALLBACK (gimp_text_tool_insert_at_cursor),
-                                text_tool);
-      g_signal_connect_swapped (text_tool->proxy_text_view, "delete-from-cursor",
-                                G_CALLBACK (gimp_text_tool_delete_from_cursor),
-                                text_tool);
-      g_signal_connect_swapped (text_tool->proxy_text_view, "backspace",
-                                G_CALLBACK (gimp_text_tool_backspace),
-                                text_tool);
-      g_signal_connect_swapped (text_tool->proxy_text_view, "cut-clipboard",
-                                G_CALLBACK (gimp_text_tool_cut_clipboard),
-                                text_tool);
-      g_signal_connect_swapped (text_tool->proxy_text_view, "copy-clipboard",
-                                G_CALLBACK (gimp_text_tool_copy_clipboard),
-                                text_tool);
-      g_signal_connect_swapped (text_tool->proxy_text_view, "paste-clipboard",
-                                G_CALLBACK (gimp_text_tool_paste_clipboard),
-                                text_tool);
-      g_signal_connect_swapped (text_tool->proxy_text_view, "toggle-overwrite",
-                                G_CALLBACK (gimp_text_tool_toggle_overwrite),
-                                text_tool);
-      g_signal_connect_swapped (text_tool->proxy_text_view, "select-all",
-                                G_CALLBACK (gimp_text_tool_select_all),
-                                text_tool);
-    }
-}
-
-static void
-gimp_text_tool_move_cursor (GimpTextTool    *text_tool,
-                            GtkMovementStep  step,
-                            gint             count,
-                            gboolean         extend_selection)
-{
-  GtkTextBuffer *buffer = text_tool->text_buffer;
-  GtkTextIter    cursor;
-  GtkTextIter    selection;
-  GtkTextIter   *sel_start;
-  gboolean       cancel_selection = FALSE;
-  gint           x_pos  = -1;
-
-  g_printerr ("%s: %s count = %d, select = %s\n",
-              G_STRFUNC,
-              g_enum_get_value (g_type_class_ref (GTK_TYPE_MOVEMENT_STEP),
-                                step)->value_name,
-              count,
-              extend_selection ? "TRUE" : "FALSE");
-
-  gtk_text_buffer_get_iter_at_mark (buffer, &cursor,
-                                    gtk_text_buffer_get_insert (buffer));
-  gtk_text_buffer_get_iter_at_mark (buffer, &selection,
-                                    gtk_text_buffer_get_selection_bound (buffer));
-
-  if (extend_selection)
-    {
-      sel_start = &selection;
-    }
-  else
-    {
-      /*  when there is a selection, moving the cursor without
-       *  extending it should move the cursor to the end of the
-       *  selection that is in moving direction
-       */
-      if (count > 0)
-        gtk_text_iter_order (&selection, &cursor);
-      else
-        gtk_text_iter_order (&cursor, &selection);
-
-      sel_start = &cursor;
-
-      /* if we actually have a selection, just move *to* the beginning/end
-       * of the selection and not *from* there on LOGICAL_POSITIONS
-       * and VISUAL_POSITIONS movement
-       */
-      if (! gtk_text_iter_equal (&cursor, &selection))
-        cancel_selection = TRUE;
-    }
-
-  switch (step)
-    {
-    case GTK_MOVEMENT_LOGICAL_POSITIONS:
-      if (! cancel_selection)
-        gtk_text_iter_forward_visible_cursor_positions (&cursor, count);
-      break;
-
-    case GTK_MOVEMENT_VISUAL_POSITIONS:
-      if (! cancel_selection)
-        {
-          if (count < 0)
-            gtk_text_iter_backward_cursor_position (&cursor);
-          else if (count > 0)
-            gtk_text_iter_forward_cursor_position (&cursor);
-        }
-      break;
-
-    case GTK_MOVEMENT_WORDS:
-      if (count < 0)
-        {
-          gtk_text_iter_backward_visible_word_starts (&cursor, -count);
-        }
-      else if (count > 0)
-        {
-	  if (! gtk_text_iter_forward_visible_word_ends (&cursor, count))
-	    gtk_text_iter_forward_to_line_end (&cursor);
-        }
-      break;
-
-    case GTK_MOVEMENT_DISPLAY_LINES:
-      {
-        GtkTextIter      start;
-        GtkTextIter      end;
-        gchar           *string;
-        gint             cursor_index;
-        PangoLayout     *layout;
-        PangoLayoutLine *layout_line;
-        PangoLayoutIter *layout_iter;
-        PangoRectangle   logical;
-        gint             line;
-        gint             trailing;
-        gint             i;
-
-        gtk_text_buffer_get_bounds (buffer, &start, &end);
-
-        string = gtk_text_buffer_get_text (buffer, &start, &cursor, FALSE);
-        cursor_index = strlen (string);
-        g_free (string);
-
-        layout = gimp_text_layout_get_pango_layout (text_tool->layout);
-
-        pango_layout_index_to_line_x (layout, cursor_index, FALSE,
-                                      &line, &x_pos);
-
-        layout_iter = pango_layout_get_iter (layout);
-        for (i = 0; i < line; i++)
-          pango_layout_iter_next_line (layout_iter);
-
-        pango_layout_iter_get_line_extents (layout_iter, NULL, &logical);
-
-        pango_layout_iter_free (layout_iter);
-
-        /*  try to go to the remembered x_pos if it exists *and* we are at
-         *  the beginning or at the end of the current line
-         */
-        if (text_tool->x_pos != -1 && (x_pos <= logical.x ||
-                                       x_pos >= logical.width))
-          x_pos = text_tool->x_pos;
-
-        line += count;
-
-        if (line < 0)
-          {
-            cursor = start;
-            break;
-          }
-        else if (line >= pango_layout_get_line_count (layout))
-          {
-            cursor = end;
-            break;
-          }
-
-        layout_iter = pango_layout_get_iter (layout);
-        for (i = 0; i < line; i++)
-          pango_layout_iter_next_line (layout_iter);
-
-        layout_line = pango_layout_iter_get_line_readonly (layout_iter);
-        pango_layout_iter_get_line_extents (layout_iter, NULL, &logical);
-
-        pango_layout_iter_free (layout_iter);
-
-        pango_layout_line_x_to_index (layout_line, x_pos,
-                                      &cursor_index, &trailing);
-
-        string = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
-
-        string[cursor_index] = '\0';
-
-        gtk_text_buffer_get_iter_at_offset (buffer, &cursor,
-                                            g_utf8_strlen (string, -1));
-
-        g_free (string);
-
-        while (trailing--)
-          gtk_text_iter_forward_char (&cursor);
-      }
-      break;
-
-    case GTK_MOVEMENT_PAGES: /* well... */
-    case GTK_MOVEMENT_BUFFER_ENDS:
-      if (count < 0)
-        {
-          gtk_text_buffer_get_start_iter (buffer, &cursor);
-        }
-      else if (count > 0)
-        {
-          gtk_text_buffer_get_end_iter (buffer, &cursor);
-        }
-      break;
-
-    case GTK_MOVEMENT_PARAGRAPH_ENDS:
-      if (count < 0)
-        {
-          gtk_text_iter_set_line_offset (&cursor, 0);
-        }
-      else if (count > 0)
-        {
-          if (! gtk_text_iter_ends_line (&cursor))
-            gtk_text_iter_forward_to_line_end (&cursor);
-        }
-      break;
-
-    case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
-      if (count < 0)
-        {
-          gtk_text_iter_set_line_offset (&cursor, 0);
-        }
-      else if (count > 0)
-        {
-          if (! gtk_text_iter_ends_line (&cursor))
-            gtk_text_iter_forward_to_line_end (&cursor);
-        }
-      break;
-
-    default:
-      return;
-    }
-
-  text_tool->x_pos = x_pos;
-
-  gimp_draw_tool_pause (GIMP_DRAW_TOOL (text_tool));
-
-  gtk_text_buffer_select_range (buffer, &cursor, sel_start);
-
-  gimp_draw_tool_resume (GIMP_DRAW_TOOL (text_tool));
-}
-
-static void
-gimp_text_tool_insert_at_cursor (GimpTextTool *text_tool,
-                                 const gchar  *str)
-{
-  gtk_text_buffer_insert_interactive_at_cursor (text_tool->text_buffer,
-                                                str, -1, TRUE);
-}
-
-static gboolean
-is_whitespace (gunichar ch,
-               gpointer user_data)
-{
-  return (ch == ' ' || ch == '\t');
-}
-
-static gboolean
-is_not_whitespace (gunichar ch,
-                   gpointer user_data)
-{
-  return ! is_whitespace (ch, user_data);
-}
-
-static gboolean
-find_whitepace_region (const GtkTextIter *center,
-                       GtkTextIter       *start,
-                       GtkTextIter       *end)
-{
-  *start = *center;
-  *end   = *center;
-
-  if (gtk_text_iter_backward_find_char (start, is_not_whitespace, NULL, NULL))
-    gtk_text_iter_forward_char (start); /* we want the first whitespace... */
-
-  if (is_whitespace (gtk_text_iter_get_char (end), NULL))
-    gtk_text_iter_forward_find_char (end, is_not_whitespace, NULL, NULL);
-
-  return ! gtk_text_iter_equal (start, end);
-}
-
-static void
-gimp_text_tool_delete_from_cursor (GimpTextTool  *text_tool,
-                                   GtkDeleteType  type,
-                                   gint           count)
-{
-  GtkTextBuffer *buffer = text_tool->text_buffer;
-  GtkTextIter    cursor;
-  GtkTextIter    end;
-
-  g_printerr ("%s: %s count = %d\n",
-              G_STRFUNC,
-              g_enum_get_value (g_type_class_ref (GTK_TYPE_DELETE_TYPE),
-                                type)->value_name,
-              count);
-
-  gtk_text_buffer_get_iter_at_mark (buffer, &cursor,
-                                    gtk_text_buffer_get_insert (buffer));
-  end = cursor;
-
-  switch (type)
-    {
-    case GTK_DELETE_CHARS:
-      if (gtk_text_buffer_get_has_selection (buffer))
-        {
-          gtk_text_buffer_delete_selection (buffer, TRUE, TRUE);
-          return;
-        }
-      else
-        {
-          gtk_text_iter_forward_cursor_positions (&end, count);
-        }
-      break;
-
-    case GTK_DELETE_WORD_ENDS:
-      if (count < 0)
-        {
-          if (! gtk_text_iter_starts_word (&cursor))
-            gtk_text_iter_backward_visible_word_starts (&cursor, 1);
-        }
-      else if (count > 0)
-        {
-          if (! gtk_text_iter_ends_word (&end) &&
-              ! gtk_text_iter_forward_visible_word_ends (&end, 1))
-            gtk_text_iter_forward_to_line_end (&end);
-        }
-      break;
-
-    case GTK_DELETE_WORDS:
-      if (! gtk_text_iter_starts_word (&cursor))
-        gtk_text_iter_backward_visible_word_starts (&cursor, 1);
-
-      if (! gtk_text_iter_ends_word (&end) &&
-          ! gtk_text_iter_forward_visible_word_ends (&end, 1))
-        gtk_text_iter_forward_to_line_end (&end);
-      break;
-
-    case GTK_DELETE_DISPLAY_LINES:
-      break;
-
-    case GTK_DELETE_DISPLAY_LINE_ENDS:
-      break;
-
-    case GTK_DELETE_PARAGRAPH_ENDS:
-      if (count < 0)
-        {
-          gtk_text_iter_set_line_offset (&cursor, 0);
-        }
-      else if (count > 0)
-        {
-          if (! gtk_text_iter_ends_line (&end))
-            gtk_text_iter_forward_to_line_end (&end);
-          else
-            gtk_text_iter_forward_cursor_positions (&end, 1);
-        }
-      break;
-
-    case GTK_DELETE_PARAGRAPHS:
-      break;
-
-    case GTK_DELETE_WHITESPACE:
-      find_whitepace_region (&cursor, &cursor, &end);
-      break;
-    }
-
-  if (! gtk_text_iter_equal (&cursor, &end))
-    {
-      gtk_text_buffer_delete_interactive (buffer, &cursor, &end, TRUE);
-    }
-}
-
-static void
-gimp_text_tool_backspace (GimpTextTool *text_tool)
-{
-  GtkTextBuffer *buffer = text_tool->text_buffer;
-
-  if (gtk_text_buffer_get_has_selection (buffer))
-    {
-      gtk_text_buffer_delete_selection (buffer, TRUE, TRUE);
-    }
-  else
-    {
-      GtkTextIter cursor;
-
-      gtk_text_buffer_get_iter_at_mark (buffer, &cursor,
-                                        gtk_text_buffer_get_insert (buffer));
-
-      gtk_text_buffer_backspace (buffer, &cursor, TRUE, TRUE);
-    }
-}
-
-static void
-gimp_text_tool_toggle_overwrite (GimpTextTool *text_tool)
-{
-  gimp_draw_tool_pause (GIMP_DRAW_TOOL (text_tool));
-
-  text_tool->overwrite_mode = ! text_tool->overwrite_mode;
-
-  gimp_draw_tool_resume (GIMP_DRAW_TOOL (text_tool));
-}
-
-static void
-gimp_text_tool_select_all (GimpTextTool *text_tool,
-                           gboolean      select)
-{
-  GtkTextBuffer *buffer = text_tool->text_buffer;
-
-  gimp_draw_tool_pause (GIMP_DRAW_TOOL (text_tool));
-
-  if (select)
-    {
-      GtkTextIter start, end;
-
-      gtk_text_buffer_get_bounds (buffer, &start, &end);
-      gtk_text_buffer_select_range (buffer, &start, &end);
-    }
-  else
-    {
-      GtkTextIter cursor;
-
-      gtk_text_buffer_get_iter_at_mark (buffer, &cursor,
-					gtk_text_buffer_get_insert (buffer));
-      gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &cursor);
-    }
-
-  gimp_draw_tool_resume (GIMP_DRAW_TOOL (text_tool));
+  gimp_text_tool_editor_halt (text_tool);
 }
 
 
@@ -1883,28 +1305,6 @@ gimp_text_tool_connect (GimpTextTool  *text_tool,
 }
 
 static void
-gimp_text_tool_options_notify (GimpTextOptions *options,
-                               GParamSpec      *pspec,
-                               GimpTextTool    *text_tool)
-{
-  const gchar *param_name = g_param_spec_get_name (pspec);
-
-  if (! strcmp (param_name, "use-editor"))
-    {
-      if (options->use_editor)
-        {
-          if (text_tool->text)
-            gimp_text_tool_editor_dialog (text_tool);
-        }
-      else
-        {
-          if (text_tool->editor)
-            gtk_widget_destroy (text_tool->editor);
-        }
-    }
-}
-
-static void
 gimp_text_tool_layer_notify (GimpTextLayer *layer,
                              GParamSpec    *pspec,
                              GimpTextTool  *text_tool)
@@ -2231,103 +1631,6 @@ gimp_text_tool_create_layer (GimpTextTool *text_tool,
   gimp_text_tool_set_drawable (text_tool, GIMP_DRAWABLE (layer), FALSE);
 }
 
-static void
-gimp_text_tool_editor_dialog (GimpTextTool *text_tool)
-{
-  GimpTool          *tool    = GIMP_TOOL (text_tool);
-  GimpTextOptions   *options = GIMP_TEXT_TOOL_GET_OPTIONS (text_tool);
-  GimpDialogFactory *dialog_factory;
-  GtkWindow         *parent  = NULL;
-
-  if (text_tool->editor)
-    {
-      gtk_window_present (GTK_WINDOW (text_tool->editor));
-      return;
-    }
-
-  dialog_factory = gimp_dialog_factory_from_name ("toplevel");
-
-  if (tool->display)
-    {
-      GimpDisplayShell *shell = gimp_display_get_shell (tool->display);
-
-      parent = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (shell)));
-    }
-
-  text_tool->editor = gimp_text_options_editor_new (parent, options,
-                                                    gimp_dialog_factory_get_menu_factory (dialog_factory),
-                                                    _("GIMP Text Editor"),
-                                                    text_tool->text_buffer);
-
-  g_object_add_weak_pointer (G_OBJECT (text_tool->editor),
-                             (gpointer) &text_tool->editor);
-
-  gimp_dialog_factory_add_foreign (dialog_factory,
-                                   "gimp-text-tool-dialog",
-                                   text_tool->editor);
-
-  gtk_widget_show (text_tool->editor);
-}
-
-static void
-gimp_text_tool_editor (GimpTextTool *text_tool)
-{
-  GimpTool         *tool    = GIMP_TOOL (text_tool);
-  GimpTextOptions  *options = GIMP_TEXT_TOOL_GET_OPTIONS (text_tool);
-  GimpDisplayShell *shell   = gimp_display_get_shell (tool->display);
-
-  gtk_im_context_set_client_window (text_tool->im_context,
-                                    gtk_widget_get_window (shell->canvas));
-
-  gtk_im_context_focus_in (text_tool->im_context);
-
-  if (text_tool->text)
-    gtk_text_buffer_set_text (text_tool->text_buffer,
-                              text_tool->text->text, -1);
-  else
-    gtk_text_buffer_set_text (text_tool->text_buffer, "", -1);
-
-  gimp_text_tool_update_layout (text_tool);
-
-  if (options->use_editor)
-    gimp_text_tool_editor_dialog (text_tool);
-}
-
-static gchar *
-gimp_text_tool_editor_get_text (GimpTextTool *text_tool)
-{
-  GtkTextBuffer *buffer = text_tool->text_buffer;
-  GtkTextIter    start, end;
-  GtkTextIter    selstart, selend;
-  gchar         *string;
-  gchar         *fb;
-  gchar         *lb;
-
-  gtk_text_buffer_get_bounds (buffer, &start, &end);
-  gtk_text_buffer_get_selection_bounds (buffer, &selstart, &selend);
-
-  fb = gtk_text_buffer_get_text (buffer, &start, &selstart, TRUE);
-  lb = gtk_text_buffer_get_text (buffer, &selstart, &end, TRUE);
-
-  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);
-    }
-
-  g_free (fb);
-  g_free (lb);
-
-  return string;
-}
-
-
 #define  RESPONSE_NEW 1
 
 static void
@@ -2354,7 +1657,7 @@ gimp_text_tool_confirm_response (GtkWidget    *widget,
           if (text_tool->proxy)
             g_object_notify (G_OBJECT (text_tool->proxy), "text");
 
-          gimp_text_tool_editor (text_tool);
+          gimp_text_tool_editor_start (text_tool);
           break;
 
         default:
@@ -2533,7 +1836,7 @@ gimp_text_tool_set_drawable (GimpTextTool *text_tool,
   return FALSE;
 }
 
-static void
+void
 gimp_text_tool_update_proxy (GimpTextTool *text_tool)
 {
   if (text_tool->text)
@@ -2553,31 +1856,6 @@ gimp_text_tool_update_proxy (GimpTextTool *text_tool)
 }
 
 static void
-gimp_text_tool_enter_text (GimpTextTool *text_tool,
-                           const gchar  *str)
-{
-  GtkTextBuffer *buffer = text_tool->text_buffer;
-  gboolean       had_selection;
-
-  had_selection = gtk_text_buffer_get_has_selection (buffer);
-
-  gimp_text_tool_delete_selection (text_tool);
-
-  if (! had_selection && text_tool->overwrite_mode && strcmp (str, "\n"))
-    {
-      GtkTextIter cursor;
-
-      gtk_text_buffer_get_iter_at_mark (buffer, &cursor,
-                                        gtk_text_buffer_get_insert (buffer));
-
-      if (! gtk_text_iter_ends_line (&cursor))
-        gimp_text_tool_delete_from_cursor (text_tool, GTK_DELETE_CHARS, 1);
-    }
-
-  gtk_text_buffer_insert_at_cursor (buffer, str, -1);
-}
-
-static void
 gimp_text_tool_text_buffer_changed (GtkTextBuffer *text_buffer,
                                     GimpTextTool  *text_tool)
 {
@@ -2593,7 +1871,7 @@ gimp_text_tool_text_buffer_mark_set (GtkTextBuffer *text_buffer,
   gimp_text_tool_update_layout (text_tool);
 }
 
-static void
+void
 gimp_text_tool_update_layout (GimpTextTool *text_tool)
 {
   GimpImage *image;
@@ -2654,43 +1932,6 @@ gimp_text_tool_xy_to_offset (GimpTextTool *text_tool,
 }
 
 
-/*  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  */
 
 void
@@ -2748,7 +1989,7 @@ gimp_text_tool_set_layer (GimpTextTool *text_tool,
 
           gimp_text_tool_frame_item (text_tool);
 
-          gimp_text_tool_editor (text_tool);
+          gimp_text_tool_editor_start (text_tool);
         }
     }
 }
diff --git a/app/tools/gimptexttool.h b/app/tools/gimptexttool.h
index 3780d7e..da56cbf 100644
--- a/app/tools/gimptexttool.h
+++ b/app/tools/gimptexttool.h
@@ -104,4 +104,9 @@ void       gimp_text_tool_paste_clipboard        (GimpTextTool *text_tool);
 void       gimp_text_tool_create_vectors         (GimpTextTool *text_tool);
 void       gimp_text_tool_create_vectors_warped  (GimpTextTool *text_tool);
 
+/*  only for the text editor  */
+void       gimp_text_tool_update_layout          (GimpTextTool *text_tool);
+void       gimp_text_tool_update_proxy           (GimpTextTool *text_tool);
+
+
 #endif /* __GIMP_TEXT_TOOL_H__ */



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