[gimp] Make the text tool use GtkTextView's key bindings



commit 64d6ebca00f832ed5e9bf2d43f81ec929fc2e26c
Author: Michael Natterer <mitch gimp org>
Date:   Mon Jun 22 23:47:04 2009 +0200

    Make the text tool use GtkTextView's key bindings
    
    * app/core/gimpmarshal.list: add marshallers needed for the binding
      singnals.
    
    * app/tools/gimptexttool.[ch]: add binding signals "move-cursor",
      "delete-from-cursor" and "backspace" and hijack GtkTextView's
      binding set to invoke them. Move code from the key_press() handler
      to the signals' default handlers. This is how it should work. In
      fact that code is #ifdef'ed away and we need an evil proxy
      GtkTextView to invoke the bindings on because of reasons stated in
      comments in the code. Ugly but works just fine.

 app/core/gimpmarshal.list |    2 +
 app/tools/gimptexttool.c  |  512 ++++++++++++++++++++++++++++++---------------
 app/tools/gimptexttool.h  |   20 ++
 3 files changed, 364 insertions(+), 170 deletions(-)
---
diff --git a/app/core/gimpmarshal.list b/app/core/gimpmarshal.list
index 0671bd7..7f97ddb 100644
--- a/app/core/gimpmarshal.list
+++ b/app/core/gimpmarshal.list
@@ -37,6 +37,8 @@ VOID: DOUBLE, DOUBLE
 VOID: DOUBLE, DOUBLE, DOUBLE, DOUBLE
 VOID: ENUM
 VOID: ENUM, ENUM, BOXED, INT
+VOID: ENUM, INT
+VOID: ENUM, INT, BOOLEAN
 VOID: ENUM, OBJECT
 VOID: ENUM, POINTER
 VOID: FLAGS
diff --git a/app/tools/gimptexttool.c b/app/tools/gimptexttool.c
index 885ea87..1479a76 100644
--- a/app/tools/gimptexttool.c
+++ b/app/tools/gimptexttool.c
@@ -35,6 +35,7 @@
 #include "core/gimpimage-undo.h"
 #include "core/gimpimage-undo-push.h"
 #include "core/gimplayer-floating-sel.h"
+#include "core/gimpmarshal.h"
 #include "core/gimptoolinfo.h"
 #include "core/gimpundostack.h"
 
@@ -69,6 +70,17 @@
 #define TEXT_UNDO_TIMEOUT 3
 
 
+#ifndef TEXT_TOOL_HACK
+enum
+{
+  MOVE_CURSOR,
+  DELETE_FROM_CURSOR,
+  BACKSPACE,
+  LAST_SIGNAL
+};
+#endif
+
+
 /*  local function prototypes  */
 
 static void gimp_text_tool_rectangle_tool_iface_init (GimpRectangleToolInterface *iface);
@@ -131,6 +143,27 @@ 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_move_cursor     (GimpTextTool      *text_tool,
+                                                 GtkMovementStep    step,
+                                                 gint               count,
+                                                 gboolean           extend_selection
+#ifdef TEXT_TOOL_HACK
+                                                 , GtkTextView     *proxy
+#endif
+                                                 );
+static void   gimp_text_tool_delete_from_cursor (GimpTextTool      *text_tool,
+                                                 GtkDeleteType      type,
+                                                 gint               count
+#ifdef TEXT_TOOL_HACK
+                                                 , GtkTextView     *proxy
+#endif
+                                                 );
+static void      gimp_text_tool_backspace       (GimpTextTool      *text_tool
+#ifdef TEXT_TOOL_HACK
+                                                 , GtkTextView     *proxy
+#endif
+                                                 );
+
 static void      gimp_text_tool_connect         (GimpTextTool      *text_tool,
                                                  GimpTextLayer     *layer,
                                                  GimpText          *text);
