[gimp] app, libgimpwidgets: allowing changing a dockable settings value while…



commit 622d0da43ee19ac993a87b8d36f85382aa858f9d
Author: Jehan <jehan girinstud io>
Date:   Sun Mar 6 14:31:27 2022 +0100

    app, libgimpwidgets: allowing changing a dockable settings value while…
    
    … blinking it.
    
    This will be necessary to demo new features available only in some
    situations. E.g. a new option in line art detection mode of bucket fill
    would require said mode to be enabled.

 app/dialogs/welcome-dialog.c         |  27 +++-
 app/tools/gimpbucketfilltool.c       |   2 +-
 app/widgets/gimppropwidgets.c        |  28 ++--
 app/widgets/gimpwidgets-utils.c      | 274 ++++++++++++++++++++++++++++-------
 app/widgets/gimpwidgets-utils.h      |   2 +
 libgimpwidgets/gimppropwidgets.c     |  66 ++++-----
 libgimpwidgets/gimpwidgets-private.c |  42 ++++++
 libgimpwidgets/gimpwidgets-private.h |  19 ++-
 8 files changed, 343 insertions(+), 117 deletions(-)
---
diff --git a/app/dialogs/welcome-dialog.c b/app/dialogs/welcome-dialog.c
index 642e9b3e31..302d33bf41 100644
--- a/app/dialogs/welcome-dialog.c
+++ b/app/dialogs/welcome-dialog.c
@@ -525,8 +525,10 @@ welcome_dialog_release_item_activated (GtkListBox    *listbox,
   for (i = 0; script_steps[i]; i++)
     {
       gchar **ids;
-      gchar  *dockable_id = NULL;
-      gchar  *widget_id   = NULL;
+      gchar  *dockable_id    = NULL;
+      gchar  *widget_id      = NULL;
+      gchar **settings       = NULL;
+      gchar  *settings_value = NULL;
 
       ids = g_strsplit (script_steps[i], ":", 2);
       /* Even if the string doesn't contain a second part, it is
@@ -536,6 +538,14 @@ welcome_dialog_release_item_activated (GtkListBox    *listbox,
       dockable_id = ids[0];
       widget_id   = ids[1];
 
+      if (widget_id != NULL)
+        {
+          settings = g_strsplit (widget_id, "=", 2);
+
+          widget_id = settings[0];
+          settings_value = settings[1];
+        }
+
       /* Allowing white spaces so that the demo in XML metadata can be
        * spaced-out or even on multiple lines for clarity.
        */
@@ -559,27 +569,30 @@ welcome_dialog_release_item_activated (GtkListBox    *listbox,
           /* All tool button IDs start with "tools-". This allows to
            * write shorter tool names in the demo script.
            */
-          if (! g_str_has_prefix (widget_id, "tools-"))
+          if (widget_id != NULL && ! g_str_has_prefix (widget_id, "tools-"))
             {
               gchar *tmp = g_strdup_printf ("tools-%s", widget_id);
 
-              g_free (ids[1]);
-              widget_id = ids[1] = tmp;
+              g_free (settings[0]);
+              widget_id = settings[0] = tmp;
             }
 
           gimp_blink_toolbox (gimp, widget_id, &blink_script);
         }
       else
         {
-          gimp_blink_dockable (gimp, dockable_id, widget_id, &blink_script);
+          gimp_blink_dockable (gimp, dockable_id,
+                               widget_id, settings_value,
+                               &blink_script);
         }
 
       g_strfreev (ids);
+      if (settings)
+        g_strfreev (settings);
     }
   if (blink_script != NULL)
     gimp_blink_play_script (blink_script);
 
-  g_list_free (blink_script);
   g_strfreev (script_steps);
 }
 
