[gimp] app: start an infrastructure for on-canvas controllers for operations



commit f4f2de1b5ca551e774319f450dd23cc582c1875a
Author: Michael Natterer <mitch gimp org>
Date:   Mon Jul 3 00:37:55 2017 +0200

    app: start an infrastructure for on-canvas controllers for operations
    
    Pass a "GimpCreateControllerFunc" to all gimppropgui-*.[ch]
    constructors which takes a callback (to update the config object when
    the on-canvas GUI) and a controller type that determines the
    callback's signature, and returns another callback (to update the
    on-canvas GUI when the config object changes).
    
    In GimpOperationTool, pass such a GimpCreateControllerFunc that
    handles creating and adding on-canvas controller via the new
    gimpfiltertool-widgets.[ch]. So far, a simple line like in the
    blend tool is supported.
    
    Add a custom GUI for gegl:spiral, and have its origin, radius and
    angle controlled by such a line.

 app/tools/Makefile.am                         |    2 +
 app/tools/gimpfiltertool-widgets.c            |  180 +++++++++++++++++++++
 app/tools/gimpfiltertool-widgets.h            |   35 ++++
 app/tools/gimpoperationtool.c                 |  208 ++++++++++++++++---------
 app/widgets/Makefile.am                       |    2 +
 app/widgets/gimppropgui-channel-mixer.c       |   35 ++--
 app/widgets/gimppropgui-channel-mixer.h       |   15 +-
 app/widgets/gimppropgui-color-balance.c       |   15 +-
 app/widgets/gimppropgui-color-balance.h       |   15 +-
 app/widgets/gimppropgui-color-rotate.c        |   19 ++-
 app/widgets/gimppropgui-color-rotate.h        |   15 +-
 app/widgets/gimppropgui-convolution-matrix.c  |   27 ++--
 app/widgets/gimppropgui-convolution-matrix.h  |   15 +-
 app/widgets/gimppropgui-diffration-patterns.c |   31 +++--
 app/widgets/gimppropgui-diffration-patterns.h |   15 +-
 app/widgets/gimppropgui-generic.c             |   26 ++--
 app/widgets/gimppropgui-generic.h             |   15 +-
 app/widgets/gimppropgui-hue-saturation.c      |   15 +-
 app/widgets/gimppropgui-hue-saturation.h      |   15 +-
 app/widgets/gimppropgui-spiral.c              |  152 ++++++++++++++++++
 app/widgets/gimppropgui-spiral.h              |   35 ++++
 app/widgets/gimppropgui.c                     |   74 +++++----
 app/widgets/gimppropgui.h                     |   93 +++++++----
 app/widgets/gimpsymmetryeditor.c              |    2 +-
 24 files changed, 791 insertions(+), 265 deletions(-)
---
diff --git a/app/tools/Makefile.am b/app/tools/Makefile.am
index 6b6e9a2..4baa7c7 100644
--- a/app/tools/Makefile.am
+++ b/app/tools/Makefile.am
@@ -80,6 +80,8 @@ libapptools_a_sources = \
        gimpfiltertool.h                \
        gimpfiltertool-settings.c       \
        gimpfiltertool-settings.h       \
+       gimpfiltertool-widgets.c        \
+       gimpfiltertool-widgets.h        \
        gimpflipoptions.c               \
        gimpflipoptions.h               \
        gimpfliptool.c                  \
diff --git a/app/tools/gimpfiltertool-widgets.c b/app/tools/gimpfiltertool-widgets.c
new file mode 100644
index 0000000..c841a36
--- /dev/null
+++ b/app/tools/gimpfiltertool-widgets.c
@@ -0,0 +1,180 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpfiltertool-widgets.c
+ *
+ * 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 "tools-types.h"
+
+#include "core/gimpitem.h"
+
+#include "display/gimpdisplay.h"
+#include "display/gimptoolline.h"
+
+#include "gimpfilteroptions.h"
+#include "gimpfiltertool.h"
+#include "gimpfiltertool-widgets.h"
+
+
+typedef struct _Controller Controller;
+
+struct _Controller
+{
+  GimpFilterTool     *filter_tool;
+  GimpControllerType  controller_type;
+  GimpToolWidget     *widget;
+  GCallback           creator_callback;
+  gpointer            creator_data;
+};
+
+
+/*  local function prototypes  */
+
+static void   gimp_filter_tool_set_line     (Controller     *controller,
+                                             GeglRectangle  *area,
+                                             gdouble         x1,
+                                             gdouble         y1,
+                                             gdouble         x2,
+                                             gdouble         y2);
+static void   gimp_filter_tool_line_changed (GimpToolWidget *widget,
+                                             Controller     *controller);
+
+
+/*  public functions  */
+
+GimpToolWidget *
+gimp_filter_tool_create_widget (GimpFilterTool     *filter_tool,
+                                GimpControllerType  controller_type,
+                                GCallback           callback,
+                                gpointer            callback_data,
+                                GCallback          *set_func,
+                                gpointer           *set_func_data)
+{
+  GimpTool         *tool;
+  GimpDisplayShell *shell;
+  Controller       *controller;
+
+  g_return_val_if_fail (GIMP_IS_FILTER_TOOL (filter_tool), NULL);
+
+  tool = GIMP_TOOL (filter_tool);
+
+  g_return_val_if_fail (tool->display != NULL, NULL);
+
+  shell = gimp_display_get_shell (tool->display);
+
+  controller = g_new0 (Controller, 1);
+
+  controller->filter_tool      = filter_tool;
+  controller->controller_type  = controller_type;
+  controller->creator_callback = callback;
+  controller->creator_data     = callback_data;
+
+  switch (controller_type)
+    {
+    case GIMP_CONTROLLER_TYPE_LINE:
+      controller->widget = gimp_tool_line_new (shell, 100, 100, 500, 500);
+
+      g_signal_connect (controller->widget, "changed",
+                        G_CALLBACK (gimp_filter_tool_line_changed),
+                        controller);
+
+      *set_func      = (GCallback) gimp_filter_tool_set_line;
+      *set_func_data = controller;
+      break;
+    }
+
+  g_object_set_data_full (G_OBJECT (controller->widget),
+                          "controller", controller,
+                          (GDestroyNotify) g_free);
+
+  return controller->widget;
+}
+
+
+/*  private functions  */
+
+static void
+gimp_filter_tool_set_line (Controller    *controller,
+                           GeglRectangle *area,
+                           gdouble        x1,
+                           gdouble        y1,
+                           gdouble        x2,
+                           gdouble        y2)
+{
+  GimpDrawable *drawable = controller->filter_tool->drawable;
+
+  if (drawable)
+    {
+      gint off_x, off_y;
+
+      gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);
+
+      x1 += off_x + area->x;
+      y1 += off_y + area->y;
+      x2 += off_x + area->x;
+      y2 += off_y + area->y;
+    }
+
+  g_signal_handlers_block_by_func (controller->widget,
+                                   gimp_filter_tool_line_changed,
+                                   controller);
+
+  g_object_set (controller->widget,
+                "x1", x1,
+                "y1", y1,
+                "x2", x2,
+                "y2", y2,
+                NULL);
+
+  g_signal_handlers_unblock_by_func (controller->widget,
+                                     gimp_filter_tool_line_changed,
+                                     controller);
+}
+
+static void
+gimp_filter_tool_line_changed (GimpToolWidget *widget,
+                               Controller     *controller)
+{
+  GimpFilterTool             *filter_tool = controller->filter_tool;
+  GimpControllerLineCallback  line_callback;
+  gdouble                     x1, y1, x2, y2;
+  gint                        off_x, off_y;
+  GeglRectangle               area;
+
+  line_callback = (GimpControllerLineCallback) controller->creator_callback;
+
+  g_object_get (widget,
+                "x1", &x1,
+                "y1", &y1,
+                "x2", &x2,
+                "y2", &y2,
+                NULL);
+
+  gimp_filter_tool_get_drawable_area (filter_tool, &off_x, &off_y, &area);
+
+  x1 -= off_x + area.x;
+  y1 -= off_y + area.y;
+  x2 -= off_x + area.x;
+  y2 -= off_y + area.y;
+
+  line_callback (controller->creator_data,
+                 &area, x1, y1, x2, y2);
+}
diff --git a/app/tools/gimpfiltertool-widgets.h b/app/tools/gimpfiltertool-widgets.h
new file mode 100644
index 0000000..db5fb07
--- /dev/null
+++ b/app/tools/gimpfiltertool-widgets.h
@@ -0,0 +1,35 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpfiltertool-widgets.h
+ *
+ * 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_FILTER_TOOL_WIDGETS_H__
+#define __GIMP_FILTER_TOOL_WIDGETS_H__
+
+
+#include "widgets/gimppropgui.h" /* FIXME remove */
+
+
+GimpToolWidget * gimp_filter_tool_create_widget (GimpFilterTool     *filter_tool,
+                                                 GimpControllerType  controller_type,
+                                                 GCallback           callback,
+                                                 gpointer            callback_data,
+                                                 GCallback          *set_func,
+                                                 gpointer           *set_func_data);
+
+
+#endif /* __GIMP_FILTER_TOOL_WIDGETS_H__ */
diff --git a/app/tools/gimpoperationtool.c b/app/tools/gimpoperationtool.c
index f436646..9cffd97 100644
--- a/app/tools/gimpoperationtool.c
+++ b/app/tools/gimpoperationtool.c
@@ -51,6 +51,7 @@
 #include "display/gimptoolgui.h"
 
 #include "gimpfilteroptions.h"