@@ -197,6 +230,10 @@ G_DEFINE_TYPE_WITH_CODE (GimpTextTool, gimp_text_tool,
 
 #define parent_class gimp_text_tool_parent_class
 
+#ifndef TEXT_TOOL_HACK
+static guint signals[LAST_SIGNAL] = { 0, };
+#endif
+
 
 void
 gimp_text_tool_register (GimpToolRegisterCallback  callback,
@@ -242,7 +279,47 @@ gimp_text_tool_class_init (GimpTextToolClass *klass)
 
   draw_tool_class->draw        = gimp_text_tool_draw;
 
+#ifndef TEXT_TOOL_HACK
+  klass->move_cursor           = gimp_text_tool_move_cursor;
+  klass->delete_from_cursor    = gimp_text_tool_delete_from_cursor;
+  klass->backspace             = gimp_text_tool_backspace;
+#endif
+
   gimp_rectangle_tool_install_properties (object_class);
+
+#ifndef TEXT_TOOL_HACK
+  signals[MOVE_CURSOR] =
+    g_signal_new ("move-cursor",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                  G_STRUCT_OFFSET (GimpTextToolClass, move_cursor),
+                  NULL, NULL,
+                  gimp_marshal_VOID__ENUM_INT_BOOLEAN,
+		  G_TYPE_NONE, 3,
+                  GTK_TYPE_MOVEMENT_STEP,
+                  G_TYPE_INT,
+                  G_TYPE_BOOLEAN);
+
+  signals[DELETE_FROM_CURSOR] =
+    g_signal_new ("delete-from-cursor",
+		  G_OBJECT_CLASS_TYPE (object_class),
+		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		  G_STRUCT_OFFSET (GimpTextToolClass, delete_from_cursor),
+		  NULL, NULL,
+		  gimp_marshal_VOID__ENUM_INT,
+		  G_TYPE_NONE, 2,
+		  GTK_TYPE_DELETE_TYPE,
+		  G_TYPE_INT);
+
+  signals[BACKSPACE] =
+    g_signal_new ("backspace",
+		  G_OBJECT_CLASS_TYPE (object_class),
+		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		  G_STRUCT_OFFSET (GimpTextToolClass, backspace),
+		  NULL, NULL,
+		  gimp_marshal_VOID__VOID,
+		  G_TYPE_NONE, 0);
+#endif
 }
 
 static void
@@ -302,6 +379,27 @@ gimp_text_tool_init (GimpTextTool *text_tool)
   text_tool->handle_rectangle_change_complete = TRUE;
 
   text_tool->x_pos = -1;
+
+#ifdef TEXT_TOOL_HACK
+  /*  we need this crap because we need some object to call
+   *  gtk_binding_set_activate() activate with. It takes a GtkObject
+   *  instead of a GObject. So instead of adding the needed binding
+   *  signals to GimpTextTool, we abuse a GtkTextView, which has all
+   *  the needed signals anyway, and connect to its signals. Puke!
+   */
+  text_tool->proxy_text_view = gtk_text_view_new ();
+  g_object_ref_sink (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, "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);
+#endif
 }
 
 static GObject *
@@ -349,6 +447,14 @@ gimp_text_tool_dispose (GObject *object)
   if (text_tool->editor)
     gtk_widget_destroy (text_tool->editor);
 
+#ifdef TEXT_TOOL_HACK
+  if (text_tool->proxy_text_view)
+    {
+      g_object_unref (text_tool->proxy_text_view);
+      text_tool->proxy_text_view = NULL;
+    }
+#endif
+
   G_OBJECT_CLASS (parent_class)->dispose (object);
 }
 
