summaryrefslogtreecommitdiffstats
path: root/app/propgui/gimppropgui.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/propgui/gimppropgui.c')
-rw-r--r--app/propgui/gimppropgui.c702
1 files changed, 702 insertions, 0 deletions
diff --git a/app/propgui/gimppropgui.c b/app/propgui/gimppropgui.c
new file mode 100644
index 0000000..fcc9a7e
--- /dev/null
+++ b/app/propgui/gimppropgui.c
@@ -0,0 +1,702 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui.c
+ * 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
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gegl.h>
+#include <gegl-paramspecs.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpconfig/gimpconfig.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "propgui-types.h"
+
+#include "gegl/gimp-gegl-utils.h"
+
+#include "operations/gimp-operation-config.h"
+
+#include "core/gimpcontext.h"
+
+#include "widgets/gimpcolorpanel.h"
+#include "widgets/gimpmessagebox.h"
+#include "widgets/gimpspinscale.h"
+#include "widgets/gimppropwidgets.h"
+
+#include "gimppropgui.h"
+#include "gimppropgui-channel-mixer.h"
+#include "gimppropgui-color-balance.h"
+#include "gimppropgui-color-rotate.h"
+#include "gimppropgui-color-to-alpha.h"
+#include "gimppropgui-convolution-matrix.h"
+#include "gimppropgui-diffraction-patterns.h"
+#include "gimppropgui-eval.h"
+#include "gimppropgui-focus-blur.h"
+#include "gimppropgui-generic.h"
+#include "gimppropgui-hue-saturation.h"
+#include "gimppropgui-motion-blur-circular.h"
+#include "gimppropgui-motion-blur-linear.h"
+#include "gimppropgui-motion-blur-zoom.h"
+#include "gimppropgui-newsprint.h"
+#include "gimppropgui-panorama-projection.h"
+#include "gimppropgui-recursive-transform.h"
+#include "gimppropgui-shadows-highlights.h"
+#include "gimppropgui-spiral.h"
+#include "gimppropgui-supernova.h"
+#include "gimppropgui-utils.h"
+#include "gimppropgui-vignette.h"
+
+#include "gimp-intl.h"
+
+
+#define HAS_KEY(p,k,v) gimp_gegl_param_spec_has_key (p, k, v)
+
+
+static gboolean gimp_prop_string_to_boolean (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data);
+static void gimp_prop_config_notify (GObject *config,
+ GParamSpec *pspec,
+ GtkWidget *widget);
+static void gimp_prop_widget_show (GtkWidget *widget,
+ GObject *config);
+static void gimp_prop_free_label_ref (GWeakRef *label_ref);
+
+/* public functions */
+
+GtkWidget *
+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;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (config),
+ property_name);
+
+ return gimp_prop_widget_new_from_pspec (config, pspec, area, context,
+ 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,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator,
+ const gchar **label)
+{
+ GtkWidget *widget = NULL;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (pspec != NULL, NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+ g_return_val_if_fail (label != NULL, NULL);
+
+ *label = NULL;
+
+ if (GEGL_IS_PARAM_SPEC_SEED (pspec))
+ {
+ widget = gimp_prop_random_seed_new (config, pspec->name);
+
+ *label = g_param_spec_get_nick (pspec);
+ }
+ else if (G_IS_PARAM_SPEC_INT (pspec) ||
+ G_IS_PARAM_SPEC_UINT (pspec) ||
+ G_IS_PARAM_SPEC_FLOAT (pspec) ||
+ G_IS_PARAM_SPEC_DOUBLE (pspec))
+ {
+ gdouble lower;
+ gdouble upper;
+ gdouble step;
+ gdouble page;
+ gint digits;
+
+ if (GEGL_IS_PARAM_SPEC_DOUBLE (pspec))
+ {
+ GeglParamSpecDouble *gspec = GEGL_PARAM_SPEC_DOUBLE (pspec);
+
+ lower = gspec->ui_minimum;
+ upper = gspec->ui_maximum;
+ step = gspec->ui_step_small;
+ page = gspec->ui_step_big;
+ digits = gspec->ui_digits;
+ }
+ else if (GEGL_IS_PARAM_SPEC_INT (pspec))
+ {
+ GeglParamSpecInt *gspec = GEGL_PARAM_SPEC_INT (pspec);
+
+ lower = gspec->ui_minimum;
+ upper = gspec->ui_maximum;
+ step = gspec->ui_step_small;
+ page = gspec->ui_step_big;
+ digits = 0;
+ }
+ else
+ {
+ gdouble value;
+
+ /* Get the min and max for the given property. */
+ _gimp_prop_widgets_get_numeric_values (config, pspec,
+ &value, &lower, &upper,
+ G_STRFUNC);
+
+ if ((upper - lower <= 1.0) &&
+ (G_IS_PARAM_SPEC_FLOAT (pspec) ||
+ G_IS_PARAM_SPEC_DOUBLE (pspec)))
+ {
+ step = 0.01;
+ page = 0.1;
+ digits = 4;
+ }
+ else if ((upper - lower <= 10.0) &&
+ (G_IS_PARAM_SPEC_FLOAT (pspec) ||
+ G_IS_PARAM_SPEC_DOUBLE (pspec)))
+ {
+ step = 0.1;
+ page = 1.0;
+ digits = 3;
+ }
+ else
+ {
+ step = 1.0;
+ page = 10.0;
+ digits = (G_IS_PARAM_SPEC_FLOAT (pspec) ||
+ G_IS_PARAM_SPEC_DOUBLE (pspec)) ? 2 : 0;
+ }
+ }
+
+ widget = gimp_prop_spin_scale_new (config, pspec->name, NULL,
+ step, page, digits);
+
+ if (HAS_KEY (pspec, "unit", "degree") &&
+ (upper - lower) == 360.0)
+ {
+ GtkWidget *hbox;
+ GtkWidget *dial;
+
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (widget), TRUE);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+
+ gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
+ gtk_widget_show (widget);
+
+ dial = gimp_prop_angle_dial_new (config, pspec->name);
+ g_object_set (dial,
+ "clockwise-angles", HAS_KEY (pspec, "direction", "cw"),
+ NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), dial, FALSE, FALSE, 0);
+ gtk_widget_show (dial);
+
+ gimp_help_set_help_data (hbox, g_param_spec_get_blurb (pspec), NULL);
+ gimp_prop_gui_bind_label (hbox, widget);
+
+ widget = hbox;
+ }
+ else if (HAS_KEY (pspec, "unit", "kelvin"))
+ {
+ GtkWidget *hbox;
+ GtkWidget *button;
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+
+ gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
+ gtk_widget_show (widget);
+
+ button = gimp_prop_kelvin_presets_new (config, pspec->name);
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ gimp_help_set_help_data (hbox, g_param_spec_get_blurb (pspec), NULL);
+ gimp_prop_gui_bind_label (hbox, widget);
+
+ widget = hbox;
+ }
+ else
+ {
+ gimp_prop_gui_bind_label (widget, widget);
+
+ if (area &&
+ (HAS_KEY (pspec, "unit", "pixel-coordinate") ||
+ HAS_KEY (pspec, "unit", "pixel-distance")) &&
+ (HAS_KEY (pspec, "axis", "x") ||
+ HAS_KEY (pspec, "axis", "y")))
+ {
+ gdouble min = lower;
+ gdouble max = upper;
+
+ if (HAS_KEY (pspec, "unit", "pixel-coordinate"))
+ {
+ /* limit pixel coordinate scales to the actual area */
+
+ gint off_x = area->x;
+ gint off_y = area->y;
+
+ if (HAS_KEY (pspec, "axis", "x"))
+ {
+ min = MAX (lower, off_x);
+ max = MIN (upper, off_x + area->width);
+ }
+ else if (HAS_KEY (pspec, "axis","y"))
+ {
+ min = MAX (lower, off_y);
+ max = MIN (upper, off_y + area->height);
+ }
+ }
+ else if (HAS_KEY (pspec, "unit", "pixel-distance"))
+ {
+ /* limit pixel distance scales to the same value on the
+ * x and y axes, so linked values have the same range,
+ * we use MAX (width, height), see issue #2540
+ */
+
+ max = MIN (upper, MAX (area->width, area->height));
+ }
+
+ gimp_spin_scale_set_scale_limits (GIMP_SPIN_SCALE (widget),
+ min, max);
+ }
+ }
+ }
+ else if (G_IS_PARAM_SPEC_STRING (pspec))
+ {
+ *label = g_param_spec_get_nick (pspec);
+
+ if (GIMP_IS_PARAM_SPEC_CONFIG_PATH (pspec))
+ {
+ widget =
+ gimp_prop_file_chooser_button_new (config, pspec->name,
+ g_param_spec_get_nick (pspec),
+ GTK_FILE_CHOOSER_ACTION_OPEN);
+ }
+ else if (HAS_KEY (pspec, "multiline", "true"))
+ {
+ GtkTextBuffer *buffer;
+ GtkWidget *view;
+
+ buffer = gimp_prop_text_buffer_new (config, pspec->name, -1);
+ view = gtk_text_view_new_with_buffer (buffer);
+ g_object_unref (buffer);
+
+ widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_widget_set_size_request (widget, -1, 150);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (widget),
+ GTK_SHADOW_IN);
+ gtk_container_add (GTK_CONTAINER (widget), view);
+ gtk_widget_show (view);
+ }
+ else if (HAS_KEY (pspec, "error", "true"))
+ {
+ GtkWidget *l;
+
+ widget = gimp_message_box_new (GIMP_ICON_WILBER_EEK);
+ gimp_message_box_set_primary_text (GIMP_MESSAGE_BOX (widget), "%s",
+ *label);
+ gimp_message_box_set_text (GIMP_MESSAGE_BOX (widget), "%s", "");
+
+ l = GIMP_MESSAGE_BOX (widget)->label[1];
+
+ g_object_bind_property (config, pspec->name,
+ l, "label",
+ G_BINDING_SYNC_CREATE);
+ g_object_bind_property_full (config, pspec->name,
+ widget, "visible",
+ G_BINDING_SYNC_CREATE,
+ gimp_prop_string_to_boolean,
+ NULL,
+ NULL, NULL);
+ *label = NULL;
+ }
+ else
+ {
+ widget = gimp_prop_entry_new (config, pspec->name, -1);
+ }
+
+ }
+ else if (G_IS_PARAM_SPEC_BOOLEAN (pspec))
+ {
+ widget = gimp_prop_check_button_new (config, pspec->name,
+ g_param_spec_get_nick (pspec));
+
+ gimp_prop_gui_bind_label (widget, widget);
+ }
+ else if (G_IS_PARAM_SPEC_ENUM (pspec))
+ {
+ widget = gimp_prop_enum_combo_box_new (config, pspec->name, 0, 0);
+ gimp_int_combo_box_set_label (GIMP_INT_COMBO_BOX (widget),
+ g_param_spec_get_nick (pspec));
+
+ gimp_prop_gui_bind_label (widget, widget);
+ }
+ else if (GIMP_IS_PARAM_SPEC_RGB (pspec))
+ {
+ gboolean has_alpha;
+ GtkWidget *button;
+
+ has_alpha = gimp_param_spec_rgb_has_alpha (pspec);
+
+ widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+
+ button = gimp_prop_color_button_new (config, pspec->name,
+ g_param_spec_get_nick (pspec),
+ 128, 24,
+ has_alpha ?
+ GIMP_COLOR_AREA_SMALL_CHECKS :
+ GIMP_COLOR_AREA_FLAT);
+ gimp_color_button_set_update (GIMP_COLOR_BUTTON (button), TRUE);
+ gimp_color_panel_set_context (GIMP_COLOR_PANEL (button), context);
+ gtk_box_pack_start (GTK_BOX (widget), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ gimp_prop_gui_bind_tooltip (button, widget);
+
+ if (create_picker_func)
+ {
+ button = create_picker_func (creator,
+ pspec->name,
+ GIMP_ICON_COLOR_PICKER_GRAY,
+ _("Pick color from the image"),
+ /* pick_abyss = */ FALSE,
+ NULL, NULL);
+ gtk_box_pack_start (GTK_BOX (widget), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+ }
+
+ *label = g_param_spec_get_nick (pspec);
+ }
+ else
+ {
+ g_warning ("%s: not supported: %s (%s)\n", G_STRFUNC,
+ g_type_name (G_TYPE_FROM_INSTANCE (pspec)), pspec->name);
+ }
+
+ /* if we have any keys for dynamic properties, listen to config's notify
+ * signal, and update the properties accordingly.
+ */
+ if (gegl_param_spec_get_property_key (pspec, "sensitive") ||
+ gegl_param_spec_get_property_key (pspec, "visible") ||
+ gegl_param_spec_get_property_key (pspec, "label") ||
+ gegl_param_spec_get_property_key (pspec, "description"))
+ {
+ g_object_set_data (G_OBJECT (widget), "gimp-prop-pspec", pspec);
+
+ g_signal_connect_object (config, "notify",
+ G_CALLBACK (gimp_prop_config_notify),
+ widget, 0);
+
+ if (gegl_param_spec_get_property_key (pspec, "visible"))
+ {
+ /* a bit of a hack: if we have a dynamic "visible" property key,
+ * connect to the widget's "show" signal, so that we can intercept
+ * our caller's gtk_widget_show() call, and keep the widget hidden if
+ * necessary.
+ */
+ g_signal_connect (widget, "show",
+ G_CALLBACK (gimp_prop_widget_show),
+ config);
+ }
+
+ /* update all the properties now */
+ gimp_prop_config_notify (config, NULL, widget);
+ }
+
+ return widget;
+}
+
+
+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
+{
+ const gchar *config_type;
+ GimpPropGuiNewFunc gui_new_func;
+}
+gui_new_funcs[] =
+{
+ { "GimpColorBalanceConfig",
+ _gimp_prop_gui_new_color_balance },
+ { "GimpHueSaturationConfig",
+ _gimp_prop_gui_new_hue_saturation },
+ { "GimpGegl-gegl-color-rotate-config",
+ _gimp_prop_gui_new_color_rotate },
+ { "GimpGegl-gegl-color-to-alpha-config",
+ _gimp_prop_gui_new_color_to_alpha },
+ { "GimpGegl-gegl-convolution-matrix-config",
+ _gimp_prop_gui_new_convolution_matrix },
+ { "GimpGegl-gegl-channel-mixer-config",
+ _gimp_prop_gui_new_channel_mixer },
+ { "GimpGegl-gegl-diffraction-patterns-config",
+ _gimp_prop_gui_new_diffraction_patterns },
+ { "GimpGegl-gegl-focus-blur-config",
+ _gimp_prop_gui_new_focus_blur },
+ { "GimpGegl-gegl-motion-blur-circular-config",
+ _gimp_prop_gui_new_motion_blur_circular },
+ { "GimpGegl-gegl-motion-blur-linear-config",
+ _gimp_prop_gui_new_motion_blur_linear },
+ { "GimpGegl-gegl-motion-blur-zoom-config",
+ _gimp_prop_gui_new_motion_blur_zoom },
+ { "GimpGegl-gegl-newsprint-config",
+ _gimp_prop_gui_new_newsprint },
+ { "GimpGegl-gegl-panorama-projection-config",
+ _gimp_prop_gui_new_panorama_projection },
+ { "GimpGegl-gegl-recursive-transform-config",
+ _gimp_prop_gui_new_recursive_transform },
+ { "GimpGegl-gegl-shadows-highlights-config",
+ _gimp_prop_gui_new_shadows_highlights },
+ { "GimpGegl-gegl-spiral-config",
+ _gimp_prop_gui_new_spiral },
+ { "GimpGegl-gegl-supernova-config",
+ _gimp_prop_gui_new_supernova },
+ { "GimpGegl-gegl-vignette-config",
+ _gimp_prop_gui_new_vignette },
+ { 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,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator)
+{
+ GtkWidget *gui = NULL;
+ GParamSpec **param_specs;
+ guint n_param_specs;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+
+ param_specs = gimp_operation_config_list_properties (config,
+ owner_type, flags,
+ &n_param_specs);
+
+ if (param_specs)
+ {
+ const gchar *config_type_name = G_OBJECT_TYPE_NAME (config);
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (gui_new_funcs); i++)
+ {
+ if (! gui_new_funcs[i].config_type ||
+ ! strcmp (gui_new_funcs[i].config_type, config_type_name))
+ {
+ g_printerr ("GUI new func match: %s\n",
+ gui_new_funcs[i].config_type ?
+ gui_new_funcs[i].config_type : "generic fallback");
+
+ gui = gui_new_funcs[i].gui_new_func (config,
+ param_specs, n_param_specs,
+ area,
+ context,
+ create_picker_func,
+ create_controller_func,
+ creator);
+ break;
+ }
+ }
+
+ g_free (param_specs);
+ }
+ else
+ {
+ gui = gtk_label_new (_("This operation has no editable properties"));
+ gimp_label_set_attributes (GTK_LABEL (gui),
+ PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
+ -1);
+ gtk_misc_set_padding (GTK_MISC (gui), 0, 4);
+ }
+
+ return gui;
+}
+
+void
+gimp_prop_gui_bind_container (GtkWidget *source,
+ GtkWidget *target)
+{
+ g_object_bind_property (source, "sensitive",
+ target, "sensitive",
+ G_BINDING_SYNC_CREATE);
+ g_object_bind_property (source, "visible",
+ target, "visible",
+ G_BINDING_SYNC_CREATE);
+}
+
+void
+gimp_prop_gui_bind_label (GtkWidget *source,
+ GtkWidget *target)
+{
+ GWeakRef *label_ref;
+ const gchar *label;
+
+ /* we want to update "target"'s "label" property whenever the label
+ * expression associated with "source" is reevaluated, however, "source"
+ * might not itself have a "label" property we can bind to. just keep around
+ * a reference to "target", and update its label manually.
+ */
+ g_return_if_fail (g_object_get_data (G_OBJECT (source),
+ "gimp-prop-label-ref") == NULL);
+
+ label_ref = g_slice_new (GWeakRef);
+
+ g_weak_ref_init (label_ref, target);
+
+ g_object_set_data_full (G_OBJECT (source),
+ "gimp-prop-label-ref", label_ref,
+ (GDestroyNotify) gimp_prop_free_label_ref);
+
+ label = g_object_get_data (G_OBJECT (source), "gimp-prop-label");
+
+ if (label)
+ g_object_set (target, "label", label, NULL);
+
+ /* note that "source" might be its own label widget, in which case there's no
+ * need to bind the rest of the properties.
+ */
+ if (source != target)
+ gimp_prop_gui_bind_tooltip (source, target);
+}
+
+void
+gimp_prop_gui_bind_tooltip (GtkWidget *source,
+ GtkWidget *target)
+{
+ g_object_bind_property (source, "tooltip-text",
+ target, "tooltip-text",
+ G_BINDING_SYNC_CREATE);
+}
+
+
+/* private functions */
+
+static gboolean
+gimp_prop_string_to_boolean (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data)
+{
+ const gchar *string = g_value_get_string (from_value);
+
+ g_value_set_boolean (to_value, string && *string);
+
+ return TRUE;
+}
+
+static void
+gimp_prop_config_notify (GObject *config,
+ GParamSpec *pspec,
+ GtkWidget *widget)
+{
+ GParamSpec *widget_pspec;
+ GWeakRef *label_ref;
+ GtkWidget *label_widget;
+ gboolean sensitive;
+ gboolean visible;
+ gchar *label;
+ gchar *description;
+
+ widget_pspec = g_object_get_data (G_OBJECT (widget), "gimp-prop-pspec");
+ label_ref = g_object_get_data (G_OBJECT (widget), "gimp-prop-label-ref");
+
+ if (label_ref)
+ label_widget = g_weak_ref_get (label_ref);
+ else
+ label_widget = NULL;
+
+ sensitive = gimp_prop_eval_boolean (config, widget_pspec, "sensitive", TRUE);
+ visible = gimp_prop_eval_boolean (config, widget_pspec, "visible", TRUE);
+ label = gimp_prop_eval_string (config, widget_pspec, "label",
+ g_param_spec_get_nick (widget_pspec));
+ description = gimp_prop_eval_string (config, widget_pspec, "description",
+ g_param_spec_get_blurb (widget_pspec));
+
+ /* we store the label in (and pass ownership over it to) the widget's
+ * "gimp-prop-label" key, so that we can use it to initialize the label
+ * widget's label in gimp_prop_gui_bind_label() upon binding.
+ */
+ g_object_set_data_full (G_OBJECT (widget), "gimp-prop-label", label, g_free);
+
+ g_signal_handlers_block_by_func (widget,
+ gimp_prop_widget_show, config);
+
+ gtk_widget_set_sensitive (widget, sensitive);
+ gtk_widget_set_visible (widget, visible);
+ if (label_widget) g_object_set (label_widget, "label", label, NULL);
+ gimp_help_set_help_data (widget, description, NULL);
+
+ g_signal_handlers_unblock_by_func (widget,
+ gimp_prop_widget_show, config);
+
+ g_free (description);
+
+ if (label_widget)
+ g_object_unref (label_widget);
+}
+
+static void
+gimp_prop_widget_show (GtkWidget *widget,
+ GObject *config)
+{
+ GParamSpec *widget_pspec;
+ gboolean visible;
+
+ widget_pspec = g_object_get_data (G_OBJECT (widget), "gimp-prop-pspec");
+
+ visible = gimp_prop_eval_boolean (config, widget_pspec, "visible", TRUE);
+
+ gtk_widget_set_visible (widget, visible);
+}
+
+static void
+gimp_prop_free_label_ref (GWeakRef *label_ref)
+{
+ g_weak_ref_clear (label_ref);
+
+ g_slice_free (GWeakRef, label_ref);
+}