[gimp] app: add persistent handle selection to GimpToolLine



commit d4cb1623ea1b4b587644dae943dc15a7d6c0fd9f
Author: Ell <ell_se yahoo com>
Date:   Wed Jul 19 14:48:09 2017 -0400

    app: add persistent handle selection to GimpToolLine
    
    En route to on-canvas gradient editing, add support for persistent
    handle selection to GimpToolLine (a handle being either an endpoint
    or a slider).  Handles are selected through clicking, however,
    unlike before, the selection persists after the mouse is released.
    A new "selection" property specifies the currently-selected handle
    (who knows, maybe in the future we'll add multi-selection), and a
    new "selection-changed" signal is emitted when the selection changes.
    
    The visual feedback has been changed to better suit the new behavior,
    and the behaviors yet to be added:  The selected handle is marked
    using highlighting; the highlighting doesn't change while hovering
    over other handles.  Only the hit-test circle is used as hover
    indication, however, we use a fixed-size circle, and only show the
    circle for the currently hovered-over handle -- no more trippy
    expanding circles :)
    
    A few minor changes along the way:
    
      - The selected handle is now the (first) one that's closest to the
        cursor, instead of the first one to pass hit-testing.
    
      - We don't move the selectd handle upon button-press, only upon
        motion, so that handles can be selected without moving them.
    
      - Show a MOVE cursor modifier when hovering over a handle.

 app/display/gimptoolline.c |  582 ++++++++++++++++++++++++++------------------
 app/display/gimptoolline.h |   40 +++-
 2 files changed, 370 insertions(+), 252 deletions(-)
---
diff --git a/app/display/gimptoolline.c b/app/display/gimptoolline.c
index 6f9ddaf..d37fd4e 100644
--- a/app/display/gimptoolline.c
+++ b/app/display/gimptoolline.c
@@ -35,6 +35,7 @@
 
 #include "widgets/gimpwidgets-utils.h"
 
+#include "gimpcanvasgroup.h"
 #include "gimpcanvashandle.h"
 #include "gimpcanvasline.h"
 #include "gimpdisplayshell.h"
@@ -43,22 +44,22 @@
 #include "gimp-intl.h"
 
 
-#define SHOW_LINE TRUE
-#define ENDPOINT_GRIP_HANDLE_TYPE GIMP_HANDLE_CROSS
-#define ENDPOINT_GRIP_HANDLE_SIZE GIMP_CANVAS_HANDLE_SIZE_CROSS
-#define SLIDER_GRIP_HANDLE_TYPE   GIMP_HANDLE_FILLED_DIAMOND
-#define SLIDER_GRIP_HANDLE_SIZE   (ENDPOINT_GRIP_HANDLE_SIZE * 2 / 3)
+#define SHOW_LINE            TRUE
+#define GRAB_LINE_MASK       GDK_MOD1_MASK
+#define ENDPOINT_HANDLE_TYPE GIMP_HANDLE_CROSS
+#define ENDPOINT_HANDLE_SIZE GIMP_CANVAS_HANDLE_SIZE_CROSS
+#define SLIDER_HANDLE_TYPE   GIMP_HANDLE_FILLED_DIAMOND
+#define SLIDER_HANDLE_SIZE   (ENDPOINT_HANDLE_SIZE * 2 / 3)
+#define HANDLE_CIRCLE_SCALE  1.8
+#define LINE_VICINITY        ((gint) (SLIDER_HANDLE_SIZE * HANDLE_CIRCLE_SCALE) / 2)
 
 
 typedef enum
 {
-  /* POINT_NONE evaluates to FALSE */
-  POINT_NONE = 0,
-  POINT_START,
-  POINT_END,
-  POINT_BOTH,
-  POINT_SLIDER
-} GimpToolLinePoint;
+  GRAB_NONE,
+  GRAB_SELECTION,
+  GRAB_LINE
+} GimpToolLineGrab;
 
 enum
 {
@@ -68,9 +69,16 @@ enum
   PROP_X2,
   PROP_Y2,
   PROP_SLIDERS,
+  PROP_SELECTION,
   PROP_STATUS_TITLE,
 };
 
+enum
+{
+  SELECTION_CHANGED,
+  LAST_SIGNAL
+};
+
 struct _GimpToolLinePrivate
 {
   gdouble            x1;
@@ -78,6 +86,7 @@ struct _GimpToolLinePrivate
   gdouble            x2;
   gdouble            y2;
   GArray            *sliders;
+  gint               selection;
   gchar             *status_title;
 
   gdouble            saved_x1;
@@ -88,17 +97,15 @@ struct _GimpToolLinePrivate
 
   gdouble            mouse_x;
   gdouble            mouse_y;
-  GimpToolLinePoint  point;
-  gint               slider_index;
-  gboolean           point_grabbed;
+  gint               hover;
+  GimpToolLineGrab   grab;
 
   GimpCanvasItem    *line;
-  GimpCanvasItem    *start_handle_circle;
-  GimpCanvasItem    *start_handle_grip;
-  GimpCanvasItem    *end_handle_circle;
-  GimpCanvasItem    *end_handle_grip;
-  GArray            *slider_handle_circles;
-  GArray            *slider_handle_grips;
+  GimpCanvasItem    *start_handle;
+  GimpCanvasItem    *end_handle;
+  GimpCanvasItem    *slider_group;
+  GArray            *slider_handles;
+  GimpCanvasItem    *handle_circle;
 };
 
 