+#include "gimpfiltertool-widgets.h"
 #include "gimpoperationtool.h"
 
 #include "gimp-intl.h"
@@ -98,9 +99,12 @@ static void        gimp_operation_tool_color_picked    (GimpFilterTool    *filte
                                                         const Babl        *sample_format,
                                                         const GimpRGB     *color);
 
+static void        gimp_operation_tool_halt            (GimpOperationTool *op_tool);
+
 static void        gimp_operation_tool_sync_op         (GimpOperationTool *op_tool,
                                                         GimpDrawable      *drawable,
                                                         gboolean           sync_colors);
+static void        gimp_operation_tool_create_gui      (GimpOperationTool *tool);
 
 static AuxInput *  gimp_operation_tool_aux_input_new   (GimpOperationTool *tool,
                                                         GeglNode          *operation,
@@ -219,7 +223,12 @@ gimp_operation_tool_initialize (GimpTool     *tool,
       GimpDrawable      *drawable    = gimp_image_get_active_drawable (image);
 
       if (filter_tool->config)
-        gimp_operation_tool_sync_op (op_tool, drawable, TRUE);
+        {
+          gimp_operation_tool_sync_op (op_tool, drawable, TRUE);
+
+          if (! op_tool->options_gui)
+            gimp_operation_tool_create_gui (op_tool);
+        }
 
       return TRUE;
     }
@@ -241,8 +250,7 @@ gimp_operation_tool_control (GimpTool       *tool,
       break;
 
     case GIMP_TOOL_ACTION_HALT:
-      g_list_foreach (op_tool->aux_inputs,
-                      (GFunc) gimp_operation_tool_aux_input_clear, NULL);
+      gimp_operation_tool_halt (op_tool);
       break;
 
     case GIMP_TOOL_ACTION_COMMIT:
@@ -294,6 +302,9 @@ gimp_operation_tool_dialog (GimpFilterTool *filter_tool)
                       TRUE, TRUE, 0);
   gtk_widget_show (tool->options_box);
 
+  g_object_add_weak_pointer (G_OBJECT (tool->options_box),
+                             (gpointer) &tool->options_box);
+
   for (list = tool->aux_inputs; list; list = g_list_next (list))
     {
       AuxInput *input = list->data;
@@ -342,47 +353,20 @@ gimp_operation_tool_color_picked (GimpFilterTool  *filter_tool,
                                   const Babl      *sample_format,
                                   const GimpRGB   *color)
 {
-  GimpOperationTool  *tool = GIMP_OPERATION_TOOL (filter_tool);
-  gchar             **pspecs;
-
-  pspecs = g_strsplit (identifier, ":", 2);
+  gchar **pspecs = g_strsplit (identifier, ":", 2);
 
   if (pspecs[1])
     {
-      GimpFilterOptions *options      = GIMP_FILTER_TOOL_GET_OPTIONS (tool);
-      GimpDrawable      *drawable     = GIMP_TOOL (filter_tool)->drawable;
-      GObjectClass      *object_class = G_OBJECT_GET_CLASS (filter_tool->config);
-      GParamSpec        *pspec_x;
-      GParamSpec        *pspec_y;
-      gint               width        = 1;
-      gint               height       = 1;
-
-      if (drawable)
-        {
-          gint off_x, off_y;
+      GObjectClass  *object_class = G_OBJECT_GET_CLASS (filter_tool->config);
+      GParamSpec    *pspec_x;
+      GParamSpec    *pspec_y;
+      gint           off_x, off_y;
+      GeglRectangle  area;
 
-          gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);
+      gimp_filter_tool_get_drawable_area (filter_tool, &off_x, &off_y, &area);
 
-          x -= off_x;
-          y -= off_y;
-
-          switch (options->region)
-            {
-            case GIMP_FILTER_REGION_SELECTION:
-              if (gimp_item_mask_intersect (GIMP_ITEM (drawable),
-                                            &off_x, &off_y, &width, &height))
-                {
-                  x -= off_x;
-                  y -= off_y;
-                }
-              break;
-
-            case GIMP_FILTER_REGION_DRAWABLE:
-              width  = gimp_item_get_width  (GIMP_ITEM (drawable));
-              height = gimp_item_get_height (GIMP_ITEM (drawable));
-              break;
-            }
-        }
+      x -= off_x + area.x;
+      y -= off_y + area.y;
 
       pspec_x = g_object_class_find_property (object_class, pspecs[0]);
       pspec_y = g_object_class_find_property (object_class, pspecs[1]);
@@ -401,8 +385,8 @@ gimp_operation_tool_color_picked (GimpFilterTool  *filter_tool,
           if (HAS_KEY (pspec_x, "unit", "relative-coordinate") &&
               HAS_KEY (pspec_y, "unit", "relative-coordinate"))
             {
-              x /= (gdouble) width;
-              y /= (gdouble) height;
+              x /= (gdouble) area.width;
+              y /= (gdouble) area.height;
             }
 
           if (G_IS_PARAM_SPEC_INT (pspec_x))
@@ -452,6 +436,50 @@ gimp_operation_tool_color_picked (GimpFilterTool  *filter_tool,
 }
 
 static void
+gimp_operation_tool_halt (GimpOperationTool *op_tool)
+{
+  if (op_tool->operation)
+    {
+      g_free (op_tool->operation);
+      op_tool->operation = NULL;
+    }
+
+  if (op_tool->title)
+    {
+      g_free (op_tool->title);
+      op_tool->title = NULL;
+    }
+
+  if (op_tool->description)
+    {
+      g_free (op_tool->description);
+      op_tool->description = NULL;
+    }
+
+  if (op_tool->undo_desc)
+    {
+      g_free (op_tool->undo_desc);
+      op_tool->undo_desc = NULL;
+    }
+
+  if (op_tool->icon_name)
+    {
+      g_free (op_tool->icon_name);
+      op_tool->icon_name = NULL;
+    }
+
+  if (op_tool->help_id)
+    {
+      g_free (op_tool->help_id);
+      op_tool->help_id = NULL;
+    }
+
+  g_list_foreach (op_tool->aux_inputs,
+                  (GFunc) gimp_operation_tool_aux_input_free, NULL);
+  op_tool->aux_inputs = NULL;
+}
+
+static void
 gimp_operation_tool_sync_op (GimpOperationTool *op_tool,
                              GimpDrawable      *drawable,
                              gboolean           sync_colors)
@@ -524,6 +552,62 @@ gimp_operation_tool_sync_op (GimpOperationTool *op_tool,
   g_free (pspecs);
 }
 
+static GCallback
+gimp_operation_tool_add_controller (GimpOperationTool  *op_tool,
+                                    GimpControllerType  controller_type,
+                                    GCallback           callback,
+                                    gpointer            callback_data,
+                                    gpointer           *set_func_data)
+{
+  GimpFilterTool *filter_tool = GIMP_FILTER_TOOL (op_tool);
+  GimpToolWidget *widget;
+  GCallback       set_func;
+
+  widget = gimp_filter_tool_create_widget (filter_tool,
+                                           controller_type,
+                                           callback,
+                                           callback_data,
+                                           &set_func,
+                                           set_func_data);
+  gimp_filter_tool_set_widget (filter_tool, widget);
+  g_object_unref (widget);
+
+  return set_func;
+}
+
+static void
+gimp_operation_tool_create_gui (GimpOperationTool *tool)
+{
+  GimpFilterTool *filter_tool = GIMP_FILTER_TOOL (tool);
+  gint            off_x, off_y;
+  GeglRectangle   area;
+
+  gimp_filter_tool_get_drawable_area (filter_tool, &off_x, &off_y, &area);
+
+  tool->options_gui =
+    gimp_prop_gui_new (G_OBJECT (filter_tool->config),
+                       G_TYPE_FROM_INSTANCE (filter_tool->config), 0,
+                       &area,
+                       GIMP_CONTEXT (GIMP_TOOL_GET_OPTIONS (tool)),
+                       (GimpCreatePickerFunc) gimp_filter_tool_add_color_picker,
+                       (GimpCreateControllerFunc) gimp_operation_tool_add_controller,
+                       tool);
+
+  g_object_add_weak_pointer (G_OBJECT (tool->options_gui),
+                             (gpointer) &tool->options_gui);
+
+  /*  ugly, see comment in the function  */
+  gimp_filter_tool_set_has_settings (filter_tool,
+                                     ! GTK_IS_LABEL (tool->options_gui));
+
+  if (tool->options_box)
+    {
+      gtk_box_pack_start (GTK_BOX (tool->options_box), tool->options_gui,
+                          TRUE, TRUE, 0);
+      gtk_widget_show (tool->options_gui);
+    }
+}
+
 
 /*  aux input utility functions  */
 
@@ -584,6 +668,8 @@ gimp_operation_tool_aux_input_clear (AuxInput *input)
 static void
 gimp_operation_tool_aux_input_free (AuxInput *input)
 {
+  gimp_operation_tool_aux_input_clear (input);
+
   g_free (input->pad);
   g_object_unref (input->node);
   gtk_widget_destroy (input->box);
@@ -641,6 +727,8 @@ gimp_operation_tool_set_operation (GimpOperationTool *tool,
                     (GDestroyNotify) gimp_operation_tool_aux_input_free);
   tool->aux_inputs = NULL;
 
+  gimp_filter_tool_set_widget (filter_tool, NULL);
+
   gimp_filter_tool_get_operation (filter_tool);
 
   if (tool->options_gui)
@@ -706,41 +794,9 @@ gimp_operation_tool_set_operation (GimpOperationTool *tool,
   if (size_group)
     g_object_unref (size_group);
 
-  if (filter_tool->config)
-    {
-      GeglRectangle *area = NULL;
-      GeglRectangle  tmp  = { 0, };
-
-      if (GIMP_TOOL (tool)->drawable)
-        {
-          GimpDrawable *drawable = GIMP_TOOL (tool)->drawable;
-
-          tmp.width  = gimp_item_get_width  (GIMP_ITEM (drawable));
-          tmp.height = gimp_item_get_height (GIMP_ITEM (drawable));
-
-          area = &tmp;
-        }
-
-      tool->options_gui =
-        gimp_prop_gui_new (G_OBJECT (filter_tool->config),
-                           G_TYPE_FROM_INSTANCE (filter_tool->config), 0,
-                           area,
-                           GIMP_CONTEXT (GIMP_TOOL_GET_OPTIONS (tool)),
-                           (GimpCreatePickerFunc) gimp_filter_tool_add_color_picker,
-                           tool);
-
-      /*  ugly, see comment in the function  */
-      gimp_filter_tool_set_has_settings (filter_tool,
-                                         ! GTK_IS_LABEL (tool->options_gui));
-
-      if (tool->options_box)
-        {
-          gtk_box_pack_start (GTK_BOX (tool->options_box), tool->options_gui,
-                              TRUE, TRUE, 0);
-          gtk_widget_show (tool->options_gui);
-        }
-    }
-
   if (GIMP_TOOL (tool)->drawable)
     gimp_operation_tool_sync_op (tool, GIMP_TOOL (tool)->drawable, TRUE);
+
+  if (filter_tool->config && GIMP_TOOL (tool)->display)
+    gimp_operation_tool_create_gui (tool);
 }
diff --git a/app/widgets/Makefile.am b/app/widgets/Makefile.am
index ad0a5ce..c45a6f8 100644
--- a/app/widgets/Makefile.am
+++ b/app/widgets/Makefile.am
@@ -317,6 +317,8 @@ libappwidgets_a_sources = \
        gimppropgui-generic.h           \
        gimppropgui-hue-saturation.c    \
        gimppropgui-hue-saturation.h    \
+       gimppropgui-spiral.c            \
+       gimppropgui-spiral.h            \
        gimppropwidgets.c               \
        gimppropwidgets.h               \
        gimpradioaction.c               \
diff --git a/app/widgets/gimppropgui-channel-mixer.c b/app/widgets/gimppropgui-channel-mixer.c
index 2535325..0a61988 100644
--- a/app/widgets/gimppropgui-channel-mixer.c
+++ b/app/widgets/gimppropgui-channel-mixer.c
@@ -37,13 +37,14 @@
 
 
 GtkWidget *
-_gimp_prop_gui_new_channel_mixer (GObject              *config,
-                                  GParamSpec          **param_specs,
-                                  guint                 n_param_specs,
-                                  GeglRectangle        *area,
-                                  GimpContext          *context,
-                                  GimpCreatePickerFunc  create_picker_func,
-                                  gpointer              picker_creator)
+_gimp_prop_gui_new_channel_mixer (GObject                  *config,
+                                  GParamSpec              **param_specs,
+                                  guint                     n_param_specs,
+                                  GeglRectangle            *area,
+                                  GimpContext              *context,
+                                  GimpCreatePickerFunc      create_picker_func,
+                                  GimpCreateControllerFunc  create_controller_func,
+                                  gpointer                  creator)
 {
   GtkWidget   *main_vbox;
   GtkWidget   *frame;
@@ -68,17 +69,17 @@ _gimp_prop_gui_new_channel_mixer (GObject              *config,
   gtk_widget_show (vbox);
 
   scale = gimp_prop_widget_new (config, "rr-gain",
-                                area, context, NULL, NULL, &label);
+                                area, context, NULL, NULL, NULL, &label);
   gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
   gtk_widget_show (scale);
 
   scale = gimp_prop_widget_new (config, "rg-gain",
-                                area, context, NULL, NULL, &label);
+                                area, context, NULL, NULL, NULL, &label);
   gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
   gtk_widget_show (scale);
 
   scale = gimp_prop_widget_new (config, "rb-gain",
-                                area, context, NULL, NULL, &label);
+                                area, context, NULL, NULL, NULL, &label);
   gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
   gtk_widget_show (scale);
 
@@ -92,17 +93,17 @@ _gimp_prop_gui_new_channel_mixer (GObject              *config,
   gtk_widget_show (vbox);
 
   scale = gimp_prop_widget_new (config, "gr-gain",
-                                area, context, NULL, NULL, &label);
+                                area, context, NULL, NULL, NULL, &label);
   gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
   gtk_widget_show (scale);
 
   scale = gimp_prop_widget_new (config, "gg-gain",
-                                area, context, NULL, NULL, &label);
+                                area, context, NULL, NULL, NULL, &label);
   gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
   gtk_widget_show (scale);
 
   scale = gimp_prop_widget_new (config, "gb-gain",
-                                area, context, NULL, NULL, &label);
+                                area, context, NULL, NULL, NULL, &label);
   gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
   gtk_widget_show (scale);
 
@@ -116,23 +117,23 @@ _gimp_prop_gui_new_channel_mixer (GObject              *config,
   gtk_widget_show (vbox);
 
   scale = gimp_prop_widget_new (config, "br-gain",
-                                area, context, NULL, NULL, &label);
+                                area, context, NULL, NULL, NULL, &label);
   gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
   gtk_widget_show (scale);
 
   scale = gimp_prop_widget_new (config, "bg-gain",
-                                area, context, NULL, NULL, &label);
+                                area, context, NULL, NULL, NULL, &label);
   gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
   gtk_widget_show (scale);
 
   scale = gimp_prop_widget_new (config, "bb-gain",
-                                area, context, NULL, NULL, &label);
+                                area, context, NULL, NULL, NULL, &label);
   gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
   gtk_widget_show (scale);
 
 
   checkbox = gimp_prop_widget_new (config, "preserve-luminosity",
-                                   area, context, NULL, NULL, &label);
+                                   area, context, NULL, NULL, NULL, &label);
   gtk_box_pack_start (GTK_BOX (main_vbox), checkbox, FALSE, FALSE, 0);
   gtk_widget_show (checkbox);
 