@@ -785,7 +891,10 @@ gimp_text_tool_key_press (GimpTool    *tool,
                           GdkEventKey *kevent,
                           GimpDisplay *display)
 {
-  GimpTextTool *text_tool = GIMP_TEXT_TOOL (tool);
+  GimpTextTool     *text_tool = GIMP_TEXT_TOOL (tool);
+  GtkTextViewClass *tv_class = g_type_class_ref (GTK_TYPE_TEXT_VIEW);
+  GtkBindingSet    *binding  = gtk_binding_set_by_class (tv_class);
+
   GtkTextMark  *cursor_mark;
   GtkTextMark  *selection_mark;
   GtkTextIter   cursor;
@@ -805,6 +914,20 @@ gimp_text_tool_key_press (GimpTool    *tool,
       return TRUE;
     }
 
+  if (gtk_binding_set_activate (binding,
+                                kevent->keyval,
+                                kevent->state,
+#ifdef TEXT_TOOL_HACK
+                                GTK_OBJECT (text_tool->proxy_text_view)
+#else
+                                (GtkObject *) text_tool
+#endif
+                                ))
+    {
+      g_printerr ("binding handled event!\n");
+      return TRUE;
+    }
+
   cursor_mark    = gtk_text_buffer_get_insert (text_tool->text_buffer);
   selection_mark = gtk_text_buffer_get_selection_bound (text_tool->text_buffer);
 
@@ -838,175 +961,6 @@ gimp_text_tool_key_press (GimpTool    *tool,
       gimp_text_tool_update_layout (text_tool);
       break;
 
-    case GDK_BackSpace:
-      gimp_text_tool_delete_text (text_tool, TRUE);
-      break;
-
-    case GDK_Delete:
-      gimp_text_tool_delete_text (text_tool, FALSE);
-      break;
-
-    case GDK_Left:
-    case GDK_KP_Left:
-      if (kevent->state & GDK_CONTROL_MASK)
-        {
-          gtk_text_iter_backward_visible_word_starts (&cursor, 1);
-        }
-      else
-        {
-          gtk_text_iter_backward_cursor_position (&cursor);
-        }
-      gtk_text_buffer_select_range (text_tool->text_buffer,
-                                    &cursor, sel_start);
-      break;
-
-    case GDK_Right:
-    case GDK_KP_Right:
-      if (kevent->state & GDK_CONTROL_MASK)
-        {
-	  if (! gtk_text_iter_forward_visible_word_ends (&cursor, 1))
-	    gtk_text_iter_forward_to_line_end (&cursor);
-        }
-      else
-        {
-          gtk_text_iter_forward_cursor_position (&cursor);
-        }
-      gtk_text_buffer_select_range (text_tool->text_buffer,
-                                    &cursor, sel_start);
-      gimp_text_tool_reset_im_context (text_tool);
-      break;
-
-    case GDK_Up:
-    case GDK_KP_Up:
-    case GDK_Down:
-    case GDK_KP_Down:
-      {
-        PangoLayout     *layout;
-        PangoLayoutLine *layout_line;
-        PangoLayoutIter *layout_iter;
-        PangoRectangle   logical;
-        gint             line;
-        gint             line_index;
-        gint             trailing;
-        gint             i;
-
-        layout = gimp_text_layout_get_pango_layout (text_tool->layout);
-
-        line       = gtk_text_iter_get_line (&cursor);
-        line_index = gtk_text_iter_get_line_index (&cursor);
-
-        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_line_index_to_x (layout_line,
-                                      layout_line->start_index + line_index,
-                                      FALSE, &x_pos);
-
-        pango_layout_iter_get_line_extents (layout_iter, NULL, &logical);
-        x_pos += logical.x;
-
-        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 && (line_index == layout_line->length ||
-                                       line_index == 0))
-          x_pos = text_tool->x_pos;
-
-        if (kevent->keyval == GDK_Up ||
-            kevent->keyval == GDK_KP_Up)
-          {
-            line--;
-            if (line < 0)
-              {
-                gtk_text_iter_set_line_offset (&cursor, 0);
-                gtk_text_buffer_select_range (text_tool->text_buffer,
-                                              &cursor, sel_start);
-                break;
-              }
-          }
-        else
-          {
-            line++;
-          }
-
-        layout_line = pango_layout_get_line_readonly (layout, line);
-
-        if (! layout_line)
-          {
-            if (kevent->keyval == GDK_Up ||
-                kevent->keyval == GDK_KP_Up)
-              {
-                gtk_text_iter_set_line_offset (&cursor, 0);
-                gtk_text_buffer_select_range (text_tool->text_buffer,
-                                              &cursor, sel_start);
-              }
-            else
-              {
-                gtk_text_iter_forward_to_line_end (&cursor);
-                gtk_text_buffer_select_range (text_tool->text_buffer,
-                                              &cursor, sel_start);
-              }
-            break;
-          }
-
-        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);
-
-        pango_layout_line_x_to_index (layout_line, x_pos - logical.x,
-                                      &line_index, &trailing);
-
-        line_index -= layout_line->start_index;
-
-        gtk_text_buffer_get_iter_at_line_index (text_tool->text_buffer,
-                                                &cursor,
-                                                line, line_index);
-
-        while (trailing--)
-          gtk_text_iter_forward_char (&cursor);
-
-        gtk_text_buffer_select_range (text_tool->text_buffer,
-                                      &cursor, sel_start);
-      }
-      break;
-
-    case GDK_Page_Up:
-    case GDK_KP_Page_Up:
-      gtk_text_buffer_get_start_iter (text_tool->text_buffer, &cursor);
-      gtk_text_buffer_select_range (text_tool->text_buffer,
-                                    &cursor, sel_start);
-      break;
-
-    case GDK_Page_Down:
-    case GDK_KP_Page_Down:
-      gtk_text_buffer_get_end_iter (text_tool->text_buffer, &cursor);
-      gtk_text_buffer_select_range (text_tool->text_buffer,
-                                    &cursor, sel_start);
-      break;
-
-    case GDK_Home:
-    case GDK_KP_Home:
-      gtk_text_iter_set_line_offset (&cursor, 0);
-      gtk_text_buffer_select_range (text_tool->text_buffer,
-                                    &cursor, sel_start);
-      break;
-
-    case GDK_End:
-    case GDK_KP_End:
-      gtk_text_iter_forward_to_line_end (&cursor);
-      gtk_text_buffer_select_range (text_tool->text_buffer,
-                                    &cursor, sel_start);
-      break;
-
     default:
       retval = FALSE;
     }