diff --git a/app/tools/gimpbucketfilltool.c b/app/tools/gimpbucketfilltool.c
index aaffba4724..e44206c617 100644
--- a/app/tools/gimpbucketfilltool.c
+++ b/app/tools/gimpbucketfilltool.c
@@ -632,7 +632,7 @@ gimp_bucket_fill_tool_button_press (GimpTool            *tool,
     {
       gimp_tool_message_literal (tool, display,
                                  _("No valid line art source selected."));
-      gimp_blink_dockable (display->gimp, "gimp-tool-options", "line-art-source", NULL);
+      gimp_blink_dockable (display->gimp, "gimp-tool-options", "line-art-source", NULL, NULL);
       return;
     }
 
diff --git a/app/widgets/gimppropwidgets.c b/app/widgets/gimppropwidgets.c
index 9f02e5d3c6..6975b660f0 100644
--- a/app/widgets/gimppropwidgets.c
+++ b/app/widgets/gimppropwidgets.c
@@ -140,7 +140,7 @@ gimp_prop_expanding_frame_new (GObject      *config,
   if (button)
     *button = toggle;
 
-  gimp_widget_set_identifier (frame, property_name);
+  gimp_widget_set_bound_property (frame, config, property_name);
   gtk_widget_show (frame);
 
   return frame;
@@ -243,7 +243,7 @@ gimp_prop_boolean_icon_box_new (GObject     *config,
                   G_CALLBACK (gimp_prop_radio_button_notify),
                   button);
 
-  gimp_widget_set_identifier (box, property_name);
+  gimp_widget_set_bound_property (box, config, property_name);
   gtk_widget_show (box);
 
   return box;
@@ -326,7 +326,7 @@ gimp_prop_layer_mode_box_new (GObject              *config,
                           G_BINDING_BIDIRECTIONAL |
                           G_BINDING_SYNC_CREATE);
 
-  gimp_widget_set_identifier (box, property_name);
+  gimp_widget_set_bound_property (box, config, property_name);
   gtk_widget_show (box);
 
   return box;
@@ -398,7 +398,7 @@ gimp_prop_color_button_new (GObject           *config,
                   G_CALLBACK (gimp_prop_color_button_notify),
                   button);
 
-  gimp_widget_set_identifier (button, property_name);
+  gimp_widget_set_bound_property (button, config, property_name);
   gtk_widget_show (button);
 
   return button;
@@ -566,7 +566,7 @@ gimp_prop_angle_dial_new (GObject     *config,
                                    l, (GDestroyNotify) g_free);
     }
 
-  gimp_widget_set_identifier (dial, property_name);
+  gimp_widget_set_bound_property (dial, config, property_name);
   gtk_widget_show (dial);
 
   return dial;
@@ -624,7 +624,7 @@ gimp_prop_angle_range_dial_new (GObject     *config,
                           G_BINDING_BIDIRECTIONAL |
                           G_BINDING_SYNC_CREATE);
 
-  gimp_widget_set_identifier (dial, alpha_property_name);
+  gimp_widget_set_bound_property (dial, config, alpha_property_name);
   gtk_widget_show (dial);
 
   return dial;
@@ -668,7 +668,7 @@ gimp_prop_polar_new (GObject     *config,
                           G_BINDING_BIDIRECTIONAL |
                           G_BINDING_SYNC_CREATE);
 
-  gimp_widget_set_identifier (polar, angle_property_name);
+  gimp_widget_set_bound_property (polar, config, angle_property_name);
   gtk_widget_show (polar);
 
   return polar;
@@ -742,7 +742,7 @@ gimp_prop_range_new (GObject     *config,
   if (sorted)
     gimp_gtk_adjustment_chain (adjustment1, adjustment2);
 
-  gimp_widget_set_identifier (vbox, lower_property_name);
+  gimp_widget_set_bound_property (vbox, config, lower_property_name);
   gtk_widget_show (vbox);
 
   return vbox;
@@ -844,7 +844,7 @@ gimp_prop_view_new (GObject     *config,
                   G_CALLBACK (gimp_prop_view_notify),
                   view);
 
-  gimp_widget_set_identifier (view, property_name);
+  gimp_widget_set_bound_property (view, config, property_name);
   gtk_widget_show (view);
 
   return view;
@@ -1045,7 +1045,7 @@ gimp_prop_number_pair_entry_new (GObject     *config,
                   G_CALLBACK (gimp_prop_number_pair_entry_config_notify),
                   number_pair_entry);
 
-  gimp_widget_set_identifier (number_pair_entry, left_number_property);
+  gimp_widget_set_bound_property (number_pair_entry, config, left_number_property);
   gtk_widget_show (number_pair_entry);
 
   return number_pair_entry;
@@ -1188,7 +1188,7 @@ gimp_prop_language_combo_box_new (GObject     *config,
                   G_CALLBACK (gimp_prop_language_combo_box_notify),
                   combo);
 
-  gimp_widget_set_identifier (combo, property_name);
+  gimp_widget_set_bound_property (combo, config, property_name);
   gtk_widget_show (combo);
 
   return combo;
@@ -1289,7 +1289,7 @@ gimp_prop_language_entry_new (GObject     *config,
                   G_CALLBACK (gimp_prop_language_entry_notify),
                   entry);
 
-  gimp_widget_set_identifier (entry, property_name);
+  gimp_widget_set_bound_property (entry, config, property_name);
   gtk_widget_show (entry);
 
   return entry;
@@ -1444,7 +1444,7 @@ gimp_prop_profile_combo_box_new (GObject      *config,
                   G_CALLBACK (gimp_prop_profile_combo_notify),
                   combo);
 
-  gimp_widget_set_identifier (combo, property_name);
+  gimp_widget_set_bound_property (combo, config, property_name);
   gtk_widget_show (combo);
 
   return combo;
@@ -1584,7 +1584,7 @@ gimp_prop_compression_combo_box_new (GObject     *config,
                   G_CALLBACK (gimp_prop_compression_combo_box_notify),
                   combo);
 
-  gimp_widget_set_identifier (combo, property_name);
+  gimp_widget_set_bound_property (combo, config, property_name);
   gtk_widget_show (combo);
 
   return combo;
diff --git a/app/widgets/gimpwidgets-utils.c b/app/widgets/gimpwidgets-utils.c
index 2aed53ab82..cbd62b647f 100644
--- a/app/widgets/gimpwidgets-utils.c
+++ b/app/widgets/gimpwidgets-utils.c
@@ -76,11 +76,20 @@ typedef struct
 {
   GList       **blink_script;
   const gchar  *widget_identifier;
-} BlinkData;
+  const gchar  *settings_value;
+} BlinkSearch;
 
+typedef struct
+{
+  GtkWidget *widget;
+  gchar     *settings_value;
+} BlinkStep;
 
-static void         gimp_search_widget_rec         (GtkWidget            *widget,
-                                                    BlinkData            *data);
+static void         gimp_widget_blink_after        (GtkWidget   *widget,
+                                                    gint         ms_timeout);
+static void         gimp_search_widget_rec         (GtkWidget   *widget,
+                                                    BlinkSearch *data);
+static void         gimp_blink_free_script         (GList       *blink_scenario);
 
 
 GtkWidget *
@@ -1404,6 +1413,28 @@ widget_blink_free (WidgetBlink *blink)
   g_slice_free (WidgetBlink, blink);
 }
 