diff --git a/app/widgets/gimppropgui-channel-mixer.h b/app/widgets/gimppropgui-channel-mixer.h
index 9c603cb..3fd363c 100644
--- a/app/widgets/gimppropgui-channel-mixer.h
+++ b/app/widgets/gimppropgui-channel-mixer.h
@@ -22,13 +22,14 @@
 
 
 GtkWidget *
-_gimp_prop_gui_new_channel_mixer (GObject              *config,
-                                  GParamSpec          **param_specs,
-                                  guint                 n_param_specs,
-                                  GeglRectangle        *area,
-                                  GimpContext          *context,
-                                  GimpCreatePickerFunc  create_picker_func,
-                                  gpointer              picker_creator);
+_gimp_prop_gui_new_channel_mixer (GObject                  *config,
+                                  GParamSpec              **param_specs,
+                                  guint                     n_param_specs,
+                                  GeglRectangle            *area,
+                                  GimpContext              *context,
+                                  GimpCreatePickerFunc      create_picker_func,
+                                  GimpCreateControllerFunc  create_controller_func,
+                                  gpointer                  creator);
 
 
 #endif /* __GIMP_PROP_GUI_CHANNEL_MIXER_H__ */
diff --git a/app/widgets/gimppropgui-color-balance.c b/app/widgets/gimppropgui-color-balance.c
index 7cb47d4..41ecfa3 100644
--- a/app/widgets/gimppropgui-color-balance.c
+++ b/app/widgets/gimppropgui-color-balance.c
@@ -70,13 +70,14 @@ create_levels_scale (GObject     *config,
 }
 
 GtkWidget *