@@ -148,7 +155,12 @@ static gboolean gimp_tool_line_get_cursor      (GimpToolWidget        *widget,
 static GimpControllerSlider *
                 gimp_tool_line_get_slider      (GimpToolLine          *line,
                                                 gint                   slider);
-static gboolean gimp_tool_line_point_motion    (GimpToolLine          *line,
+static GimpCanvasItem *
+                gimp_tool_line_get_handle      (GimpToolLine          *line,
+                                                gint                   handle);
+
+static gboolean
+               gimp_tool_line_selection_motion (GimpToolLine          *line,
                                                 gboolean               constrain);
 
 static void     gimp_tool_line_update_handles  (GimpToolLine          *line);
@@ -157,11 +169,18 @@ static void     gimp_tool_line_update_status   (GimpToolLine          *line,
                                                 GdkModifierType        state,
                                                 gboolean               proximity);
 
+static gboolean gimp_tool_line_handle_hit      (GimpCanvasItem        *handle,
+                                                gdouble                x,
+                                                gdouble                y,
+                                                gdouble               *min_dist);
+
 
 G_DEFINE_TYPE (GimpToolLine, gimp_tool_line, GIMP_TYPE_TOOL_WIDGET)
 
 #define parent_class gimp_tool_line_parent_class
 
+static guint line_signals[LAST_SIGNAL] = { 0, };
+
 
 static void
 gimp_tool_line_class_init (GimpToolLineClass *klass)
@@ -182,6 +201,15 @@ gimp_tool_line_class_init (GimpToolLineClass *klass)
   widget_class->motion_modifier = gimp_tool_line_motion_modifier;
   widget_class->get_cursor      = gimp_tool_line_get_cursor;
 
+  line_signals[SELECTION_CHANGED] =
+    g_signal_new ("selection-changed",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_FIRST,
+                  G_STRUCT_OFFSET (GimpToolLineClass, selection_changed),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+
   g_object_class_install_property (object_class, PROP_X1,
                                    g_param_spec_double ("x1", NULL, NULL,
                                                         -GIMP_MAX_IMAGE_SIZE,
@@ -215,6 +243,14 @@ gimp_tool_line_class_init (GimpToolLineClass *klass)
                                                        G_TYPE_ARRAY,
                                                        GIMP_PARAM_READWRITE));
 
+  g_object_class_install_property (object_class, PROP_SELECTION,
+                                   g_param_spec_int ("selection", NULL, NULL,
+                                                     GIMP_TOOL_LINE_HANDLE_NONE,
+                                                     G_MAXINT,
+                                                     GIMP_TOOL_LINE_HANDLE_NONE,
+                                                     GIMP_PARAM_READWRITE |
+                                                     G_PARAM_CONSTRUCT));
+
   g_object_class_install_property (object_class, PROP_STATUS_TITLE,
                                    g_param_spec_string ("status-title",
                                                         NULL, NULL,
@@ -236,10 +272,12 @@ gimp_tool_line_init (GimpToolLine *line)
 
   private->sliders = g_array_new (FALSE, FALSE, sizeof (GimpControllerSlider));
 
-  private->slider_handle_circles = g_array_new (FALSE, TRUE,
-                                                sizeof (GimpCanvasItem *));
-  private->slider_handle_grips   = g_array_new (FALSE, TRUE,
-                                                sizeof (GimpCanvasItem *));
+  private->selection = GIMP_TOOL_LINE_HANDLE_NONE;
+  private->hover     = GIMP_TOOL_LINE_HANDLE_NONE;
+  private->grab      = GRAB_NONE;
+
+  private->slider_handles = g_array_new (FALSE, TRUE,
+                                         sizeof (GimpCanvasItem *));
 }
 
 static void
@@ -259,40 +297,36 @@ gimp_tool_line_constructed (GObject *object)
 
   gimp_canvas_item_set_visible (private->line, SHOW_LINE);
 
-  private->start_handle_circle =
-    gimp_tool_widget_add_handle (widget,
-                                 GIMP_HANDLE_CIRCLE,
-                                 private->x1,
-                                 private->y1,
-                                 2 * ENDPOINT_GRIP_HANDLE_SIZE,
-                                 2 * ENDPOINT_GRIP_HANDLE_SIZE,
-                                 GIMP_HANDLE_ANCHOR_CENTER);
-
-  private->start_handle_grip =
+  private->start_handle =
     gimp_tool_widget_add_handle (widget,
-                                 ENDPOINT_GRIP_HANDLE_TYPE,
+                                 ENDPOINT_HANDLE_TYPE,
                                  private->x1,
                                  private->y1,
-                                 ENDPOINT_GRIP_HANDLE_SIZE,
-                                 ENDPOINT_GRIP_HANDLE_SIZE,
+                                 ENDPOINT_HANDLE_SIZE,
+                                 ENDPOINT_HANDLE_SIZE,
                                  GIMP_HANDLE_ANCHOR_CENTER);
 
-  private->end_handle_circle =
+  private->end_handle =
     gimp_tool_widget_add_handle (widget,
-                                 GIMP_HANDLE_CIRCLE,
+                                 ENDPOINT_HANDLE_TYPE,
                                  private->x2,
                                  private->y2,
-                                 2 * ENDPOINT_GRIP_HANDLE_SIZE,
-                                 2 * ENDPOINT_GRIP_HANDLE_SIZE,
+                                 ENDPOINT_HANDLE_SIZE,
+                                 ENDPOINT_HANDLE_SIZE,
                                  GIMP_HANDLE_ANCHOR_CENTER);
 
-  private->end_handle_grip =
+  private->slider_group =
+    gimp_canvas_group_new (gimp_tool_widget_get_shell (widget));
+  gimp_tool_widget_add_item (widget, private->slider_group);
+  g_object_unref (private->slider_group);
+
+  private->handle_circle =
     gimp_tool_widget_add_handle (widget,
-                                 ENDPOINT_GRIP_HANDLE_TYPE,
-                                 private->x2,
-                                 private->y2,
-                                 ENDPOINT_GRIP_HANDLE_SIZE,
-                                 ENDPOINT_GRIP_HANDLE_SIZE,
+                                 GIMP_HANDLE_CIRCLE,
+                                 private->x1,
+                                 private->y1,
+                                 ENDPOINT_HANDLE_SIZE * HANDLE_CIRCLE_SCALE,
+                                 ENDPOINT_HANDLE_SIZE * HANDLE_CIRCLE_SCALE,
                                  GIMP_HANDLE_ANCHOR_CENTER);
 
   gimp_tool_line_changed (widget);
@@ -306,8 +340,7 @@ gimp_tool_line_finalize (GObject *object)
 
   g_clear_pointer (&private->sliders, g_array_unref);
   g_clear_pointer (&private->status_title, g_free);
-  g_clear_pointer (&private->slider_handle_circles, g_array_unref);
-  g_clear_pointer (&private->slider_handle_grips, g_array_unref);
+  g_clear_pointer (&private->slider_handles, g_array_unref);
 
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
@@ -337,10 +370,41 @@ gimp_tool_line_set_property (GObject      *object,
       break;
 
     case PROP_SLIDERS:
-      g_return_if_fail (g_value_get_boxed (value) != NULL);
+      {
+        GArray *sliders = g_value_dup_boxed (value);
+
+        g_return_if_fail (sliders != NULL);
+
+        if (GIMP_TOOL_LINE_HANDLE_IS_SLIDER (private->selection) &&
+            sliders->len != private->sliders->len)
+          {
+            gimp_tool_line_set_selection (line, GIMP_TOOL_LINE_HANDLE_NONE);
+          }
+
+        if (GIMP_TOOL_LINE_HANDLE_IS_SLIDER (private->hover))
+          private->hover = GIMP_TOOL_LINE_HANDLE_NONE;
 
-      g_array_unref (private->sliders);
-      private->sliders = g_value_dup_boxed (value);
+        g_array_unref (private->sliders);
+        private->sliders = sliders;
+      }
+      break;
+
+    case PROP_SELECTION:
+      {
+        gint selection = g_value_get_int (value);
+
+        g_return_if_fail (selection < (gint) private->sliders->len);
+
+        if (selection != private->selection)
+          {
+            private->selection = selection;
+
+            if (private->grab == GRAB_SELECTION)
+              private->grab = GRAB_NONE;
+
+            g_signal_emit (line, line_signals[SELECTION_CHANGED], 0);
+          }
+      }
       break;
 
     case PROP_STATUS_TITLE:
@@ -384,6 +448,10 @@ gimp_tool_line_get_property (GObject    *object,
       g_value_set_boxed (value, private->sliders);
       break;
 
+    case PROP_SELECTION:
+      g_value_set_int (value, private->selection);
+      break;
+
     case PROP_STATUS_TITLE:
       g_value_set_string (value, private->status_title);
       break;
@@ -407,73 +475,54 @@ gimp_tool_line_changed (GimpToolWidget *widget)
                         private->x2,
                         private->y2);
 
-  gimp_canvas_handle_set_position (private->start_handle_circle,
-                                   private->x1,
-                                   private->y1);
-  gimp_canvas_handle_set_position (private->start_handle_grip,
+  gimp_canvas_handle_set_position (private->start_handle,
                                    private->x1,
                                    private->y1);
 
-  gimp_canvas_handle_set_position (private->end_handle_circle,
-                                   private->x2,
-                                   private->y2);
-  gimp_canvas_handle_set_position (private->end_handle_grip,
+  gimp_canvas_handle_set_position (private->end_handle,
                                    private->x2,
                                    private->y2);
 
   /* remove excessive slider handles */
-  for (i = private->sliders->len; i < private->slider_handle_circles->len; i++)
+  for (i = private->sliders->len; i < private->slider_handles->len; i++)
     {
-      gimp_tool_widget_remove_item (widget,
-                                    g_array_index (private->slider_handle_circles,
-                                                   GimpCanvasItem *, i));
-      gimp_tool_widget_remove_item (widget,
-                                    g_array_index (private->slider_handle_grips,
-                                                   GimpCanvasItem *, i));
+      gimp_canvas_group_remove_item (GIMP_CANVAS_GROUP (private->slider_group),
+                                     gimp_tool_line_get_handle (line, i));
     }
 
-  g_array_set_size (private->slider_handle_circles, private->sliders->len);
-  g_array_set_size (private->slider_handle_grips,   private->sliders->len);
+  g_array_set_size (private->slider_handles, private->sliders->len);
 
   for (i = 0; i < private->sliders->len; i++)
     {
       gdouble          value;
       gdouble          x;
       gdouble          y;
-      GimpCanvasItem **circle;
-      GimpCanvasItem **grip;
+      GimpCanvasItem **handle;
 
       value = gimp_tool_line_get_slider (line, i)->value;
 
       x = private->x1 + (private->x2 - private->x1) * value;
       y = private->y1 + (private->y2 - private->y1) * value;
 
-      circle = &g_array_index (private->slider_handle_circles,
-                               GimpCanvasItem *, i);
-      grip   = &g_array_index (private->slider_handle_grips,
-                               GimpCanvasItem *, i);
+      handle = &g_array_index (private->slider_handles, GimpCanvasItem *, i);
 
-      if (*circle)
+      if (*handle)
         {
-          gimp_canvas_handle_set_position (*circle, x, y);
-          gimp_canvas_handle_set_position (*grip,   x, y);
+          gimp_canvas_handle_set_position (*handle, x, y);
         }
       else
         {
-          *circle = gimp_tool_widget_add_handle (widget,
-                                                 GIMP_HANDLE_CIRCLE,
-                                                 x,
-                                                 y,
-                                                 2 * SLIDER_GRIP_HANDLE_SIZE,
-                                                 2 * SLIDER_GRIP_HANDLE_SIZE,
-                                                 GIMP_HANDLE_ANCHOR_CENTER);
-          *grip   = gimp_tool_widget_add_handle (widget,
-                                                 SLIDER_GRIP_HANDLE_TYPE,
-                                                 x,
-                                                 y,
-                                                 SLIDER_GRIP_HANDLE_SIZE,
-                                                 SLIDER_GRIP_HANDLE_SIZE,
-                                                 GIMP_HANDLE_ANCHOR_CENTER);
+          *handle = gimp_canvas_handle_new (gimp_tool_widget_get_shell (widget),
+                                            SLIDER_HANDLE_TYPE,
+                                            GIMP_HANDLE_ANCHOR_CENTER,
+                                            x,
+                                            y,
+                                            SLIDER_HANDLE_SIZE,
+                                            SLIDER_HANDLE_SIZE);
+
+          gimp_canvas_group_add_item (GIMP_CANVAS_GROUP (private->slider_group),
+                                      *handle);
+          g_object_unref (*handle);
         }
     }
 
@@ -491,30 +540,41 @@ gimp_tool_line_button_press (GimpToolWidget      *widget,
   GimpToolLine        *line    = GIMP_TOOL_LINE (widget);
   GimpToolLinePrivate *private = line->private;
 
-  if (private->point != POINT_NONE)
+  private->grab = GRAB_NONE;
+
+  private->saved_x1 = private->x1;
+  private->saved_y1 = private->y1;
+  private->saved_x2 = private->x2;
+  private->saved_y2 = private->y2;
+
+  if (GIMP_TOOL_LINE_HANDLE_IS_SLIDER (private->hover))
     {
-      private->saved_x1 = private->x1;
-      private->saved_y1 = private->y1;
-      private->saved_x2 = private->x2;
-      private->saved_y2 = private->y2;
+      private->saved_slider_value =
+        gimp_tool_line_get_slider (line, private->hover)->value;
+    }
 
-      if (private->point == POINT_SLIDER)
-        {
-          private->saved_slider_value =
-            gimp_tool_line_get_slider (line, private->slider_index)->value;
-        }
+  if (private->hover != GIMP_TOOL_LINE_HANDLE_NONE)
+    {
+      gimp_tool_line_set_selection (line, private->hover);
 
-      private->point_grabbed = TRUE;
+      private->grab = GRAB_SELECTION;
+    }
+  else if (state & GRAB_LINE_MASK)
+    {
+      private->grab = GRAB_LINE;
+    }
 
-      gimp_tool_line_point_motion (line,
-                                   state & gimp_get_constrain_behavior_mask ());
+  if (grab == GRAB_NONE)
+    {
+      private->hover = GIMP_TOOL_LINE_HANDLE_NONE;
 
-      return private->point;
+      gimp_tool_line_set_selection (line, GIMP_TOOL_LINE_HANDLE_NONE);
     }
 
+  gimp_tool_line_update_handles (line);
   gimp_tool_line_update_status (line, state, TRUE);
 
-  return 0;
+  return private->grab != GRAB_NONE;
 }
 
 void
@@ -527,11 +587,13 @@ gimp_tool_line_button_release (GimpToolWidget        *widget,
   GimpToolLine        *line    = GIMP_TOOL_LINE (widget);
   GimpToolLinePrivate *private = line->private;
 
-  if (release_type == GIMP_BUTTON_RELEASE_CANCEL)
+  if (release_type == GIMP_BUTTON_RELEASE_CANCEL &&
+      private->grab != GRAB_NONE)
     {
-      if (private->point == POINT_SLIDER)
+      if (private->grab == GRAB_SELECTION &&
+          GIMP_TOOL_LINE_HANDLE_IS_SLIDER (private->selection))
         {
-          gimp_tool_line_get_slider (line, private->slider_index)->value =
+          gimp_tool_line_get_slider (line, private->selection)->value =
             private->saved_slider_value;
         }
 
@@ -543,7 +605,7 @@ gimp_tool_line_button_release (GimpToolWidget        *widget,
                     NULL);
     }
 
-  private->point_grabbed = FALSE;
+  private->grab = GRAB_NONE;
 }
 
 void
@@ -560,7 +622,7 @@ gimp_tool_line_motion (GimpToolWidget   *widget,
   private->mouse_x = coords->x;
   private->mouse_y = coords->y;
 
-  if (private->point == POINT_BOTH)
+  if (private->grab == GRAB_LINE)
     {
       g_object_set (line,
                     "x1", private->x1 + diff_x,
@@ -573,7 +635,7 @@ gimp_tool_line_motion (GimpToolWidget   *widget,
     {
       gboolean constrain = (state & gimp_get_constrain_behavior_mask ()) != 0;
 
-      gimp_tool_line_point_motion (line, constrain);
+      gimp_tool_line_selection_motion (line, constrain);
     }
 
   gimp_tool_line_update_status (line, state, TRUE);
@@ -592,51 +654,38 @@ gimp_tool_line_hover (GimpToolWidget   *widget,
   private->mouse_x = coords->x;
   private->mouse_y = coords->y;
 
-  gimp_tool_line_update_handles (line);
-
-  private->point = POINT_NONE;
+  private->hover = GIMP_TOOL_LINE_HANDLE_NONE;
 
-  if (state & GDK_MOD1_MASK)
-    {
-      private->point = POINT_BOTH;
-    }
-  else
+  if (! (state & GRAB_LINE_MASK))
     {
-      /* give sliders precedence over the endpoints, since they're smaller */
-      for (i = private->sliders->len - 1; i >= 0; i--)
+      /* find the closest handle to the cursor */
+      gdouble min_dist     = G_MAXDOUBLE;
+      gint    first_handle = private->sliders->len - 1;
+
+      /* skip the sliders if the two endpoints are the same, in particular so
+       * that if the line is created during a button-press event (as in the
+       * blend tool), the end endpoint is dragged, instead of a slider.
+       */
+      if (private->x1 == private->x2 && private->y1 == private->y2)
+        first_handle = -1;
+
+      for (i = first_handle; i > GIMP_TOOL_LINE_HANDLE_NONE; i--)
         {
-          GimpCanvasItem *circle;
+          GimpCanvasItem *handle;
 
-          circle = g_array_index (private->slider_handle_circles,
-                                  GimpCanvasItem *, i);
-
-          if (gimp_canvas_item_hit (circle, private->mouse_x, private->mouse_y))
-            {
-              private->point        = POINT_SLIDER;
-              private->slider_index = i;
-
-              break;
-            }
-        }
+          handle = gimp_tool_line_get_handle (line, i);
 
-      if (private->point == POINT_NONE)
-        {
-          if (gimp_canvas_item_hit (private->end_handle_circle,
-                                    private->mouse_x,
-                                    private->mouse_y))
-            {
-              private->point = POINT_END;
-            }
-          else if (gimp_canvas_item_hit (private->start_handle_circle,
+          if (gimp_tool_line_handle_hit (handle,
                                          private->mouse_x,
-                                         private->mouse_y))
+                                         private->mouse_y,
+                                         &min_dist))
             {
-              private->point = POINT_START;
+              private->hover = i;
             }
         }
     }
 
-  gimp_tool_line_update_hilight (line);
+  gimp_tool_line_update_handles (line);
   gimp_tool_line_update_status (line, state, proximity);
 }
 
@@ -650,7 +699,7 @@ gimp_tool_line_motion_modifier (GimpToolWidget  *widget,
 
   if (key == gimp_get_constrain_behavior_mask ())
     {
-      gimp_tool_line_point_motion (line, press);
+      gimp_tool_line_selection_motion (line, press);
 
       gimp_tool_line_update_status (line, state, TRUE);
     }
@@ -667,7 +716,14 @@ gimp_tool_line_get_cursor (GimpToolWidget     *widget,
   GimpToolLine        *line    = GIMP_TOOL_LINE (widget);
   GimpToolLinePrivate *private = line->private;
 
-  if (private->point == POINT_BOTH)
+  if (private->grab ==GRAB_LINE || (state & GRAB_LINE_MASK))
+    {
+      *modifier = GIMP_CURSOR_MODIFIER_MOVE;
+
+      return TRUE;
+    }
+  else if (private->grab  == GRAB_SELECTION ||
+           private->hover >  GIMP_TOOL_LINE_HANDLE_NONE)
     {
       *modifier = GIMP_CURSOR_MODIFIER_MOVE;
 
@@ -688,17 +744,49 @@ gimp_tool_line_get_slider (GimpToolLine *line,
   return &g_array_index (private->sliders, GimpControllerSlider, slider);
 }
 
+static GimpCanvasItem *
+gimp_tool_line_get_handle (GimpToolLine *line,
+                           gint          handle)
+{
+  GimpToolLinePrivate *private = line->private;
+
+  switch (handle)
+    {
+    case GIMP_TOOL_LINE_HANDLE_NONE:
+      return NULL;
+
+    case GIMP_TOOL_LINE_HANDLE_START:
+      return private->start_handle;
+
+    case GIMP_TOOL_LINE_HANDLE_END:
+      return private->end_handle;
+
+    default:
+      g_assert (handle >= 0 &&
+                handle <  (gint) private->slider_handles->len);
+
+      return g_array_index (private->slider_handles,
+                            GimpCanvasItem *, handle);
+    }
+}
+
 static gboolean
-gimp_tool_line_point_motion (GimpToolLine *line,
-                             gboolean      constrain)
+gimp_tool_line_selection_motion (GimpToolLine *line,
+                                 gboolean      constrain)
 {
   GimpToolLinePrivate *private = line->private;
   gdouble              x       = private->mouse_x;
   gdouble              y       = private->mouse_y;
 
-  switch (private->point)
+  if (private->grab != GRAB_SELECTION)
+    return FALSE;
+
+  switch (private->selection)
     {
-    case POINT_START:
+    case GIMP_TOOL_LINE_HANDLE_NONE:
+      g_assert_not_reached ();
+
+    case GIMP_TOOL_LINE_HANDLE_START:
       if (constrain)
         gimp_constrain_line (private->x2, private->y2,
                              &x, &y,
@@ -710,7 +798,7 @@ gimp_tool_line_point_motion (GimpToolLine *line,
                     NULL);
       return TRUE;
 
-    case POINT_END:
+    case GIMP_TOOL_LINE_HANDLE_END:
       if (constrain)
         gimp_constrain_line (private->x1, private->y1,
                              &x, &y,
@@ -722,7 +810,7 @@ gimp_tool_line_point_motion (GimpToolLine *line,
                     NULL);
       return TRUE;
 
-    case POINT_SLIDER:
+    default:
       {
         gdouble length_sqr;
 
@@ -737,7 +825,7 @@ gimp_tool_line_point_motion (GimpToolLine *line,
             GimpControllerSlider *slider;
             gdouble               value;
 
-            slider = gimp_tool_line_get_slider (line, private->slider_index);
+            slider = gimp_tool_line_get_slider (line, private->selection);
 
             /* project the cursor position onto the line */
             value  = (private->x2 - private->x1) * (x - private->x1) +
@@ -761,82 +849,41 @@ gimp_tool_line_point_motion (GimpToolLine *line,
 
         return TRUE;
       }
-
-    default:
-      break;
     }
-
-  return FALSE;
 }
 
 static void
 gimp_tool_line_update_handles (GimpToolLine *line)
 {
   GimpToolLinePrivate *private = line->private;
-  gboolean             start_visible,  end_visible;
-  gint                 start_diameter, end_diameter;
-  gint                 i;
-
-  /* Calculate handle visibility */
-  if (private->point_grabbed)
-    {
-      start_visible = FALSE;
-      end_visible   = FALSE;
-    }
-  else
-    {
-      start_diameter = gimp_canvas_handle_calc_size (private->start_handle_circle,
-                                                     private->mouse_x,
-                                                     private->mouse_y,
-                                                     0,
-                                                     2 * ENDPOINT_GRIP_HANDLE_SIZE);
-      start_visible = start_diameter > 2;
-
-      end_diameter = gimp_canvas_handle_calc_size (private->end_handle_circle,
-                                                   private->mouse_x,
-                                                   private->mouse_y,
-                                                   0,
-                                                   2 * ENDPOINT_GRIP_HANDLE_SIZE);
-      end_visible = end_diameter > 2;
-    }
+  GimpCanvasItem      *handle;
+  gboolean             visible;
 
-  gimp_canvas_item_set_visible (private->start_handle_circle, start_visible);
-  gimp_canvas_item_set_visible (private->end_handle_circle,   end_visible);
+  handle = gimp_tool_line_get_handle (line, private->hover);
 
-  if (start_visible)
-    gimp_canvas_handle_set_size (private->start_handle_circle,
-                                 start_diameter, start_diameter);
+  visible = handle && private->grab == GRAB_NONE;
 
-  if (end_visible)
-    gimp_canvas_handle_set_size (private->end_handle_circle,
-                                 end_diameter, end_diameter);
-
-  for (i = 0; i < private->sliders->len; i++)
+  if (visible)
     {
-      GimpCanvasItem *circle;
-      gboolean        visible;
-      gint            diameter;
+      gdouble x;
+      gdouble y;
+      gint    width;
+      gint    height;
 
-      circle = g_array_index (private->slider_handle_circles,
-                              GimpCanvasItem *, i);
+      gimp_canvas_handle_get_position (handle, &x,     &y);
+      gimp_canvas_handle_get_size     (handle, &width, &height);
 
-      if (private->point_grabbed)
-        visible = FALSE;
-      else
-        {
-          diameter = gimp_canvas_handle_calc_size (circle,
-                                                   private->mouse_x,
-                                                   private->mouse_y,
-                                                   0,
-                                                   2 * SLIDER_GRIP_HANDLE_SIZE);
-          visible = diameter > 2;
-        }
+      width   = MAX (width,  SLIDER_HANDLE_SIZE);
+      height  = MAX (height, SLIDER_HANDLE_SIZE);
 
-      gimp_canvas_item_set_visible (circle, visible);
+      width  *= HANDLE_CIRCLE_SCALE;
+      height *= HANDLE_CIRCLE_SCALE;
 
-      if (visible)
-        gimp_canvas_handle_set_size (circle, diameter, diameter);
+      gimp_canvas_handle_set_position (private->handle_circle, x,     y);
+      gimp_canvas_handle_set_size     (private->handle_circle, width, height);
     }
+
+  gimp_canvas_item_set_visible (private->handle_circle, visible);
 }
 
 static void
@@ -845,32 +892,15 @@ gimp_tool_line_update_hilight (GimpToolLine *line)
   GimpToolLinePrivate *private = line->private;
   gint                 i;
 
-  gimp_canvas_item_set_highlight (private->start_handle_circle,
-                                  private->point == POINT_START);
-  gimp_canvas_item_set_highlight (private->start_handle_grip,
-                                  private->point == POINT_START);
-
-  gimp_canvas_item_set_highlight (private->end_handle_circle,
-                                  private->point == POINT_END);
-  gimp_canvas_item_set_highlight (private->end_handle_grip,
-                                  private->point == POINT_END);
-
-  for (i = 0; i < private->sliders->len; i++)
+  for (i = GIMP_TOOL_LINE_HANDLE_NONE + 1;
+       i < (gint) private->sliders->len;
+       i++)
     {
-      GimpCanvasItem *circle;
-      GimpCanvasItem *grip;
-      gboolean        highlight;
+      GimpCanvasItem *handle;
 
-      circle = g_array_index (private->slider_handle_circles,
-                              GimpCanvasItem *, i);
-      grip   = g_array_index (private->slider_handle_grips,
-                              GimpCanvasItem *, i);
+      handle = gimp_tool_line_get_handle (line, i);
 
-      highlight =
-        (private->point == POINT_SLIDER && private->slider_index == i);
-
-      gimp_canvas_item_set_highlight (circle, highlight);
-      gimp_canvas_item_set_highlight (grip,   highlight);
+      gimp_canvas_item_set_highlight (handle, i == private->selection);
     }
 }
 
@@ -907,6 +937,40 @@ gimp_tool_line_update_status (GimpToolLine    *line,
     }
 }
 
+static gboolean
+gimp_tool_line_handle_hit (GimpCanvasItem *handle,
+                           gdouble         x,
+                           gdouble         y,
+                           gdouble        *min_dist)
+{
+  gdouble handle_x;
+  gdouble handle_y;
+  gint    handle_width;
+  gint    handle_height;
+  gint    radius;
+  gdouble dist;
+
+  gimp_canvas_handle_get_position (handle, &handle_x,     &handle_y);
+  gimp_canvas_handle_get_size     (handle, &handle_width, &handle_height);
+
+  radius = ((gint) (handle_width * HANDLE_CIRCLE_SCALE)) / 2;
+  radius = MAX (radius, LINE_VICINITY);
+
+  dist = gimp_canvas_item_transform_distance (handle,
+                                              x, y, handle_x, handle_y);
+
+  if (dist <= radius && dist < *min_dist)
+    {
+      *min_dist = dist;
+
+      return TRUE;
+    }
+  else
+    {
+      return FALSE;
+    }
+}
+
 
 /*  public functions  */
 
@@ -940,6 +1004,12 @@ gimp_tool_line_set_sliders (GimpToolLine               *line,
 
   private = line->private;
 
+  if (GIMP_TOOL_LINE_HANDLE_IS_SLIDER (private->selection) &&
+      private->sliders->len != n_sliders)
+    {
+      gimp_tool_line_set_selection (line, GIMP_TOOL_LINE_HANDLE_NONE);
+    }
+
   g_array_set_size (private->sliders, n_sliders);
 
   memcpy (private->sliders->data, sliders,
@@ -964,3 +1034,33 @@ gimp_tool_line_get_sliders (GimpToolLine *line,
 
   return (const GimpControllerSlider *) private->sliders->data;
 }
+
+void
+gimp_tool_line_set_selection (GimpToolLine *line,
+                              gint          handle)
+{
+  GimpToolLinePrivate *private;
+
+  g_return_if_fail (GIMP_IS_TOOL_LINE (line));
+
+  private = line->private;
+
+  g_return_if_fail (handle >= GIMP_TOOL_LINE_HANDLE_NONE &&
+                    handle <  (gint) private->sliders->len);
+
+  g_object_set (line,
+                "selection", handle,
+                NULL);
+}
+
+gint
+gimp_tool_line_get_selection (GimpToolLine *line)
+{
+  GimpToolLinePrivate *private;
+
+  g_return_val_if_fail (GIMP_IS_TOOL_LINE (line), GIMP_TOOL_LINE_HANDLE_NONE);
+
+  private = line->private;
+
+  return private->selection;
+}
diff --git a/app/display/gimptoolline.h b/app/display/gimptoolline.h
index 2398d03..e4f21a4 100644
--- a/app/display/gimptoolline.h
+++ b/app/display/gimptoolline.h
@@ -25,6 +25,17 @@
 #include "gimptoolwidget.h"
 
 
+/* in the context of GimpToolLine, "handle" is a collective term for either an
+ * endpoint or a slider.  a handle value may be either a (nonnegative) slider
+ * index, or one of the values below:
+ */
+#define GIMP_TOOL_LINE_HANDLE_NONE  (-3)
+#define GIMP_TOOL_LINE_HANDLE_START (-2)
+#define GIMP_TOOL_LINE_HANDLE_END   (-1)
+
+#define GIMP_TOOL_LINE_HANDLE_IS_SLIDER(handle) ((handle) >= 0)
+
+
 #define GIMP_TYPE_TOOL_LINE            (gimp_tool_line_get_type ())
 #define GIMP_TOOL_LINE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_LINE, 
GimpToolLine))
 #define GIMP_TOOL_LINE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_LINE, 
GimpToolLineClass))
@@ -47,22 +58,29 @@ struct _GimpToolLine
 struct _GimpToolLineClass
 {
   GimpToolWidgetClass  parent_class;
+
+  /*  signals  */
+  void (* selection_changed) (GimpToolLine *line);
 };
 
 
-GType                        gimp_tool_line_get_type    (void) G_GNUC_CONST;
+GType                        gimp_tool_line_get_type      (void) G_GNUC_CONST;
+
+GimpToolWidget             * gimp_tool_line_new           (GimpDisplayShell           *shell,
+                                                           gdouble                     x1,
+                                                           gdouble                     y1,
+                                                           gdouble                     x2,
+                                                           gdouble                     y2);
 
-GimpToolWidget             * gimp_tool_line_new         (GimpDisplayShell           *shell,
-                                                         gdouble                     x1,
-                                                         gdouble                     y1,
-                                                         gdouble                     x2,
-                                                         gdouble                     y2);
+void                         gimp_tool_line_set_sliders   (GimpToolLine               *line,
+                                                           const GimpControllerSlider *sliders,
+                                                           gint                        n_sliders);
+const GimpControllerSlider * gimp_tool_line_get_sliders   (GimpToolLine               *line,
+                                                           gint                       *n_sliders);
 
-void                         gimp_tool_line_set_sliders (GimpToolLine               *line,
-                                                         const GimpControllerSlider *sliders,
-                                                         gint                        n_sliders);
-const GimpControllerSlider * gimp_tool_line_get_sliders (GimpToolLine               *line,
-                                                         gint                       *n_sliders);
+void                         gimp_tool_line_set_selection (GimpToolLine               *line,
+                                                           gint                        handle);
+gint                         gimp_tool_line_get_selection (GimpToolLine               *line);
 
 
 #endif /* __GIMP_TOOL_LINE_H__ */


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