+static gboolean
+gimp_widget_blink_start_timeout (GtkWidget *widget)
+{
+  WidgetBlink *blink;
+
+  blink = g_object_get_data (G_OBJECT (widget), "gimp-widget-blink");
+  if (blink)
+    {
+      blink->timeout_id = 0;
+      gimp_widget_blink (widget);
+    }
+  else
+    {
+      /* If the data is not here anymore, our blink has been canceled
+       * already. Also delete the script, if any.
+       */
+      g_object_set_data (G_OBJECT (widget), "gimp-widget-blink-script", NULL);
+    }
+
+  return G_SOURCE_REMOVE;
+}
+
 static gboolean
 gimp_widget_blink_timeout (GtkWidget *widget)
 {
@@ -1416,7 +1447,75 @@ gimp_widget_blink_timeout (GtkWidget *widget)
   gimp_highlight_widget (widget, blink->counter % 2 == 1, blink->rect);
   blink->counter++;
 
-  if (blink->counter == 3)
+  if (blink->counter == 1)
+    {
+      if (script)
+        {
+          BlinkStep *step = script->data;
+
+          if (step->settings_value)
+            {
+              const gchar *prop_name;
+              GObject     *config;
+              GParamSpec  *param_spec;
+
+              prop_name = g_object_get_data (G_OBJECT (widget),
+                                             "gimp-widget-property-name");
+              config    = g_object_get_data (G_OBJECT (widget),
+                                             "gimp-widget-property-config");
+
+              if (config && G_IS_OBJECT (config) && prop_name)
+                {
+                  param_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (config),
+                                                             prop_name);
+                  if (! param_spec)
+                    {
+                      g_printerr ("%s: %s has no property named '%s'.\n",
+                                  G_STRFUNC,
+                                  g_type_name (G_TYPE_FROM_INSTANCE (config)),
+                                  prop_name);
+                      return G_SOURCE_CONTINUE;
+                    }
+                  if (! (param_spec->flags & G_PARAM_WRITABLE))
+                    {
+                      g_printerr ("%s: property '%s' of %s is not writable.\n",
+                                  G_STRFUNC,
+                                  param_spec->name,
+                                  g_type_name (param_spec->owner_type));
+                      return G_SOURCE_CONTINUE;
+                    }
+                  if (g_type_is_a (G_TYPE_FROM_INSTANCE (param_spec), G_TYPE_PARAM_ENUM) ||
+                      g_type_is_a (G_TYPE_FROM_INSTANCE (param_spec), G_TYPE_PARAM_INT)  ||
+                      g_type_is_a (G_TYPE_FROM_INSTANCE (param_spec), G_TYPE_PARAM_BOOLEAN))
+                    {
+                      gchar  *endptr;
+                      gint64  enum_value;
+
+                      enum_value = g_ascii_strtoll (step->settings_value, &endptr, 10);
+                      if (enum_value == 0 && endptr == step->settings_value)
+                        {
+                          g_printerr ("%s: settings value '%s' cannot properly be converted to int.\n",
+                                      G_STRFUNC, step->settings_value);
+                          return G_SOURCE_CONTINUE;
+                        }
+
+                      g_object_set (config,
+                                    prop_name, enum_value,
+                                    NULL);
+                    }
+                  else
+                    {
+                      g_printerr ("%s: currently unsupported type '%s' for property %s of %s.\n",
+                                  G_STRFUNC,
+                                  g_type_name (G_TYPE_FROM_INSTANCE (param_spec)),
+                                  param_spec->name,
+                                  g_type_name (param_spec->owner_type));
+                    }
+                }
+            }
+        }
+    }
+  else if (blink->counter == 3)
     {
       blink->timeout_id = 0;
 
@@ -1424,14 +1523,19 @@ gimp_widget_blink_timeout (GtkWidget *widget)
 
       if (script)
         {
-          GtkWidget *next_widget = script->data;
-
           if (script->next)
-            g_object_set_data_full (G_OBJECT (next_widget), "gimp-widget-blink-script",
-                                    g_list_copy (script->next),
-                                    (GDestroyNotify) g_list_free);
+            {
+              BlinkStep *next_step   = script->next->data;
+              GtkWidget *next_widget = next_step->widget;
 
-          gimp_widget_blink (next_widget);
+              g_object_set_data_full (G_OBJECT (next_widget), "gimp-widget-blink-script",
+                                      script->next,
+                                      (GDestroyNotify) gimp_blink_free_script);
+              script->next->prev = NULL;
+              script->next       = NULL;
+
+              gimp_widget_blink_after (next_widget, 500);
+            }
 
           g_object_set_data (G_OBJECT (widget), "gimp-widget-blink-script", NULL);
         }
@@ -1467,25 +1571,42 @@ gimp_widget_blink (GtkWidget *widget)
 }
 
 void