-_gimp_prop_gui_new_color_balance (GObject              *config,
-                                  GParamSpec          **param_specs,
-                                  guint                 n_param_specs,
-                                  GeglRectangle        *area,
-                                  GimpContext          *context,
-                                  GimpCreatePickerFunc  create_picker_func,
-                                  gpointer              picker_creator)
+_gimp_prop_gui_new_color_balance (GObject                  *config,
+                                  GParamSpec              **param_specs,
+                                  guint                     n_param_specs,
+                                  GeglRectangle            *area,
+                                  GimpContext              *context,
+                                  GimpCreatePickerFunc      create_picker_func,
+                                  GimpCreateControllerFunc  create_controller_func,
+                                  gpointer                  creator)
 {
   GtkWidget *main_vbox;
   GtkWidget *vbox;
diff --git a/app/widgets/gimppropgui-color-balance.h b/app/widgets/gimppropgui-color-balance.h
index ff3dbe8..cd8e9a5 100644
--- a/app/widgets/gimppropgui-color-balance.h
+++ b/app/widgets/gimppropgui-color-balance.h
@@ -22,13 +22,14 @@
 
 
 GtkWidget *
-_gimp_prop_gui_new_color_balance (GObject              *config,
-                                  GParamSpec          **param_specs,
-                                  guint                 n_param_specs,
-                                  GeglRectangle        *area,
-                                  GimpContext          *context,
-                                  GimpCreatePickerFunc  create_picker_func,
-                                  gpointer              picker_creator);
+_gimp_prop_gui_new_color_balance (GObject                  *config,
+                                  GParamSpec              **param_specs,
+                                  guint                     n_param_specs,
+                                  GeglRectangle            *area,
+                                  GimpContext              *context,
+                                  GimpCreatePickerFunc      create_picker_func,
+                                  GimpCreateControllerFunc  create_controller_func,
+                                  gpointer                  creator);
 
 
 #endif /* __GIMP_PROP_GUI_COLOR_BALANCE_H__ */
diff --git a/app/widgets/gimppropgui-color-rotate.c b/app/widgets/gimppropgui-color-rotate.c
index 9e7e4ea..3a2675d 100644
--- a/app/widgets/gimppropgui-color-rotate.c
+++ b/app/widgets/gimppropgui-color-rotate.c
@@ -189,13 +189,14 @@ gimp_prop_polar_box_new (GObject     *config,
 }
 
 GtkWidget *
-_gimp_prop_gui_new_color_rotate (GObject              *config,
-                                 GParamSpec          **param_specs,
-                                 guint                 n_param_specs,
-                                 GeglRectangle        *area,
-                                 GimpContext          *context,
-                                 GimpCreatePickerFunc  create_picker_func,
-                                 gpointer              picker_creator)
+_gimp_prop_gui_new_color_rotate (GObject                  *config,
+                                 GParamSpec              **param_specs,
+                                 guint                     n_param_specs,
+                                 GeglRectangle            *area,
+                                 GimpContext              *context,
+                                 GimpCreatePickerFunc      create_picker_func,
+                                 GimpCreateControllerFunc  create_controller_func,
+                                 gpointer                  creator)
 {
   GtkWidget *main_vbox;
   GtkWidget *frame;
@@ -242,7 +243,9 @@ _gimp_prop_gui_new_color_rotate (GObject              *config,
   box = _gimp_prop_gui_new_generic (config,
                                     param_specs + 6, 2,
                                     area, context,
-                                    create_picker_func, picker_creator);
+                                    create_picker_func,
+                                    create_controller_func,
+                                    creator);
   gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 0);
   gtk_widget_show (box);
 
diff --git a/app/widgets/gimppropgui-color-rotate.h b/app/widgets/gimppropgui-color-rotate.h
index ba54b9c..be2de88 100644
--- a/app/widgets/gimppropgui-color-rotate.h
+++ b/app/widgets/gimppropgui-color-rotate.h
@@ -22,13 +22,14 @@
 
 
 GtkWidget *
-_gimp_prop_gui_new_color_rotate (GObject              *config,
-                                 GParamSpec          **param_specs,
-                                 guint                 n_param_specs,
-                                 GeglRectangle        *area,
-                                 GimpContext          *context,
-                                 GimpCreatePickerFunc  create_picker_func,
-                                 gpointer              picker_creator);
+_gimp_prop_gui_new_color_rotate (GObject                  *config,
+                                 GParamSpec              **param_specs,
+                                 guint                     n_param_specs,
+                                 GeglRectangle            *area,
+                                 GimpContext              *context,
+                                 GimpCreatePickerFunc      create_picker_func,
+                                 GimpCreateControllerFunc  create_controller_func,
+                                 gpointer                  creator);
 
 
 #endif /* __GIMP_PROP_GUI_COLOR_ROTATE_H__ */
diff --git a/app/widgets/gimppropgui-convolution-matrix.c b/app/widgets/gimppropgui-convolution-matrix.c
index d4a7415..c012765 100644
--- a/app/widgets/gimppropgui-convolution-matrix.c
+++ b/app/widgets/gimppropgui-convolution-matrix.c
@@ -131,13 +131,14 @@ convolution_matrix_rotate_flip (GtkWidget *button,
 }
 
 GtkWidget *
-_gimp_prop_gui_new_convolution_matrix (GObject              *config,
-                                       GParamSpec          **param_specs,
-                                       guint                 n_param_specs,
-                                       GeglRectangle        *area,
-                                       GimpContext          *context,
-                                       GimpCreatePickerFunc  create_picker_func,
-                                       gpointer              picker_creator)
+_gimp_prop_gui_new_convolution_matrix (GObject                  *config,
+                                       GParamSpec              **param_specs,
+                                       guint                     n_param_specs,
+                                       GeglRectangle            *area,
+                                       GimpContext              *context,
+                                       GimpCreatePickerFunc      create_picker_func,
+                                       GimpCreateControllerFunc  create_controller_func,
+                                       gpointer                  creator)
 {
   GtkWidget   *main_vbox;
   GtkWidget   *vbox;
@@ -269,12 +270,12 @@ _gimp_prop_gui_new_convolution_matrix (GObject              *config,
   gtk_widget_show (hbox);
 
   scale = gimp_prop_widget_new (config, "divisor",
-                                area, context, NULL, NULL, &label);
+                                area, context, NULL, NULL, NULL, &label);
   gtk_box_pack_start (GTK_BOX (hbox), scale, TRUE, TRUE, 0);
   gtk_widget_show (scale);
 
   scale = gimp_prop_widget_new (config, "offset",
-                                area, context, NULL, NULL, &label);
+                                area, context, NULL, NULL, NULL, &label);
   gtk_box_pack_start (GTK_BOX (hbox), scale, TRUE, TRUE, 0);
   gtk_widget_show (scale);
 
@@ -288,7 +289,9 @@ _gimp_prop_gui_new_convolution_matrix (GObject              *config,
   vbox2 = _gimp_prop_gui_new_generic (config,
                                       param_specs + 27, 4,
                                       area, context,
-                                      create_picker_func, picker_creator);
+                                      create_picker_func,
+                                      create_controller_func,
+                                      creator);
   gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 0);
   gtk_widget_show (vbox2);
 