@@ -1468,6 +1422,224 @@ gimp_text_tool_draw_selection (GimpDrawTool *draw_tool,
   pango_layout_iter_free (line_iter);
 }
 
+static void
+gimp_text_tool_move_cursor (GimpTextTool    *text_tool,
+                            GtkMovementStep  step,
+                            gint             count,
+                            gboolean         extend_selection
+#ifdef TEXT_TOOL_HACK
+                            , GtkTextView     *proxy
+#endif
+                            )
+{
+  GtkTextMark  *cursor_mark;
+  GtkTextMark  *selection_mark;
+  GtkTextIter   cursor;
+  GtkTextIter   selection;
+  GtkTextIter  *sel_start;
+  gint          x_pos  = -1;
+
+  cursor_mark    = gtk_text_buffer_get_insert (text_tool->text_buffer);
+  selection_mark = gtk_text_buffer_get_selection_bound (text_tool->text_buffer);
+
+  gtk_text_buffer_get_iter_at_mark (text_tool->text_buffer,
+                                    &cursor, cursor_mark);
+  gtk_text_buffer_get_iter_at_mark (text_tool->text_buffer,
+                                    &selection, selection_mark);
+
+  if (extend_selection)
+    sel_start = &selection;
+  else
+    sel_start = &cursor;
+
+  switch (step)
+    {
+    case GTK_MOVEMENT_LOGICAL_POSITIONS:
+      gtk_text_iter_forward_visible_cursor_positions (&cursor, count);
+      break;
+
+    case GTK_MOVEMENT_VISUAL_POSITIONS:
+      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:
+      {
+        PangoLayout     *layout;
+        PangoLayoutLine *layout_line;
+        PangoLayoutIter *layout_iter;
+        PangoRectangle   logical;
+        gint             line;
+        gint             line_index;
+        gint             trailing;
+        gint             i;
+
+        layout = gimp_text_layout_get_pango_layout (text_tool->layout);
+
+        line       = gtk_text_iter_get_line (&cursor);
+        line_index = gtk_text_iter_get_line_index (&cursor);
+
+        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_line_index_to_x (layout_line,
+                                      layout_line->start_index + line_index,
+                                      FALSE, &x_pos);
+
+        pango_layout_iter_get_line_extents (layout_iter, NULL, &logical);
+        x_pos += logical.x;
+
+        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 && (line_index == layout_line->length ||
+                                       line_index == 0))
+          x_pos = text_tool->x_pos;
+
+        if (count < 0)
+          {
+            line--;
+            if (line < 0)
+              {
+                gtk_text_iter_set_line_offset (&cursor, 0);
+                break;
+              }
+          }
+        else if (count > 0)
+          {
+            line++;
+          }
+
+        layout_line = pango_layout_get_line_readonly (layout, line);
+
+        if (! layout_line)
+          {
+            if (count < 0)
+              {
+                gtk_text_iter_set_line_offset (&cursor, 0);
+              }
+            else if (count > 0)
+              {
+                gtk_text_iter_forward_to_line_end (&cursor);
+              }
+            break;
+          }
+
+        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);
+
+        pango_layout_line_x_to_index (layout_line, x_pos - logical.x,
+                                      &line_index, &trailing);
+
+        line_index -= layout_line->start_index;
+
+        gtk_text_buffer_get_iter_at_line_index (text_tool->text_buffer,
+                                                &cursor,
+                                                line, line_index);
+
+        while (trailing--)
+          gtk_text_iter_forward_char (&cursor);
+      }
+      break;
+
+    case GTK_MOVEMENT_PAGES:
+      if (count < 0)
+        {
+          gtk_text_buffer_get_start_iter (text_tool->text_buffer, &cursor);
+        }
+      else if (count > 0)
+        {
+          gtk_text_buffer_get_end_iter (text_tool->text_buffer, &cursor);
+        }
+      break;
+
+    case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
+      if (count < 0)
+        {
+          gtk_text_iter_set_line_offset (&cursor, 0);
+        }
+      else if (count > 0)
+        {
+          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 (text_tool->text_buffer,
+                                &cursor, sel_start);
+
+  gimp_draw_tool_resume (GIMP_DRAW_TOOL (text_tool));
+
+#ifdef TEXT_TOOL_HACK
+  g_signal_stop_emission_by_name (proxy, "move-cursor");
+#endif
+}
+
+static void
+gimp_text_tool_delete_from_cursor (GimpTextTool  *text_tool,
+                                   GtkDeleteType  type,
+                                   gint           count
+#ifdef TEXT_TOOL_HACK
+                                   , GtkTextView *proxy
+#endif
+                            )
+{
+  gimp_text_tool_delete_text (text_tool, FALSE);
+
+#ifdef TEXT_TOOL_HACK
+  g_signal_stop_emission_by_name (proxy, "delete-from-cursor");
+#endif
+}
+
+static void
+gimp_text_tool_backspace (GimpTextTool  *text_tool
+#ifdef TEXT_TOOL_HACK
+                          , GtkTextView *proxy
+#endif
+                          )
+{
+  gimp_text_tool_delete_text (text_tool, TRUE);
+
+#ifdef TEXT_TOOL_HACK
+  g_signal_stop_emission_by_name (proxy, "backspace");
+#endif
+}
+
 static gboolean
 gimp_text_tool_rectangle_change_complete (GimpRectangleTool *rect_tool)
 {
diff --git a/app/tools/gimptexttool.h b/app/tools/gimptexttool.h
index fe11781..1e443ce 100644
--- a/app/tools/gimptexttool.h
+++ b/app/tools/gimptexttool.h
@@ -19,6 +19,9 @@
 #define __GIMP_TEXT_TOOL_H__
 
 
+//#define TEXT_TOOL_HACK 1
+
+
 #include "gimpdrawtool.h"
 
 
@@ -72,11 +75,28 @@ struct _GimpTextTool
   gint            x_pos;
 
   GimpTextLayout *layout;
+
+#ifdef TEXT_TOOL_HACK
+  GtkWidget      *proxy_text_view; /* this sucks so much */
+#endif
 };
 
 struct _GimpTextToolClass
 {
   GimpDrawToolClass parent_class;
+
+#ifndef TEXT_TOOL_HACK
+  void (* move_cursor)        (GimpTextTool    *text_tool,
+                               GtkMovementStep  step,
+                               gint             count,
+                               gboolean         extend_selection);
+
+  void (* delete_from_cursor) (GimpTextTool    *text_tool,
+                               GtkDeleteType    type,
+                               gint             count);
+
+  void (* backspace)          (GimpTextTool    *text_tool);
+#endif
 };
 
 



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