-gimp_widget_script_blink (GtkWidget  *widget,
-                          GList     **blink_scenario)
+gimp_widget_script_blink (GtkWidget    *widget,
+                          const gchar  *settings_value,
+                          GList       **blink_scenario)
 {
-  *blink_scenario = g_list_append (*blink_scenario, widget);
+  BlinkStep *step;
+
+  step = g_slice_new (BlinkStep);
+  step->widget         = widget;
+  step->settings_value = g_strdup (settings_value);
+
+  *blink_scenario = g_list_append (*blink_scenario, step);
 
   while ((widget = gtk_widget_get_parent (widget)))
     gimp_widget_blink_cancel (widget);
 }
 
+/* gimp_blink_play_script:
+ * @blink_scenario:
+ *
+ * This function will play the @blink_scenario and free the associated
+ * data once done.
+ */
 void
 gimp_blink_play_script (GList *blink_scenario)
 {
+  BlinkStep *step;
+
   g_return_if_fail (g_list_length (blink_scenario) > 0);
 
-  g_object_set_data_full (G_OBJECT (blink_scenario->data),
+  step = blink_scenario->data;
+
+  g_object_set_data_full (G_OBJECT (step->widget),
                           "gimp-widget-blink-script",
-                          g_list_copy (blink_scenario->next),
-                          (GDestroyNotify) g_list_free);
-  gimp_widget_blink (blink_scenario->data);
+                          blink_scenario,
+                          (GDestroyNotify) gimp_blink_free_script);
+  gimp_widget_blink (step->widget);
 }
 
 void
@@ -1565,7 +1686,7 @@ gimp_blink_toolbox (Gimp         *gimp,
       action = gimp_ui_manager_find_action (ui_manager, "tools", action_name);
       gimp_action_activate (GIMP_ACTION (action));
     }