@@ -296,7 +299,9 @@ _gimp_prop_gui_new_convolution_matrix (GObject              *config,
                                       param_specs + 31,
                                       n_param_specs - 31,
                                       area, context,
-                                      create_picker_func, picker_creator);
+                                      create_picker_func,
+                                      create_controller_func,
+                                      creator);
   gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 0);
   gtk_widget_show (vbox2);
 
diff --git a/app/widgets/gimppropgui-convolution-matrix.h b/app/widgets/gimppropgui-convolution-matrix.h
index acc7ac9..94165c1 100644
--- a/app/widgets/gimppropgui-convolution-matrix.h
+++ b/app/widgets/gimppropgui-convolution-matrix.h
@@ -22,13 +22,14 @@
 
 
 GtkWidget *
-_gimp_prop_gui_new_convolution_matrix (GObject              *config,
-                                       GParamSpec          **param_specs,
-                                       guint                 n_param_specs,
-                                       GeglRectangle        *area,
-                                       GimpContext          *context,
-                                       GimpCreatePickerFunc  create_picker_func,
-                                       gpointer              picker_creator);
+_gimp_prop_gui_new_convolution_matrix (GObject                  *config,
+                                       GParamSpec              **param_specs,
+                                       guint                     n_param_specs,
+                                       GeglRectangle            *area,
+                                       GimpContext              *context,
+                                       GimpCreatePickerFunc      create_picker_func,
+                                       GimpCreateControllerFunc  create_controller_func,
+                                       gpointer                  creator);
 
 
 #endif /* __GIMP_PROP_GUI_CONVOLUTION_MATRIX_H__ */
diff --git a/app/widgets/gimppropgui-diffration-patterns.c b/app/widgets/gimppropgui-diffration-patterns.c
index e870835..301a2d1 100644
--- a/app/widgets/gimppropgui-diffration-patterns.c
+++ b/app/widgets/gimppropgui-diffration-patterns.c
@@ -38,13 +38,14 @@
 
 
 GtkWidget *
-_gimp_prop_gui_new_diffraction_patterns (GObject              *config,
-                                         GParamSpec          **param_specs,
-                                         guint                 n_param_specs,
-                                         GeglRectangle        *area,
-                                         GimpContext          *context,
-                                         GimpCreatePickerFunc  create_picker_func,
-                                         gpointer              picker_creator)
+_gimp_prop_gui_new_diffraction_patterns (GObject                  *config,
+                                         GParamSpec              **param_specs,
+                                         guint                     n_param_specs,
+                                         GeglRectangle            *area,
+                                         GimpContext              *context,
+                                         GimpCreatePickerFunc      create_picker_func,
+                                         GimpCreateControllerFunc  create_controller_func,
+                                         gpointer                  creator)
 {
   GtkWidget *notebook;
   GtkWidget *vbox;
@@ -59,7 +60,9 @@ _gimp_prop_gui_new_diffraction_patterns (GObject              *config,
   vbox = _gimp_prop_gui_new_generic (config,
                                      param_specs + 0, 3,
                                      area, context,
-                                     create_picker_func, picker_creator);
+                                     create_picker_func,
+                                     create_controller_func,
+                                     creator);
   gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
                             gtk_label_new (_("Frequencies")));
@@ -68,7 +71,9 @@ _gimp_prop_gui_new_diffraction_patterns (GObject              *config,
   vbox = _gimp_prop_gui_new_generic (config,
                                      param_specs + 3, 3,
                                      area, context,
-                                     create_picker_func, picker_creator);
+                                     create_picker_func,
+                                     create_controller_func,
+                                     creator);
   gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
                             gtk_label_new (_("Contours")));
@@ -77,7 +82,9 @@ _gimp_prop_gui_new_diffraction_patterns (GObject              *config,
   vbox = _gimp_prop_gui_new_generic (config,
                                      param_specs + 6, 3,
                                      area, context,
-                                     create_picker_func, picker_creator);
+                                     create_picker_func,
+                                     create_controller_func,
+                                     creator);
   gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
                             gtk_label_new (_("Sharp Edges")));
@@ -86,7 +93,9 @@ _gimp_prop_gui_new_diffraction_patterns (GObject              *config,
   vbox = _gimp_prop_gui_new_generic (config,
                                      param_specs + 9, 3,
                                      area, context,
-                                     create_picker_func, picker_creator);
+                                     create_picker_func,
+                                     create_controller_func,
+                                     creator);
   gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
                             gtk_label_new (_("Other Options")));
diff --git a/app/widgets/gimppropgui-diffration-patterns.h b/app/widgets/gimppropgui-diffration-patterns.h
index b703694..3a9d039 100644
--- a/app/widgets/gimppropgui-diffration-patterns.h
+++ b/app/widgets/gimppropgui-diffration-patterns.h
@@ -22,13 +22,14 @@
 
 
 GtkWidget *
-_gimp_prop_gui_new_diffraction_patterns (GObject              *config,
-                                         GParamSpec          **param_specs,
-                                         guint                 n_param_specs,
-                                         GeglRectangle        *area,
-                                         GimpContext          *context,
-                                         GimpCreatePickerFunc  create_picker_func,
-                                         gpointer              picker_creator);
+_gimp_prop_gui_new_diffraction_patterns (GObject                  *config,
+                                         GParamSpec              **param_specs,
+                                         guint                     n_param_specs,
+                                         GeglRectangle            *area,
+                                         GimpContext              *context,
+                                         GimpCreatePickerFunc      create_picker_func,
+                                         GimpCreateControllerFunc  create_controller_func,
+                                         gpointer                  creator);
 
 
 #endif /* __GIMP_PROP_GUI_DIFFRATION_PATTERNS_H__ */
diff --git a/app/widgets/gimppropgui-generic.c b/app/widgets/gimppropgui-generic.c
index 23d13d9..805beea 100644
--- a/app/widgets/gimppropgui-generic.c
+++ b/app/widgets/gimppropgui-generic.c
@@ -56,13 +56,14 @@ static void   gimp_prop_gui_chain_toggled (GimpChainButton *chain,
 /*  public functions  */
 
 GtkWidget *
-_gimp_prop_gui_new_generic (GObject              *config,
-                            GParamSpec          **param_specs,
-                            guint                 n_param_specs,
-                            GeglRectangle        *area,
-                            GimpContext          *context,
-                            GimpCreatePickerFunc  create_picker_func,
-                            gpointer              picker_creator)
+_gimp_prop_gui_new_generic (GObject                  *config,
+                            GParamSpec              **param_specs,
+                            guint                     n_param_specs,
+                            GeglRectangle            *area,
+                            GimpContext              *context,
+                            GimpCreatePickerFunc      create_picker_func,
+                            GimpCreateControllerFunc  create_controller_func,
+                            gpointer                  creator)
 {
   GtkWidget    *main_vbox;
   GtkSizeGroup *label_group;
@@ -104,12 +105,14 @@ _gimp_prop_gui_new_generic (GObject              *config,
           widget_x = gimp_prop_widget_new_from_pspec (config, pspec,
                                                       area, context,
                                                       create_picker_func,
-                                                      picker_creator,
+                                                      create_controller_func,
+                                                      creator,
                                                       &label_x);
           widget_y = gimp_prop_widget_new_from_pspec (config, next_pspec,
                                                       area, context,
                                                       create_picker_func,
-                                                      picker_creator,
+                                                      create_controller_func,
+                                                      creator,
                                                       &label_y);
 
           adj_x = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (widget_x));
@@ -167,7 +170,7 @@ _gimp_prop_gui_new_generic (GObject              *config,
               pspec_name = g_strconcat (pspec->name, ":",
                                         next_pspec->name, NULL);
 
-              button = create_picker_func (picker_creator,
+              button = create_picker_func (creator,
                                            pspec_name,
                                            GIMP_ICON_CURSOR,
                                            _("Pick coordinates from the image"),
@@ -188,7 +191,8 @@ _gimp_prop_gui_new_generic (GObject              *config,
           widget = gimp_prop_widget_new_from_pspec (config, pspec,
                                                     area, context,
                                                     create_picker_func,
-                                                    picker_creator,
+                                                    create_controller_func,
+                                                    creator,
                                                     &label);
 
           if (GTK_IS_SCROLLED_WINDOW (widget))
diff --git a/app/widgets/gimppropgui-generic.h b/app/widgets/gimppropgui-generic.h
index cfd9ab6..52c4f4f 100644
--- a/app/widgets/gimppropgui-generic.h
+++ b/app/widgets/gimppropgui-generic.h
@@ -23,13 +23,14 @@
 #define __GIMP_PROP_GUI_GENERIC_H__
 
 
-GtkWidget * _gimp_prop_gui_new_generic (GObject              *config,
-                                        GParamSpec          **param_specs,
-                                        guint                 n_param_specs,
-                                        GeglRectangle        *area,
-                                        GimpContext          *context,
-                                        GimpCreatePickerFunc  create_picker_func,
-                                        gpointer              picker_creator);
+GtkWidget * _gimp_prop_gui_new_generic (GObject                  *config,
+                                        GParamSpec              **param_specs,
+                                        guint                     n_param_specs,
+                                        GeglRectangle            *area,
+                                        GimpContext              *context,
+                                        GimpCreatePickerFunc      create_picker_func,
+                                        GimpCreateControllerFunc  create_controller_func,
+                                        gpointer                  creator);
 
 
 #endif /* __GIMP_PROP_GUI_GENERIC_H__ */
diff --git a/app/widgets/gimppropgui-hue-saturation.c b/app/widgets/gimppropgui-hue-saturation.c
index df54137..21d2d53 100644
--- a/app/widgets/gimppropgui-hue-saturation.c
+++ b/app/widgets/gimppropgui-hue-saturation.c
@@ -98,13 +98,14 @@ hue_saturation_range_notify (GObject          *object,
 }
 
 GtkWidget *
-_gimp_prop_gui_new_hue_saturation (GObject              *config,
-                                   GParamSpec          **param_specs,
-                                   guint                 n_param_specs,
-                                   GeglRectangle        *area,
-                                   GimpContext          *context,
-                                   GimpCreatePickerFunc  create_picker_func,
-                                   gpointer              picker_creator)
+_gimp_prop_gui_new_hue_saturation (GObject                  *config,
+                                   GParamSpec              **param_specs,
+                                   guint                     n_param_specs,
+                                   GeglRectangle            *area,
+                                   GimpContext              *context,
+                                   GimpCreatePickerFunc      create_picker_func,
+                                   GimpCreateControllerFunc  create_controller_func,
+                                   gpointer                  creator)
 {
   GtkWidget *main_vbox;
   GtkWidget *frame;
diff --git a/app/widgets/gimppropgui-hue-saturation.h b/app/widgets/gimppropgui-hue-saturation.h
index e349e2e..d56c6f4 100644
--- a/app/widgets/gimppropgui-hue-saturation.h
+++ b/app/widgets/gimppropgui-hue-saturation.h
@@ -22,13 +22,14 @@
 
 
 GtkWidget *
-_gimp_prop_gui_new_hue_saturation (GObject              *config,
-                                   GParamSpec          **param_specs,
-                                   guint                 n_param_specs,
-                                   GeglRectangle        *area,
-                                   GimpContext          *context,
-                                   GimpCreatePickerFunc  create_picker_func,
-                                   gpointer              picker_creator);
+_gimp_prop_gui_new_hue_saturation (GObject                  *config,
+                                   GParamSpec              **param_specs,
+                                   guint                     n_param_specs,
+                                   GeglRectangle            *area,
+                                   GimpContext              *context,
+                                   GimpCreatePickerFunc      create_picker_func,
+                                   GimpCreateControllerFunc  create_controller_func,
+                                   gpointer                  creator);
 
 
 #endif /* __GIMP_PROP_GUI_HUE_SATURATION_H__ */
diff --git a/app/widgets/gimppropgui-spiral.c b/app/widgets/gimppropgui-spiral.c
new file mode 100644
index 0000000..a0d652c
--- /dev/null
+++ b/app/widgets/gimppropgui-spiral.c
@@ -0,0 +1,152 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-spiral.c
+ * Copyright (C) 2017  Michael Natterer <mitch 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 "libgimpmath/gimpmath.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "widgets-types.h"
+
+#include "core/gimpcontext.h"
+
+#include "gimppropgui.h"
+#include "gimppropgui-generic.h"
+#include "gimppropgui-spiral.h"
+
+#include "gimp-intl.h"
+
+
+static void
+line_callback (GObject       *config,
+               GeglRectangle *area,
+               gdouble        x1,
+               gdouble        y1,
+               gdouble        x2,
+               gdouble        y2)
+{
+  gdouble x, y;
+  gdouble radius;
+  gdouble rotation;
+
+  g_object_set_data_full (G_OBJECT (config), "area",
+                          g_memdup (area, sizeof (GeglRectangle)),
+                          (GDestroyNotify) g_free);
+
+  x        = x1 / area->width;
+  y        = y1 / area->height;
+  radius   = sqrt (SQR (x2 - x1) + SQR (y2 - y1));
+  rotation = atan2 (x2 - x1, y2 - y1) * 180 / G_PI;
+
+  if (rotation < 0)
+    rotation += 360.0;
+
+  g_object_set (config,
+                "x",        x,
+                "y",        y,
+                "radius",   radius,
+                "rotation", rotation,
+                NULL);
+}
+
+static void
+config_notify (GObject          *config,
+               const GParamSpec *pspec,
+               gpointer          set_data)
+{
+  GimpControllerLineCallback  set_func;
+  GeglRectangle              *area;
+  gdouble                     x, y;
+  gdouble                     radius;
+  gdouble                     rotation;
+  gdouble                     x1, y1, x2, y2;
+
+  set_func = g_object_get_data (G_OBJECT (config), "set-func");
+  area     = g_object_get_data (G_OBJECT (config), "area");
+
+  g_object_get (config,
+                "x",        &x,
+                "y",        &y,
+                "radius",   &radius,
+                "rotation", &rotation,
+                NULL);
+
+  x1 = x * area->width;
+  y1 = y * area->height;
+  x2 = x1 + sin (rotation * (G_PI / 180.0)) * radius;
+  y2 = y1 + cos (rotation * (G_PI / 180.0)) * radius;
+
+  set_func (set_data, area, x1, y1, x2, y2);
+}
+
+GtkWidget *
+_gimp_prop_gui_new_spiral (GObject                  *config,
+                           GParamSpec              **param_specs,
+                           guint                     n_param_specs,
+                           GeglRectangle            *area,
+                           GimpContext              *context,
+                           GimpCreatePickerFunc      create_picker_func,
+                           GimpCreateControllerFunc  create_controller_func,
+                           gpointer                  creator)
+{
+  GtkWidget *vbox;
+
+  g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+  g_return_val_if_fail (param_specs != NULL, NULL);
+  g_return_val_if_fail (n_param_specs > 0, NULL);
+  g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+
+  vbox = _gimp_prop_gui_new_generic (config,
+                                     param_specs, n_param_specs,
+                                     area, context,
+                                     create_picker_func,
+                                     create_controller_func,
+                                     creator);
+
+
+  if (create_controller_func)
+    {
+      GCallback set_func;
+      gpointer  set_data;
+
+      set_func = create_controller_func (creator,
+                                         GIMP_CONTROLLER_TYPE_LINE,
+                                         (GCallback) line_callback,
+                                         config,
+                                         &set_data);
+
+      g_object_set_data (G_OBJECT (config), "set-func", set_func);
+
+      g_object_set_data_full (G_OBJECT (config), "area",
+                              g_memdup (area, sizeof (GeglRectangle)),
+                              (GDestroyNotify) g_free);
+
+      config_notify (config, NULL, set_data);
+
+      g_signal_connect (config, "notify",
+                        G_CALLBACK (config_notify),
+                        set_data);
+    }
+
+  return vbox;
+}
diff --git a/app/widgets/gimppropgui-spiral.h b/app/widgets/gimppropgui-spiral.h
new file mode 100644
index 0000000..fef7ab9
--- /dev/null
+++ b/app/widgets/gimppropgui-spiral.h
@@ -0,0 +1,35 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-spiral.h
+ *
+ * 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_PROP_GUI_SPIRAL_H__
+#define __GIMP_PROP_GUI_SPIRAL_H__
+
+
+GtkWidget *
+_gimp_prop_gui_new_spiral (GObject                  *config,
+                           GParamSpec              **param_specs,
+                           guint                     n_param_specs,
+                           GeglRectangle            *area,
+                           GimpContext              *context,
+                           GimpCreatePickerFunc      create_picker_func,
+                           GimpCreateControllerFunc  create_controller_func,
+                           gpointer                  creator);
+
+
+#endif /* __GIMP_PROP_GUI_SPIRAL_H__ */
diff --git a/app/widgets/gimppropgui.c b/app/widgets/gimppropgui.c
index 2c1e281..3f30233 100644
--- a/app/widgets/gimppropgui.c
+++ b/app/widgets/gimppropgui.c
@@ -2,7 +2,7 @@
  * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
  *
  * gimppropgui.c
- * Copyright (C) 2002-2014  Michael Natterer <mitch gimp org>
+ * Copyright (C) 2002-2017  Michael Natterer <mitch gimp org>
  *                          Sven Neumann <sven gimp org>
  *
  * This program is free software: you can redistribute it and/or modify
@@ -48,6 +48,7 @@
 #include "gimppropgui-color-rotate.h"
 #include "gimppropgui-convolution-matrix.h"
 #include "gimppropgui-diffration-patterns.h"
+#include "gimppropgui-spiral.h"
 #include "gimppropgui-eval.h"
 #include "gimppropgui-generic.h"
 #include "gimppropgui-hue-saturation.h"
@@ -78,13 +79,14 @@ static void          gimp_prop_free_label_ref          (GWeakRef       *label_re
 /*  public functions  */
 
 GtkWidget *
-gimp_prop_widget_new (GObject              *config,
-                      const gchar          *property_name,
-                      GeglRectangle        *area,
-                      GimpContext          *context,
-                      GimpCreatePickerFunc  create_picker_func,
-                      gpointer              picker_creator,
-                      const gchar         **label)
+gimp_prop_widget_new (GObject                  *config,
+                      const gchar              *property_name,
+                      GeglRectangle            *area,
+                      GimpContext              *context,
+                      GimpCreatePickerFunc      create_picker_func,
+                      GimpCreateControllerFunc  create_controller_func,
+                      gpointer                  creator,
+                      const gchar             **label)
 {
   GParamSpec *pspec;
 
@@ -94,18 +96,21 @@ gimp_prop_widget_new (GObject              *config,
                                         property_name);
 
   return gimp_prop_widget_new_from_pspec (config, pspec, area, context,
-                                          create_picker_func, picker_creator,
+                                          create_picker_func,
+                                          create_controller_func,
+                                          creator,
                                           label);
 }
 
 GtkWidget *
-gimp_prop_widget_new_from_pspec (GObject               *config,
-                                 GParamSpec            *pspec,
-                                 GeglRectangle         *area,
-                                 GimpContext           *context,
-                                 GimpCreatePickerFunc   create_picker_func,
-                                 gpointer               picker_creator,
-                                 const gchar          **label)
+gimp_prop_widget_new_from_pspec (GObject                  *config,
+                                 GParamSpec               *pspec,
+                                 GeglRectangle            *area,
+                                 GimpContext              *context,
+                                 GimpCreatePickerFunc      create_picker_func,
+                                 GimpCreateControllerFunc  create_controller_func,
+                                 gpointer                  creator,
+                                 const gchar             **label)
 {
   GtkWidget *widget = NULL;
 
@@ -370,7 +375,7 @@ gimp_prop_widget_new_from_pspec (GObject               *config,
 
       if (create_picker_func)
         {
-          button = create_picker_func (picker_creator,
+          button = create_picker_func (creator,
                                        pspec->name,
                                        GIMP_ICON_COLOR_PICKER_GRAY,
                                        _("Pick color from the image"),
@@ -421,13 +426,14 @@ gimp_prop_widget_new_from_pspec (GObject               *config,
 }
 
 
-typedef GtkWidget * (* GimpPropGuiNewFunc) (GObject              *config,
-                                            GParamSpec          **param_specs,
-                                            guint                 n_param_specs,
-                                            GeglRectangle        *area,
-                                            GimpContext          *context,
-                                            GimpCreatePickerFunc  create_picker_func,
-                                            gpointer              picker_creator);
+typedef GtkWidget * (* GimpPropGuiNewFunc) (GObject                  *config,
+                                            GParamSpec              **param_specs,
+                                            guint                     n_param_specs,
+                                            GeglRectangle            *area,
+                                            GimpContext              *context,
+                                            GimpCreatePickerFunc      create_picker_func,
+                                            GimpCreateControllerFunc  create_controller_func,
+                                            gpointer                  creator);
 
 static const struct
 {
@@ -449,19 +455,22 @@ gui_new_funcs[] =
     _gimp_prop_gui_new_channel_mixer },
   { "GimpGegl-gegl-diffraction-patterns-config",
     _gimp_prop_gui_new_diffraction_patterns },
+  { "GimpGegl-gegl-spiral-config",
+    _gimp_prop_gui_new_spiral },
   { NULL,
     _gimp_prop_gui_new_generic }
 };
 
 
 GtkWidget *
-gimp_prop_gui_new (GObject              *config,
-                   GType                 owner_type,
-                   GParamFlags           flags,
-                   GeglRectangle        *area,
-                   GimpContext          *context,
-                   GimpCreatePickerFunc  create_picker_func,
-                   gpointer              picker_creator)
+gimp_prop_gui_new (GObject                  *config,
+                   GType                     owner_type,
+                   GParamFlags               flags,
+                   GeglRectangle            *area,
+                   GimpContext              *context,
+                   GimpCreatePickerFunc      create_picker_func,
+                   GimpCreateControllerFunc  create_controller_func,
+                   gpointer                  creator)
 {
   GtkWidget    *gui = NULL;
   GParamSpec  **param_specs;
@@ -512,7 +521,8 @@ gimp_prop_gui_new (GObject              *config,
                                                    area,
                                                    context,
                                                    create_picker_func,
-                                                   picker_creator);
+                                                   create_controller_func,
+                                                   creator);
               break;
             }
         }
diff --git a/app/widgets/gimppropgui.h b/app/widgets/gimppropgui.h
index 9a1649a..2cd6730 100644
--- a/app/widgets/gimppropgui.h
+++ b/app/widgets/gimppropgui.h
@@ -2,7 +2,7 @@
  * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
  *
  * gimppropgui.h
- * Copyright (C) 2002-2014 Michael Natterer <mitch gimp org>
+ * Copyright (C) 2002-2017 Michael Natterer <mitch 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
@@ -24,40 +24,63 @@
 
 /*  A view on all of an object's properties  */
 
-typedef GtkWidget * (* GimpCreatePickerFunc) (gpointer     creator,
-                                              const gchar *property_name,
-                                              const gchar *icon_name,
-                                              const gchar *tooltip,
-                                              gboolean     pick_abyss);
-
-GtkWidget * gimp_prop_widget_new            (GObject              *config,
-                                             const gchar          *property_name,
-                                             GeglRectangle        *area,
-                                             GimpContext          *context,
-                                             GimpCreatePickerFunc  create_picker,
-                                             gpointer              picker_creator,
-                                             const gchar         **label);
-GtkWidget * gimp_prop_widget_new_from_pspec (GObject              *config,
-                                             GParamSpec           *pspec,
-                                             GeglRectangle        *area,
-                                             GimpContext          *context,
-                                             GimpCreatePickerFunc  create_picker,
-                                             gpointer              picker_creator,
-                                             const gchar         **label);
-GtkWidget * gimp_prop_gui_new               (GObject              *config,
-                                             GType                 owner_type,
-                                             GParamFlags           flags,
-                                             GeglRectangle        *area,
-                                             GimpContext          *context,
-                                             GimpCreatePickerFunc  create_picker,
-                                             gpointer              picker_creator);
-
-void        gimp_prop_gui_bind_container    (GtkWidget            *source,
-                                             GtkWidget            *target);
-void        gimp_prop_gui_bind_label        (GtkWidget            *source,
-                                             GtkWidget            *target);
-void        gimp_prop_gui_bind_tooltip      (GtkWidget            *source,
-                                             GtkWidget            *target);
+typedef enum
+{
+  GIMP_CONTROLLER_TYPE_LINE
+} GimpControllerType;
+
+typedef void (* GimpControllerLineCallback) (gpointer        data,
+                                             GeglRectangle  *area,
+                                             gdouble         x1,
+                                             gdouble         y1,
+                                             gdouble         x2,
+                                             gdouble         y2);
+
+
+typedef GtkWidget * (* GimpCreatePickerFunc)     (gpointer            creator,
+                                                  const gchar        *property_name,
+                                                  const gchar        *icon_name,
+                                                  const gchar        *tooltip,
+                                                  gboolean            pick_abyss);
+
+typedef GCallback   (* GimpCreateControllerFunc) (gpointer            creator,
+                                                  GimpControllerType  controller_type,
+                                                  GCallback           callback,
+                                                  gpointer            callback_data,
+                                                  gpointer           *set_func_data);
+
+
+GtkWidget * gimp_prop_widget_new            (GObject                 *config,
+                                             const gchar             *property_name,
+                                             GeglRectangle           *area,
+                                             GimpContext             *context,
+                                             GimpCreatePickerFunc     create_picker,
+                                             GimpCreateControllerFunc create_controller,
+                                             gpointer                 creator,
+                                             const gchar            **label);
+GtkWidget * gimp_prop_widget_new_from_pspec (GObject                 *config,
+                                             GParamSpec              *pspec,
+                                             GeglRectangle           *area,
+                                             GimpContext             *context,
+                                             GimpCreatePickerFunc     create_picker,
+                                             GimpCreateControllerFunc create_controller,
+                                             gpointer                 creator,
+                                             const gchar            **label);
+GtkWidget * gimp_prop_gui_new               (GObject                 *config,
+                                             GType                    owner_type,
+                                             GParamFlags              flags,
+                                             GeglRectangle           *area,
+                                             GimpContext             *context,
+                                             GimpCreatePickerFunc     create_picker,
+                                             GimpCreateControllerFunc create_controller,
+                                             gpointer                 creator);
+
+void        gimp_prop_gui_bind_container    (GtkWidget               *source,
+                                             GtkWidget               *target);
+void        gimp_prop_gui_bind_label        (GtkWidget               *source,
+                                             GtkWidget               *target);
+void        gimp_prop_gui_bind_tooltip      (GtkWidget               *source,
+                                             GtkWidget               *target);
 
 
 #endif /* __GIMP_PROP_GUI_H__ */
diff --git a/app/widgets/gimpsymmetryeditor.c b/app/widgets/gimpsymmetryeditor.c
index 108d6fd..21f5a66 100644
--- a/app/widgets/gimpsymmetryeditor.c
+++ b/app/widgets/gimpsymmetryeditor.c
@@ -251,7 +251,7 @@ gimp_symmetry_editor_set_options (GimpSymmetryEditor *editor,
                                                gimp_image_get_width  (image),
                                                gimp_image_get_height (image)),
                                GIMP_IMAGE_EDITOR (editor)->context,
-                               NULL, NULL);
+                               NULL, NULL, NULL);
       gtk_box_pack_start (GTK_BOX (editor->p->options_vbox), gui,
                           FALSE, FALSE, 0);
       gtk_widget_show (gui);


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