-  gimp_blink_dockable (gimp, "gimp-toolbox", action_name, blink_scenario);
+  gimp_blink_dockable (gimp, "gimp-toolbox", action_name, NULL, blink_scenario);
 }
 
 /**
@@ -1573,6 +1694,7 @@ gimp_blink_toolbox (Gimp         *gimp,
  * @gimp:
  * @dockable_identifier:
  * @widget_identifier:
+ * @settings_value:
  * @blink_scenario:
  *
  * This function will raise the dockable identified by
@@ -1582,20 +1704,28 @@ gimp_blink_toolbox (Gimp         *gimp,
  * Then it will find the widget identified by @widget_identifier. Note
  * that for propwidgets, this is usually the associated property name.
  *
+ * If @settings_value is set, then it will try to change the widget
+ * value, depending the type of widgets. Note that for now, only
+ * property widgets of enum, int or boolean types can be set (so the
+ * @settings_value string must represent an int value).
+ *
  * Finally it will either blink this widget immediately to raise
  * attention, or add it to the @blink_scenario if not %NULL. The blink
  * scenario must be explicitly started with gimp_blink_play_script()
- * when ready.
+ * when ready. @blink_scenario should be considered as opaque data, so
+ * you should not free it directly and let gimp_blink_play_script() do
+ * so for you.
  */
 void
 gimp_blink_dockable (Gimp         *gimp,
                      const gchar  *dockable_identifier,
                      const gchar  *widget_identifier,
+                     const gchar  *settings_value,
                      GList       **blink_scenario)
 {
-  GtkWidget  *dockable;
-  GdkMonitor *monitor;
-  BlinkData  *data;
+  GtkWidget   *dockable;
+  GdkMonitor  *monitor;
+  BlinkSearch *data;
 
   g_return_if_fail (GIMP_IS_GIMP (gimp));
 
@@ -1613,13 +1743,14 @@ gimp_blink_dockable (Gimp         *gimp,
 
   if (widget_identifier)
     {
-      data = g_slice_new (BlinkData);
+      data = g_slice_new (BlinkSearch);
       data->blink_script      = blink_scenario;
       data->widget_identifier = widget_identifier;
+      data->settings_value    = settings_value;
       gtk_container_foreach (GTK_CONTAINER (dockable),
                              (GtkCallback) gimp_search_widget_rec,
                              (gpointer) data);
-      g_slice_free (BlinkData, data);
+      g_slice_free (BlinkSearch, data);
     }
 }
 
@@ -2135,40 +2266,75 @@ gimp_widget_flush_expose (void)
 /*  private functions  */
 
 static void
-gimp_search_widget_rec (GtkWidget *widget,
-                        BlinkData *data)
+gimp_widget_blink_after (GtkWidget *widget,
+                         gint       ms_timeout)
+{
+  WidgetBlink *blink;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  blink = widget_blink_new ();
+
+  g_object_set_data_full (G_OBJECT (widget), "gimp-widget-blink", blink,
+                          (GDestroyNotify) widget_blink_free);
+
+  blink->timeout_id = g_timeout_add (ms_timeout,
+                                     (GSourceFunc) gimp_widget_blink_start_timeout,
+                                     widget);
+}
+
+static void
+gimp_search_widget_rec (GtkWidget   *widget,
+                        BlinkSearch *data)
 {
-  GList       **blink_script = data->blink_script;
-  const gchar  *searched_id  = data->widget_identifier;
+  GList       **blink_script   = data->blink_script;
+  const gchar  *searched_id    = data->widget_identifier;
+  const gchar  *settings_value = data->settings_value;
+  const gchar  *id;
 
-  if (gtk_widget_is_visible (widget))
+  id = g_object_get_data (G_OBJECT (widget),
+                          "gimp-widget-identifier");
+
+  if (id == NULL)
+    /* Using propwidgets identifiers as fallback. */
+    id = g_object_get_data (G_OBJECT (widget),
+                            "gimp-widget-property-name");
+
+  if (id && g_strcmp0 (id, searched_id) == 0)
+    {
+      /* Giving focus to help scrolling the dockable so that the
+       * widget is visible. Note that it seems to work fine if the
+       * dockable was already present, not if it was just created.
+       *
+       * TODO: this should be fixed so that we always make the
+       * widget visible before blinking, otherwise it's a bit
+       * useless when this happens.
+       */
+      gtk_widget_grab_focus (widget);
+      if (blink_script)
+        gimp_widget_script_blink (widget, settings_value, blink_script);
+      else if (gtk_widget_is_visible (widget))
+        gimp_widget_blink (widget);
+    }
+  else if (GTK_IS_CONTAINER (widget))
     {
-      const gchar *id;
+      gtk_container_foreach (GTK_CONTAINER (widget),
+                             (GtkCallback) gimp_search_widget_rec,
+                             (gpointer) data);
+    }
+}
 
-      id = g_object_get_data (G_OBJECT (widget),
-                              "gimp-widget-identifier");
+static void
+gimp_blink_free_script (GList *blink_scenario)
+{
+  GList *iter;
 
-      if (id && g_strcmp0 (id, searched_id) == 0)
-        {
-          /* Giving focus to help scrolling the dockable so that the
-           * widget is visible. Note that it seems to work fine if the
-           * dockable was already present, not if it was just created.
-           *
-           * TODO: this should be fixed so that we always make the
-           * widget visible before blinking, otherwise it's a bit
-           * useless when this happens.
-           */
-          gtk_widget_grab_focus (widget);
-          if (blink_script)
-            gimp_widget_script_blink (widget, blink_script);
-          else
-            gimp_widget_blink (widget);
-        }
-      else if (GTK_IS_CONTAINER (widget))
-        {
-          gtk_container_foreach (GTK_CONTAINER (widget),
-                                 (GtkCallback) gimp_search_widget_rec,
-                                 (gpointer) data);
-        }
+  for (iter = blink_scenario; iter; iter = iter->next)
+    {
+      BlinkStep *step = iter->data;
+
+      g_free (step->settings_value);
+      g_slice_free (BlinkStep, step);
     }
+  g_list_free (blink_scenario);
 }
diff --git a/app/widgets/gimpwidgets-utils.h b/app/widgets/gimpwidgets-utils.h
index f60cdb9551..c0b53f435e 100644
--- a/app/widgets/gimpwidgets-utils.h
+++ b/app/widgets/gimpwidgets-utils.h
@@ -100,9 +100,11 @@ void              gimp_blink_toolbox               (Gimp                  *gimp,
 void              gimp_blink_dockable              (Gimp                  *gimp,
                                                     const gchar           *dockable_identifier,
                                                     const gchar           *widget_identifier,
+                                                    const gchar           *settings_value,
                                                     GList               **blink_script);
 
 void              gimp_widget_script_blink         (GtkWidget            *widget,
+                                                    const gchar          *widget_identifier,
                                                     GList               **blink_script);
 void              gimp_blink_play_script           (GList                *blink_script);
 
diff --git a/libgimpwidgets/gimppropwidgets.c b/libgimpwidgets/gimppropwidgets.c
index b353e1250b..d6469ae4d4 100644
--- a/libgimpwidgets/gimppropwidgets.c
+++ b/libgimpwidgets/gimppropwidgets.c
@@ -135,7 +135,7 @@ gimp_prop_check_button_new (GObject     *config,
                           button, "active",
                           G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
 
-  gimp_widget_set_identifier (button, property_name);
+  gimp_widget_set_bound_property (button, config, property_name);
 
   return button;
 }
@@ -216,7 +216,7 @@ gimp_prop_enum_check_button_new (GObject     *config,
 
   gtk_widget_show (button);
 
-  gimp_widget_set_identifier (button, property_name);
+  gimp_widget_set_bound_property (button, config, property_name);
 
   return button;
 }
@@ -365,7 +365,7 @@ gimp_prop_switch_new (GObject     *config,
   if (switch_out)
     *switch_out = pswitch;
 
-  gimp_widget_set_identifier (hbox, property_name);
+  gimp_widget_set_bound_property (hbox, config, property_name);
 
   return hbox;
 }
@@ -431,7 +431,7 @@ gimp_prop_int_combo_box_new (GObject      *config,
                           combo_box, "value",
                           G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
 
-  gimp_widget_set_identifier (combo_box, property_name);
+  gimp_widget_set_bound_property (combo_box, config, property_name);
 
   return combo_box;
 }
@@ -499,7 +499,7 @@ gimp_prop_pointer_combo_box_new (GObject      *config,
                   G_CALLBACK (gimp_prop_pointer_combo_box_notify),
                   combo_box);
 
-  gimp_widget_set_identifier (combo_box, property_name);
+  gimp_widget_set_bound_property (combo_box, config, property_name);
 
   gtk_widget_show (combo_box);
 
@@ -591,7 +591,7 @@ gimp_prop_enum_combo_box_new (GObject     *config,
 
   gtk_widget_show (combo_box);
 
-  gimp_widget_set_identifier (combo_box, property_name);
+  gimp_widget_set_bound_property (combo_box, config, property_name);
 
   return combo_box;
 }
@@ -751,7 +751,7 @@ gimp_prop_boolean_combo_box_new (GObject     *config,
                   G_CALLBACK (gimp_prop_boolean_combo_box_notify),
                   combo);
 
-  gimp_widget_set_identifier (combo, property_name);
+  gimp_widget_set_bound_property (combo, config, property_name);
 
   gtk_widget_show (combo);
 
@@ -890,7 +890,7 @@ gimp_prop_enum_radio_frame_new (GObject     *config,
 
   gtk_widget_show (frame);
 
-  gimp_widget_set_identifier (frame, property_name);
+  gimp_widget_set_bound_property (frame, config, property_name);
 
   return frame;
 }
@@ -962,7 +962,7 @@ gimp_prop_enum_radio_box_new (GObject     *config,
 
   g_object_set_data (G_OBJECT (vbox), "radio-button", button);
 
-  gimp_widget_set_identifier (vbox, property_name);
+  gimp_widget_set_bound_property (vbox, config, property_name);
 
   gtk_widget_show (vbox);
 
@@ -1012,7 +1012,7 @@ gimp_prop_int_radio_frame_new (GObject      *config,
   gtk_container_add (GTK_CONTAINER (frame), box);
   gtk_widget_show (box);
 
-  gimp_widget_set_identifier (frame, property_name);
+  gimp_widget_set_bound_property (frame, config, property_name);
 
   gtk_widget_show (frame);
 
@@ -1102,7 +1102,7 @@ gimp_prop_int_radio_box_new (GObject      *config,
 
   g_object_set_data (G_OBJECT (vbox), "radio-button", button);
 
-  gimp_widget_set_identifier (vbox, property_name);
+  gimp_widget_set_bound_property (vbox, config, property_name);
 
   gtk_widget_show (vbox);
 
@@ -1156,7 +1156,7 @@ gimp_prop_enum_label_new (GObject     *config,
                   G_CALLBACK (gimp_prop_enum_label_notify),
                   label);
 
-  gimp_widget_set_identifier (label, property_name);
+  gimp_widget_set_bound_property (label, config, property_name);
 
   gtk_widget_show (label);
 
@@ -1240,7 +1240,7 @@ gimp_prop_boolean_radio_frame_new (GObject     *config,
 
   g_object_set_data (G_OBJECT (frame), "radio-button", button);
 
-  gimp_widget_set_identifier (frame, property_name);
+  gimp_widget_set_bound_property (frame, config, property_name);
 
   gtk_widget_show (frame);
 
@@ -1317,7 +1317,7 @@ gimp_prop_enum_icon_box_new (GObject     *config,
                   G_CALLBACK (gimp_prop_radio_button_notify),
                   button);
 
-  gimp_widget_set_identifier (box, property_name);
+  gimp_widget_set_bound_property (box, config, property_name);
 
   gtk_widget_show (box);
 
@@ -1432,7 +1432,7 @@ gimp_prop_spin_button_new (GObject     *config,
                      "gimp-prop-adjustment-binding",
                      binding);
 
-  gimp_widget_set_identifier (spinbutton, property_name);
+  gimp_widget_set_bound_property (spinbutton, config, property_name);
 
   gtk_widget_show (spinbutton);
 
@@ -1481,7 +1481,7 @@ gimp_prop_label_spin_new (GObject     *config,
                           widget, "value",
                           G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
 
-  gimp_widget_set_identifier (widget, property_name);
+  gimp_widget_set_bound_property (widget, config, property_name);
 
   return widget;
 }
@@ -1559,7 +1559,7 @@ gimp_prop_spin_scale_new (GObject     *config,
 
   gtk_widget_show (spinscale);
 
-  gimp_widget_set_identifier (spinscale, property_name);
+  gimp_widget_set_bound_property (spinscale, config, property_name);
 
   return spinscale;
 }
@@ -1756,7 +1756,7 @@ gimp_prop_hscale_new (GObject     *config,
                   G_CALLBACK (gimp_prop_adjustment_notify),
                   adjustment);
 
-  gimp_widget_set_identifier (scale, property_name);
+  gimp_widget_set_bound_property (scale, config, property_name);
 
   gtk_widget_show (scale);
 
@@ -1867,7 +1867,7 @@ gimp_prop_scale_entry_new (GObject     *config,
 
     }
 
-  gimp_widget_set_identifier (widget, property_name);
+  gimp_widget_set_bound_property (widget, config, property_name);
 
   return widget;
 }
@@ -2113,7 +2113,7 @@ gimp_prop_memsize_entry_new (GObject     *config,
                   G_CALLBACK (gimp_prop_memsize_notify),
                   entry);
 
-  gimp_widget_set_identifier (entry, property_name);
+  gimp_widget_set_bound_property (entry, config, property_name);
 
   gtk_widget_show (entry);
 
@@ -2223,7 +2223,7 @@ gimp_prop_label_new (GObject     *config,
   g_object_bind_property (config, property_name,
                           label, "label", flags);
 
-  gimp_widget_set_identifier (label, property_name);
+  gimp_widget_set_bound_property (label, config, property_name);
 
   return label;
 }
@@ -2294,7 +2294,7 @@ gimp_prop_entry_new (GObject     *config,
                   G_CALLBACK (gimp_prop_entry_notify),
                   entry);
 
-  gimp_widget_set_identifier (entry, property_name);
+  gimp_widget_set_bound_property (entry, config, property_name);
 
   gtk_widget_show (entry);
 
@@ -2409,7 +2409,7 @@ gimp_prop_label_entry_new (GObject     *config,
                           label_entry, "value",
                           G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
 
-  gimp_widget_set_identifier (label_entry, property_name);
+  gimp_widget_set_bound_property (label_entry, config, property_name);
 
   gtk_widget_show (label_entry);
 
@@ -2637,7 +2637,7 @@ gimp_prop_string_combo_box_new (GObject      *config,
                   G_CALLBACK (gimp_prop_string_combo_box_notify),
                   combo_box);
 
-  gimp_widget_set_identifier (combo_box, property_name);
+  gimp_widget_set_bound_property (combo_box, config, property_name);
 
   gtk_widget_show (combo_box);
 
@@ -3013,7 +3013,7 @@ gimp_prop_path_editor_new (GObject     *config,
                       editor);
     }
 
-  gimp_widget_set_identifier (editor, path_property_name);
+  gimp_widget_set_bound_property (editor, config, path_property_name);
 
   gtk_widget_show (editor);
 
@@ -3324,7 +3324,7 @@ gimp_prop_size_entry_new (GObject                   *config,
                       entry);
     }
 
-  gimp_widget_set_identifier (entry, property_name);
+  gimp_widget_set_bound_property (entry, config, property_name);
 
   gtk_widget_show (entry);
 
@@ -3562,7 +3562,7 @@ gimp_prop_coordinates_new (GObject                   *config,
       return NULL;
     }
 
-  gimp_widget_set_identifier (entry, x_property_name);
+  gimp_widget_set_bound_property (entry, config, x_property_name);
 
   gtk_widget_show (entry);
 
@@ -4019,7 +4019,7 @@ gimp_prop_color_area_new (GObject           *config,
                   G_CALLBACK (gimp_prop_color_area_notify),
                   area);
 
-  gimp_widget_set_identifier (area, property_name);
+  gimp_widget_set_bound_property (area, config, property_name);
 
   gtk_widget_show (area);
 
@@ -4120,7 +4120,7 @@ gimp_prop_color_select_new (GObject           *config,
                           button, "color",
                           G_BINDING_BIDIRECTIONAL);
 
-  gimp_widget_set_identifier (button, property_name);
+  gimp_widget_set_bound_property (button, config, property_name);
 
   gtk_widget_show (button);
 
@@ -4168,7 +4168,7 @@ gimp_prop_label_color_new (GObject     *config,
                           prop_widget, "value",
                           G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
 
-  gimp_widget_set_identifier (prop_widget, property_name);
+  gimp_widget_set_bound_property (prop_widget, config, property_name);
 
   gtk_widget_show (prop_widget);
 
@@ -4245,7 +4245,7 @@ gimp_prop_unit_combo_box_new (GObject     *config,
                   G_CALLBACK (gimp_prop_unit_combo_box_notify),
                   combo);
 
-  gimp_widget_set_identifier (combo, property_name);
+  gimp_widget_set_bound_property (combo, config, property_name);
 
   gtk_widget_show (combo);
 
@@ -4357,7 +4357,7 @@ gimp_prop_icon_image_new (GObject     *config,
                           image, "icon-name",
                           G_BINDING_BIDIRECTIONAL);
 
-  gimp_widget_set_identifier (image, property_name);
+  gimp_widget_set_bound_property (image, config, property_name);
 
   g_free (icon_name);
 
@@ -4429,7 +4429,7 @@ gimp_prop_expander_new (GObject     *config,
                   G_CALLBACK (gimp_prop_expander_notify),
                   expander);
 
-  gimp_widget_set_identifier (expander, property_name);
+  gimp_widget_set_bound_property (expander, config, property_name);
 
   gtk_widget_show (expander);
 
diff --git a/libgimpwidgets/gimpwidgets-private.c b/libgimpwidgets/gimpwidgets-private.c
index 567779a95a..96dbf07b5f 100644
--- a/libgimpwidgets/gimpwidgets-private.c
+++ b/libgimpwidgets/gimpwidgets-private.c
@@ -205,6 +205,13 @@ gimp_widgets_init (GimpHelpFunc           standard_help_func,
  * as widget identifier. You can always use this function to override a
  * given widget identifier with a more specific name.
  *
+ * Note that when a widget is bound to a property, in other words when
+ * in one of our propwidgets API, you should rather use
+ * gimp_widget_set_bound_property() because it allows more easily to
+ * tweak values.
+ * gimp_widget_set_identifier() is more destined to random widgets which
+ * you just want to be able to blink.
+ *
  * It's doesn't need to be in public API because it is only used for
  * internal blinking ability in core GIMP GUI.
  */
@@ -220,6 +227,41 @@ gimp_widget_set_identifier (GtkWidget   *widget,
                           (GDestroyNotify) g_free);
 }
 
+/**
+ * gimp_widget_set_bound_property:
+ * @widget:
+ * @config:
+ * @property_name:
+ *
+ * This is similar to gimp_widget_set_identifier() because the
+ * property_name can be used as identifier by our blink API.
+ * You can still set explicitly (and additionally)
+ * gimp_widget_set_identifier() for rare cases where 2 widgets in a same
+ * container would bind the same property (or 2 properties named the
+ * same way for 2 different config objects). The identifier will be used
+ * in priority to the property name (which can still be used for
+ * changing the widget value, so it remains important to also set it).
+ *
+ * It's doesn't need to be in public API because it is only used for
+ * internal blinking ability in core GIMP GUI.
+ */
+void
+gimp_widget_set_bound_property (GtkWidget   *widget,
+                                GObject     *config,
+                                const gchar *property_name)
+{
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  g_object_set_data_full (G_OBJECT (widget),
+                          "gimp-widget-property-name",
+                          g_strdup (property_name),
+                          (GDestroyNotify) g_free);
+  g_object_set_data_full (G_OBJECT (widget),
+                          "gimp-widget-property-config",
+                          g_object_ref (config),
+                          (GDestroyNotify) g_object_unref);
+}
+
 /* clean up babl (in particular, so that the fish cache is constructed) if the
  * compiler supports destructors
  */
diff --git a/libgimpwidgets/gimpwidgets-private.h b/libgimpwidgets/gimpwidgets-private.h
index 62edfcff8d..dea6e8ccab 100644
--- a/libgimpwidgets/gimpwidgets-private.h
+++ b/libgimpwidgets/gimpwidgets-private.h
@@ -39,14 +39,17 @@ extern GimpEnsureModulesFunc _gimp_ensure_modules_func;
 G_BEGIN_DECLS
 
 
-void  gimp_widgets_init          (GimpHelpFunc           standard_help_func,
-                                  GimpGetColorFunc       get_foreground_func,
-                                  GimpGetColorFunc       get_background_func,
-                                  GimpEnsureModulesFunc  ensure_modules_func,
-                                  const gchar           *test_base_dir);
-
-void  gimp_widget_set_identifier (GtkWidget             *widget,
-                                  const gchar           *identifier);
+void  gimp_widgets_init              (GimpHelpFunc           standard_help_func,
+                                      GimpGetColorFunc       get_foreground_func,
+                                      GimpGetColorFunc       get_background_func,
+                                      GimpEnsureModulesFunc  ensure_modules_func,
+                                      const gchar           *test_base_dir);
+
+void  gimp_widget_set_identifier     (GtkWidget             *widget,
+                                      const gchar           *identifier);
+void  gimp_widget_set_bound_property (GtkWidget             *widget,
+                                      GObject               *config,
+                                      const gchar           *property_name);
 
 
 G_END_DECLS


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