1 /* LIBGIMP - The GIMP Library
2  * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
3  *
4  * gimppropwidgets.c
5  * Copyright (C) 2002-2007  Michael Natterer <mitch@gimp.org>
6  *                          Sven Neumann <sven@gimp.org>
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with this library.  If not, see
15  * <https://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include <string.h>
21 
22 #include <gegl.h>
23 /* FIXME: #undef GTK_DISABLE_DEPRECATED */
24 #undef GTK_DISABLE_DEPRECATED
25 #include <gtk/gtk.h>
26 
27 #include "libgimpcolor/gimpcolor.h"
28 #include "libgimpmath/gimpmath.h"
29 #include "libgimpbase/gimpbase.h"
30 #include "libgimpconfig/gimpconfig.h"
31 
32 #include "gimpwidgetstypes.h"
33 
34 #undef GIMP_DISABLE_DEPRECATED
35 #include "gimpoldwidgets.h"
36 #include "gimppropwidgets.h"
37 #include "gimpspinbutton.h"
38 #include "gimpunitmenu.h"
39 
40 #define GIMP_DISABLE_DEPRECATED
41 #include "gimpwidgets.h"
42 
43 #include "libgimp/libgimp-intl.h"
44 
45 
46 /**
47  * SECTION: gimppropwidgets
48  * @title: GimpPropWidgets
49  * @short_description: Editable views on #GObject properties.
50  *
51  * Editable views on #GObject properties.
52  **/
53 
54 
55 /*  utility function prototypes  */
56 
57 static void         set_param_spec     (GObject     *object,
58                                         GtkWidget   *widget,
59                                         GParamSpec  *param_spec);
60 static void         set_radio_spec     (GObject     *object,
61                                         GParamSpec  *param_spec);
62 static GParamSpec * get_param_spec     (GObject     *object);
63 
64 static GParamSpec * find_param_spec    (GObject     *object,
65                                         const gchar *property_name,
66                                         const gchar *strloc);
67 static GParamSpec * check_param_spec   (GObject     *object,
68                                         const gchar *property_name,
69                                         GType        type,
70                                         const gchar *strloc);
71 static GParamSpec * check_param_spec_w (GObject     *object,
72                                         const gchar *property_name,
73                                         GType        type,
74                                         const gchar *strloc);
75 
76 static gboolean     get_numeric_values (GObject     *object,
77                                         GParamSpec  *param_spec,
78                                         gdouble     *value,
79                                         gdouble     *lower,
80                                         gdouble     *upper,
81                                         const gchar *strloc);
82 
83 static void         connect_notify     (GObject     *config,
84                                         const gchar *property_name,
85                                         GCallback    callback,
86                                         gpointer     callback_data);
87 
88 
89 /******************/
90 /*  check button  */
91 /******************/
92 
93 static void   gimp_prop_check_button_callback (GtkWidget  *widget,
94                                                GObject    *config);
95 static void   gimp_prop_check_button_notify   (GObject    *config,
96                                                GParamSpec *param_spec,
97                                                GtkWidget  *button);
98 
99 /**
100  * gimp_prop_check_button_new:
101  * @config:        Object to which property is attached.
102  * @property_name: Name of boolean property controlled by checkbutton.
103  * @label:         Label to give checkbutton (including mnemonic).
104  *
105  * Creates a #GtkCheckButton that displays and sets the specified
106  * boolean property.
107  * If @label is #NULL, the @property_name's nick will be used as label
108  * of the returned button.
109  *
110  * Return value: The newly created #GtkCheckButton widget.
111  *
112  * Since: 2.4
113  */
114 GtkWidget *
gimp_prop_check_button_new(GObject * config,const gchar * property_name,const gchar * label)115 gimp_prop_check_button_new (GObject     *config,
116                             const gchar *property_name,
117                             const gchar *label)
118 {
119   GParamSpec  *param_spec;
120   GtkWidget   *button;
121   gboolean     value;
122 
123   g_return_val_if_fail (G_IS_OBJECT (config), NULL);
124   g_return_val_if_fail (property_name != NULL, NULL);
125 
126   param_spec = check_param_spec_w (config, property_name,
127                                    G_TYPE_PARAM_BOOLEAN, G_STRFUNC);
128   if (! param_spec)
129     return NULL;
130 
131   if (! label)
132     label = g_param_spec_get_nick (param_spec);
133 
134   g_object_get (config,
135                 property_name, &value,
136                 NULL);
137 
138   button = gtk_check_button_new_with_mnemonic (label);
139   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), value);
140 
141   set_param_spec (G_OBJECT (button), button, param_spec);
142 
143   g_signal_connect (button, "toggled",
144                     G_CALLBACK (gimp_prop_check_button_callback),
145                     config);
146 
147   connect_notify (config, property_name,
148                   G_CALLBACK (gimp_prop_check_button_notify),
149                   button);
150 
151   return button;
152 }
153 
154 static void
gimp_prop_check_button_callback(GtkWidget * widget,GObject * config)155 gimp_prop_check_button_callback (GtkWidget *widget,
156                                  GObject   *config)
157 {
158   GParamSpec *param_spec;
159   gboolean    value;
160   gboolean    v;
161 
162   param_spec = get_param_spec (G_OBJECT (widget));
163   if (! param_spec)
164     return;
165 
166   value = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
167 
168   g_object_get (config, param_spec->name, &v, NULL);
169 
170   if (v != value)
171     {
172       g_object_set (config, param_spec->name, value, NULL);
173 
174       gimp_toggle_button_sensitive_update (GTK_TOGGLE_BUTTON (widget));
175     }
176 }
177 
178 static void
gimp_prop_check_button_notify(GObject * config,GParamSpec * param_spec,GtkWidget * button)179 gimp_prop_check_button_notify (GObject    *config,
180                                GParamSpec *param_spec,
181                                GtkWidget  *button)
182 {
183   gboolean value;
184 
185   g_object_get (config,
186                 param_spec->name, &value,
187                 NULL);
188 
189   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) != value)
190     {
191       g_signal_handlers_block_by_func (button,
192                                        gimp_prop_check_button_callback,
193                                        config);
194 
195       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), value);
196       gimp_toggle_button_sensitive_update (GTK_TOGGLE_BUTTON (button));
197 
198       g_signal_handlers_unblock_by_func (button,
199                                          gimp_prop_check_button_callback,
200                                          config);
201     }
202 }
203 
204 
205 static void   gimp_prop_enum_check_button_callback (GtkWidget  *widget,
206                                                     GObject    *config);
207 static void   gimp_prop_enum_check_button_notify   (GObject    *config,
208                                                     GParamSpec *param_spec,
209                                                     GtkWidget  *button);
210 
211 /**
212  * gimp_prop_enum_check_button_new:
213  * @config:        Object to which property is attached.
214  * @property_name: Name of enum property controlled by checkbutton.
215  * @label:         Label to give checkbutton (including mnemonic).
216  * @false_value:   Enum value corresponding to unchecked state.
217  * @true_value:    Enum value corresponding to checked state.
218  *
219  * Creates a #GtkCheckButton that displays and sets the specified
220  * property of type Enum.  Note that this widget only allows two values
221  * for the enum, one corresponding to the "checked" state and the
222  * other to the "unchecked" state.
223  * If @label is #NULL, the @property_name's nick will be used as label
224  * of the returned button.
225  *
226  * Return value: The newly created #GtkCheckButton widget.
227  *
228  * Since: 2.4
229  */
230 GtkWidget *
gimp_prop_enum_check_button_new(GObject * config,const gchar * property_name,const gchar * label,gint false_value,gint true_value)231 gimp_prop_enum_check_button_new (GObject     *config,
232                                  const gchar *property_name,
233                                  const gchar *label,
234                                  gint         false_value,
235                                  gint         true_value)
236 {
237   GParamSpec  *param_spec;
238   GtkWidget   *button;
239   gint        value;
240 
241   g_return_val_if_fail (G_IS_OBJECT (config), NULL);
242   g_return_val_if_fail (property_name != NULL, NULL);
243 
244   param_spec = check_param_spec_w (config, property_name,
245                                    G_TYPE_PARAM_ENUM, G_STRFUNC);
246   if (! param_spec)
247     return NULL;
248 
249   if (! label)
250     label = g_param_spec_get_nick (param_spec);
251 
252   g_object_get (config,
253                 property_name, &value,
254                 NULL);
255 
256   button = gtk_check_button_new_with_mnemonic (label);
257   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
258                                 value == true_value);
259 
260   if (value != false_value && value != true_value)
261     gtk_toggle_button_set_inconsistent (GTK_TOGGLE_BUTTON (button), TRUE);
262 
263   set_param_spec (G_OBJECT (button), button, param_spec);
264 
265   g_object_set_data (G_OBJECT (button), "false-value",
266                      GINT_TO_POINTER (false_value));
267   g_object_set_data (G_OBJECT (button), "true-value",
268                      GINT_TO_POINTER (true_value));
269 
270   g_signal_connect (button, "toggled",
271                     G_CALLBACK (gimp_prop_enum_check_button_callback),
272                     config);
273 
274   connect_notify (config, property_name,
275                   G_CALLBACK (gimp_prop_enum_check_button_notify),
276                   button);
277 
278   return button;
279 }
280 
281 static void
gimp_prop_enum_check_button_callback(GtkWidget * widget,GObject * config)282 gimp_prop_enum_check_button_callback (GtkWidget *widget,
283                                       GObject   *config)
284 {
285   GParamSpec *param_spec;
286   gint        false_value;
287   gint        true_value;
288   gint        value;
289   gint        v;
290 
291   param_spec = get_param_spec (G_OBJECT (widget));
292   if (! param_spec)
293     return;
294 
295   false_value = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
296                                                     "false-value"));
297   true_value  = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
298                                                     "true-value"));
299 
300   value = (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)) ?
301            true_value : false_value);
302 
303   g_object_get (config, param_spec->name, &v, NULL);
304 
305   if (v != value)
306     {
307       g_object_set (config, param_spec->name, value, NULL);
308 
309       gtk_toggle_button_set_inconsistent (GTK_TOGGLE_BUTTON (widget), FALSE);
310 
311       gimp_toggle_button_sensitive_update (GTK_TOGGLE_BUTTON (widget));
312     }
313 }
314 
315 static void
gimp_prop_enum_check_button_notify(GObject * config,GParamSpec * param_spec,GtkWidget * button)316 gimp_prop_enum_check_button_notify (GObject    *config,
317                                     GParamSpec *param_spec,
318                                     GtkWidget  *button)
319 {
320   gint     value;
321   gint     false_value;
322   gint     true_value;
323   gboolean active       = FALSE;
324   gboolean inconsistent = FALSE;
325 
326   g_object_get (config,
327                 param_spec->name, &value,
328                 NULL);
329 
330   false_value = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button),
331                                                     "false-value"));
332   true_value  = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button),
333                                                     "true-value"));
334 
335   if (value == true_value)
336     active = TRUE;
337   else if (value != false_value)
338     inconsistent = TRUE;
339 
340   gtk_toggle_button_set_inconsistent (GTK_TOGGLE_BUTTON (button),
341                                       inconsistent);
342 
343   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) != active)
344     {
345       g_signal_handlers_block_by_func (button,
346                                        gimp_prop_enum_check_button_callback,
347                                        config);
348 
349       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
350       gimp_toggle_button_sensitive_update (GTK_TOGGLE_BUTTON (button));
351 
352       g_signal_handlers_unblock_by_func (button,
353                                          gimp_prop_enum_check_button_callback,
354                                          config);
355     }
356 }
357 
358 
359 /*************************/
360 /*  int/enum combo box   */
361 /*************************/
362 
363 static void gimp_prop_int_combo_box_callback     (GtkWidget  *widget,
364                                                   GObject    *config);
365 static void gimp_prop_int_combo_box_notify       (GObject    *config,
366                                                   GParamSpec *param_spec,
367                                                   GtkWidget  *widget);
368 
369 static void gimp_prop_pointer_combo_box_callback (GtkWidget  *widget,
370                                                   GObject    *config);
371 static void gimp_prop_pointer_combo_box_notify   (GObject    *config,
372                                                   GParamSpec *param_spec,
373                                                   GtkWidget  *combo_box);
374 
375 /**
376  * gimp_prop_int_combo_box_new:
377  * @config:        Object to which property is attached.
378  * @property_name: Name of int property controlled by combo box.
379  * @store:         #GimpIntStore holding list of labels, values, etc.
380  *
381  * Creates a #GimpIntComboBox widget to display and set the specified
382  * property.  The contents of the widget are determined by @store,
383  * which should be created using gimp_int_store_new().
384  *
385  * Return value: The newly created #GimpIntComboBox widget.
386  *
387  * Since: 2.4
388  */
389 GtkWidget *
gimp_prop_int_combo_box_new(GObject * config,const gchar * property_name,GimpIntStore * store)390 gimp_prop_int_combo_box_new (GObject      *config,
391                              const gchar  *property_name,
392                              GimpIntStore *store)
393 {
394   GParamSpec *param_spec;
395   GtkWidget  *combo_box;
396   gint        value;
397 
398   g_return_val_if_fail (G_IS_OBJECT (config), NULL);
399   g_return_val_if_fail (property_name != NULL, NULL);
400 
401   param_spec = check_param_spec_w (config, property_name,
402                                    G_TYPE_PARAM_INT, G_STRFUNC);
403   if (! param_spec)
404     return NULL;
405 
406   g_object_get (config,
407                 property_name, &value,
408                 NULL);
409 
410   combo_box = g_object_new (GIMP_TYPE_INT_COMBO_BOX,
411                             "model", store,
412                             NULL);
413 
414   gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo_box), value);
415 
416   g_signal_connect (combo_box, "changed",
417                     G_CALLBACK (gimp_prop_int_combo_box_callback),
418                     config);
419 
420   set_param_spec (G_OBJECT (combo_box), combo_box, param_spec);
421 
422   connect_notify (config, property_name,
423                   G_CALLBACK (gimp_prop_int_combo_box_notify),
424                   combo_box);
425 
426   return combo_box;
427 }
428 
429 /**
430  * gimp_prop_pointer_combo_box_new:
431  * @config:        Object to which property is attached.
432  * @property_name: Name of GType/gpointer property controlled by combo box.
433  * @store:         #GimpIntStore holding list of labels, values, etc.
434  *
435  * Creates a #GimpIntComboBox widget to display and set the specified
436  * property.  The contents of the widget are determined by @store,
437  * which should be created using gimp_int_store_new().
438  * Values are GType/gpointer data, and therefore must be stored in the
439  * "user-data" column, instead of the usual "value" column.
440  *
441  * Return value: The newly created #GimpIntComboBox widget.
442  *
443  * Since: 2.10
444  */
445 GtkWidget *
gimp_prop_pointer_combo_box_new(GObject * config,const gchar * property_name,GimpIntStore * store)446 gimp_prop_pointer_combo_box_new (GObject      *config,
447                                  const gchar  *property_name,
448                                  GimpIntStore *store)
449 {
450   GParamSpec *param_spec;
451   GtkWidget  *combo_box;
452   gpointer    property_value;
453 
454   g_return_val_if_fail (G_IS_OBJECT (config), NULL);
455   g_return_val_if_fail (property_name != NULL, NULL);
456 
457   param_spec = check_param_spec_w (config, property_name,
458                                    G_TYPE_PARAM_GTYPE, G_STRFUNC);
459   if (! param_spec)
460     {
461       param_spec = check_param_spec_w (config, property_name,
462                                        G_TYPE_PARAM_POINTER, G_STRFUNC);
463       if (! param_spec)
464         return NULL;
465     }
466 
467   g_object_get (config,
468                 property_name, &property_value,
469                 NULL);
470 
471   /* We use a GimpIntComboBox but we cannot store gpointer in the
472    * "value" column, because gpointer is not a subset of gint. Instead
473    * we store the value in the "user-data" column which is a gpointer.
474    */
475   combo_box = g_object_new (GIMP_TYPE_INT_COMBO_BOX,
476                             "model", store,
477                             NULL);
478 
479   gimp_int_combo_box_set_active_by_user_data (GIMP_INT_COMBO_BOX (combo_box),
480                                               property_value);
481 
482   g_signal_connect (combo_box, "changed",
483                     G_CALLBACK (gimp_prop_pointer_combo_box_callback),
484                     config);
485 
486   set_param_spec (G_OBJECT (combo_box), combo_box, param_spec);
487 
488   connect_notify (config, property_name,
489                   G_CALLBACK (gimp_prop_pointer_combo_box_notify),
490                   combo_box);
491 
492   return combo_box;
493 }
494 
495 /**
496  * gimp_prop_enum_combo_box_new:
497  * @config:        Object to which property is attached.
498  * @property_name: Name of enum property controlled by combo box.
499  * @minimum:       Smallest allowed value of enum.
500  * @maximum:       Largest allowed value of enum.
501  *
502  * Creates a #GimpIntComboBox widget to display and set the specified
503  * enum property.  The @mimimum_value and @maximum_value give the
504  * possibility of restricting the allowed range to a subset of the
505  * enum.  If the two values are equal (e.g., 0, 0), then the full
506  * range of the Enum is used.
507  *
508  * Return value: The newly created #GimpEnumComboBox widget.
509  *
510  * Since: 2.4
511  */
512 GtkWidget *
gimp_prop_enum_combo_box_new(GObject * config,const gchar * property_name,gint minimum,gint maximum)513 gimp_prop_enum_combo_box_new (GObject     *config,
514                               const gchar *property_name,
515                               gint         minimum,
516                               gint         maximum)
517 {
518   GParamSpec   *param_spec;
519   GtkListStore *store = NULL;
520   GtkWidget    *combo_box;
521   gint          value;
522 
523   g_return_val_if_fail (G_IS_OBJECT (config), NULL);
524   g_return_val_if_fail (property_name != NULL, NULL);
525 
526   param_spec = check_param_spec_w (config, property_name,
527                                    G_TYPE_PARAM_ENUM, G_STRFUNC);
528   if (! param_spec)
529     return NULL;
530 
531   g_object_get (config,
532                 property_name, &value,
533                 NULL);
534 
535   if (minimum != maximum)
536     {
537       store = gimp_enum_store_new_with_range (param_spec->value_type,
538                                               minimum, maximum);
539     }
540   else if (param_spec->value_type == GIMP_TYPE_DESATURATE_MODE)
541     {
542       /* this is a bad hack, if we get more of those, we should probably
543        * think of something less ugly
544        */
545       store = gimp_enum_store_new_with_values (param_spec->value_type,
546                                                5,
547                                                GIMP_DESATURATE_LUMINANCE,
548                                                GIMP_DESATURATE_LUMA,
549                                                GIMP_DESATURATE_LIGHTNESS,
550                                                GIMP_DESATURATE_AVERAGE,
551                                                GIMP_DESATURATE_VALUE);
552     }
553   else if (param_spec->value_type == GIMP_TYPE_SELECT_CRITERION)
554     {
555       /* ditto */
556       store = gimp_enum_store_new_with_values (param_spec->value_type,
557                                                12,
558                                                GIMP_SELECT_CRITERION_COMPOSITE,
559                                                GIMP_SELECT_CRITERION_R,
560                                                GIMP_SELECT_CRITERION_G,
561                                                GIMP_SELECT_CRITERION_B,
562                                                GIMP_SELECT_CRITERION_A,
563                                                GIMP_SELECT_CRITERION_H,
564                                                GIMP_SELECT_CRITERION_S,
565                                                GIMP_SELECT_CRITERION_V,
566                                                GIMP_SELECT_CRITERION_LCH_L,
567                                                GIMP_SELECT_CRITERION_LCH_C,
568                                                GIMP_SELECT_CRITERION_LCH_H);
569     }
570 
571   if (store)
572     {
573       combo_box = g_object_new (GIMP_TYPE_ENUM_COMBO_BOX,
574                                 "model", store,
575                                 NULL);
576       g_object_unref (store);
577     }
578   else
579     {
580       combo_box = gimp_enum_combo_box_new (param_spec->value_type);
581     }
582 
583   gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo_box), value);
584 
585   g_signal_connect (combo_box, "changed",
586                     G_CALLBACK (gimp_prop_int_combo_box_callback),
587                     config);
588 
589   set_param_spec (G_OBJECT (combo_box), combo_box, param_spec);
590 
591   connect_notify (config, property_name,
592                   G_CALLBACK (gimp_prop_int_combo_box_notify),
593                   combo_box);
594 
595   return combo_box;
596 }
597 
598 static void
gimp_prop_int_combo_box_callback(GtkWidget * widget,GObject * config)599 gimp_prop_int_combo_box_callback (GtkWidget *widget,
600                                   GObject   *config)
601 {
602   GParamSpec  *param_spec;
603   gint         value;
604 
605   param_spec = get_param_spec (G_OBJECT (widget));
606   if (! param_spec)
607     return;
608 
609   if (gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &value))
610     {
611       gint v;
612 
613       g_object_get (config, param_spec->name, &v, NULL);
614 
615       if (v != value)
616         g_object_set (config, param_spec->name, value, NULL);
617     }
618 }
619 
620 static void
gimp_prop_int_combo_box_notify(GObject * config,GParamSpec * param_spec,GtkWidget * combo_box)621 gimp_prop_int_combo_box_notify (GObject    *config,
622                                 GParamSpec *param_spec,
623                                 GtkWidget  *combo_box)
624 {
625   gint value;
626 
627   g_object_get (config,
628                 param_spec->name, &value,
629                 NULL);
630 
631   g_signal_handlers_block_by_func (combo_box,
632                                    gimp_prop_int_combo_box_callback,
633                                    config);
634 
635   gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo_box), value);
636 
637   g_signal_handlers_unblock_by_func (combo_box,
638                                      gimp_prop_int_combo_box_callback,
639                                      config);
640 }
641 
642 static void
gimp_prop_pointer_combo_box_callback(GtkWidget * widget,GObject * config)643 gimp_prop_pointer_combo_box_callback (GtkWidget *widget,
644                                       GObject   *config)
645 {
646   GParamSpec *param_spec;
647   gpointer    value;
648 
649   param_spec = get_param_spec (G_OBJECT (widget));
650   if (! param_spec)
651     return;
652 
653   if (gimp_int_combo_box_get_active_user_data (GIMP_INT_COMBO_BOX (widget),
654                                                &value))
655     {
656       gpointer v;
657 
658       g_object_get (config, param_spec->name, &v, NULL);
659 
660       if (v != value)
661         g_object_set (config, param_spec->name, value, NULL);
662     }
663 }
664 
665 static void
gimp_prop_pointer_combo_box_notify(GObject * config,GParamSpec * param_spec,GtkWidget * combo_box)666 gimp_prop_pointer_combo_box_notify (GObject    *config,
667                                     GParamSpec *param_spec,
668                                     GtkWidget  *combo_box)
669 {
670   gpointer value;
671 
672   g_object_get (config,
673                 param_spec->name, &value,
674                 NULL);
675 
676   g_signal_handlers_block_by_func (combo_box,
677                                    gimp_prop_pointer_combo_box_callback,
678                                    config);
679 
680   gimp_int_combo_box_set_active_by_user_data (GIMP_INT_COMBO_BOX (combo_box),
681                                               value);
682 
683   g_signal_handlers_unblock_by_func (combo_box,
684                                      gimp_prop_pointer_combo_box_callback,
685                                      config);
686 }
687 
688 /************************/
689 /*  boolean combo box   */
690 /************************/
691 
692 static void   gimp_prop_boolean_combo_box_callback (GtkWidget   *combo,
693                                                     GObject     *config);
694 static void   gimp_prop_boolean_combo_box_notify   (GObject     *config,
695                                                     GParamSpec  *param_spec,
696                                                     GtkWidget   *combo);
697 
698 
699 /**
700  * gimp_prop_boolean_combo_box_new:
701  * @config:        Object to which property is attached.
702  * @property_name: Name of boolean property controlled by combo box.
703  * @true_text:     Label used for entry corresponding to %TRUE value.
704  * @false_text:    Label used for entry corresponding to %FALSE value.
705  *
706  * Creates a #GtkComboBox widget to display and set the specified
707  * boolean property.  The combo box will have two entries, one
708  * displaying the @true_text label, the other displaying the
709  * @false_text label.
710  *
711  * Return value: The newly created #GtkComboBox widget.
712  *
713  * Since: 2.4
714  */
715 GtkWidget *
gimp_prop_boolean_combo_box_new(GObject * config,const gchar * property_name,const gchar * true_text,const gchar * false_text)716 gimp_prop_boolean_combo_box_new (GObject     *config,
717                                  const gchar *property_name,
718                                  const gchar *true_text,
719                                  const gchar *false_text)
720 {
721   GParamSpec *param_spec;
722   GtkWidget  *combo;
723   gboolean    value;
724 
725   g_return_val_if_fail (G_IS_OBJECT (config), NULL);
726   g_return_val_if_fail (property_name != NULL, NULL);
727 
728   param_spec = check_param_spec_w (config, property_name,
729                                    G_TYPE_PARAM_BOOLEAN, G_STRFUNC);
730   if (! param_spec)
731     return NULL;
732 
733   g_object_get (config,
734                 property_name, &value,
735                 NULL);
736 
737   combo = gimp_int_combo_box_new (true_text,  TRUE,
738                                   false_text, FALSE,
739                                   NULL);
740 
741   gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), value);
742 
743   g_signal_connect (combo, "changed",
744                     G_CALLBACK (gimp_prop_boolean_combo_box_callback),
745                     config);
746 
747   set_param_spec (G_OBJECT (combo), combo, param_spec);
748 
749   connect_notify (config, property_name,
750                   G_CALLBACK (gimp_prop_boolean_combo_box_notify),
751                   combo);
752 
753   return combo;
754 }
755 
756 static void
gimp_prop_boolean_combo_box_callback(GtkWidget * combo,GObject * config)757 gimp_prop_boolean_combo_box_callback (GtkWidget *combo,
758                                       GObject   *config)
759 {
760   GParamSpec  *param_spec;
761   gint         value;
762 
763   param_spec = get_param_spec (G_OBJECT (combo));
764   if (! param_spec)
765     return;
766 
767   if (gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo), &value))
768     {
769       gint v;
770 
771       g_object_get (config, param_spec->name, &v, NULL);
772 
773       if (v != value)
774         g_object_set (config, param_spec->name, value, NULL);
775     }
776 }
777 
778 static void
gimp_prop_boolean_combo_box_notify(GObject * config,GParamSpec * param_spec,GtkWidget * combo)779 gimp_prop_boolean_combo_box_notify (GObject    *config,
780                                     GParamSpec *param_spec,
781                                     GtkWidget  *combo)
782 {
783   gboolean value;
784 
785   g_object_get (config,
786                 param_spec->name, &value,
787                 NULL);
788 
789   g_signal_handlers_block_by_func (combo,
790                                    gimp_prop_boolean_combo_box_callback,
791                                    config);
792 
793   gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), value);
794 
795   g_signal_handlers_unblock_by_func (combo,
796                                      gimp_prop_boolean_combo_box_callback,
797                                      config);
798 }
799 
800 
801 /*****************/
802 /*  radio boxes  */
803 /*****************/
804 
805 static void  gimp_prop_radio_button_callback (GtkWidget   *widget,
806                                               GObject     *config);
807 static void  gimp_prop_radio_button_notify   (GObject     *config,
808                                               GParamSpec  *param_spec,
809                                               GtkWidget   *button);
810 
811 
812 /**
813  * gimp_prop_enum_radio_frame_new:
814  * @config:        Object to which property is attached.
815  * @property_name: Name of enum property controlled by the radio buttons.
816  * @title:         Label for the frame holding the buttons
817  * @minimum:       Smallest value of enum to be included.
818  * @maximum:       Largest value of enum to be included.
819  *
820  * Creates a group of radio buttons which function to set and display
821  * the specified enum property.  The @minimum and @maximum arguments
822  * allow only a subset of the enum to be used.  If the two arguments
823  * are equal (e.g., 0, 0), then the full range of the enum will be used.
824  * If @title is #NULL, the @property_name's nick will be used as label
825  * of the returned frame.
826  *
827  * Return value: A #GimpFrame containing the radio buttons.
828  *
829  * Since: 2.4
830  */
831 GtkWidget *
gimp_prop_enum_radio_frame_new(GObject * config,const gchar * property_name,const gchar * title,gint minimum,gint maximum)832 gimp_prop_enum_radio_frame_new (GObject     *config,
833                                 const gchar *property_name,
834                                 const gchar *title,
835                                 gint         minimum,
836                                 gint         maximum)
837 {
838   GParamSpec *param_spec;
839   GtkWidget  *frame;
840   GtkWidget  *button;
841   gint        value;
842 
843   g_return_val_if_fail (G_IS_OBJECT (config), NULL);
844   g_return_val_if_fail (property_name != NULL, NULL);
845 
846   param_spec = check_param_spec_w (config, property_name,
847                                    G_TYPE_PARAM_ENUM, G_STRFUNC);
848   if (! param_spec)
849     return NULL;
850 
851   if (! title)
852     title = g_param_spec_get_nick (param_spec);
853 
854   g_object_get (config,
855                 property_name, &value,
856                 NULL);
857 
858   if (minimum != maximum)
859     {
860       frame = gimp_enum_radio_frame_new_with_range (param_spec->value_type,
861                                                     minimum, maximum,
862                                                     gtk_label_new (title),
863                                                     G_CALLBACK (gimp_prop_radio_button_callback),
864                                                     config,
865                                                     &button);
866     }
867   else
868     {
869       frame = gimp_enum_radio_frame_new (param_spec->value_type,
870                                          gtk_label_new (title),
871                                          G_CALLBACK (gimp_prop_radio_button_callback),
872                                          config,
873                                          &button);
874     }
875 
876   gimp_int_radio_group_set_active (GTK_RADIO_BUTTON (button), value);
877 
878   set_radio_spec (G_OBJECT (button), param_spec);
879 
880   connect_notify (config, property_name,
881                   G_CALLBACK (gimp_prop_radio_button_notify),
882                   button);
883 
884   g_object_set_data (G_OBJECT (frame), "radio-button", button);
885 
886   return frame;
887 }
888 
889 /**
890  * gimp_prop_enum_radio_box_new:
891  * @config:        Object to which property is attached.
892  * @property_name: Name of enum property controlled by the radio buttons.
893  * @minimum:       Smallest value of enum to be included.
894  * @maximum:       Largest value of enum to be included.
895  *
896  * Creates a group of radio buttons which function to set and display
897  * the specified enum property.  The @minimum and @maximum arguments
898  * allow only a subset of the enum to be used.  If the two arguments
899  * are equal (e.g., 0, 0), then the full range of the enum will be used.
900  * If you want to assign a label to the group of radio buttons, use
901  * gimp_prop_enum_radio_frame_new() instead of this function.
902  *
903  * Return value: A #GtkVBox containing the radio buttons.
904  *
905  * Since: 2.4
906  */
907 GtkWidget *
gimp_prop_enum_radio_box_new(GObject * config,const gchar * property_name,gint minimum,gint maximum)908 gimp_prop_enum_radio_box_new (GObject     *config,
909                               const gchar *property_name,
910                               gint         minimum,
911                               gint         maximum)
912 {
913   GParamSpec *param_spec;
914   GtkWidget  *vbox;
915   GtkWidget  *button;
916   gint        value;
917 
918   g_return_val_if_fail (G_IS_OBJECT (config), NULL);
919   g_return_val_if_fail (property_name != NULL, NULL);
920 
921   param_spec = check_param_spec_w (config, property_name,
922                                    G_TYPE_PARAM_ENUM, G_STRFUNC);
923   if (! param_spec)
924     return NULL;
925 
926   g_object_get (config,
927                 property_name, &value,
928                 NULL);
929 
930   if (minimum != maximum)
931     {
932       vbox = gimp_enum_radio_box_new_with_range (param_spec->value_type,
933                                                  minimum, maximum,
934                                                  G_CALLBACK (gimp_prop_radio_button_callback),
935                                                  config,
936                                                  &button);
937     }
938   else
939     {
940       vbox = gimp_enum_radio_box_new (param_spec->value_type,
941                                       G_CALLBACK (gimp_prop_radio_button_callback),
942                                       config,
943                                       &button);
944     }
945 
946   gimp_int_radio_group_set_active (GTK_RADIO_BUTTON (button), value);
947 
948   set_radio_spec (G_OBJECT (button), param_spec);
949 
950   connect_notify (config, property_name,
951                   G_CALLBACK (gimp_prop_radio_button_notify),
952                   button);
953 
954   g_object_set_data (G_OBJECT (vbox), "radio-button", button);
955 
956   return vbox;
957 }
958 
959 
960 /***********/
961 /*  label  */
962 /***********/
963 
964 static void  gimp_prop_enum_label_notify (GObject    *config,
965                                           GParamSpec *param_spec,
966                                           GtkWidget  *label);
967 
968 /**
969  * gimp_prop_enum_label_new:
970  * @config:         Object to which property is attached.
971  * @property_name:  Name of enum property to be displayed.
972  *
973  * Return value: The newly created #GimpEnumLabel widget.
974  *
975  * Since: 2.4
976  */
977 GtkWidget *
gimp_prop_enum_label_new(GObject * config,const gchar * property_name)978 gimp_prop_enum_label_new (GObject     *config,
979                           const gchar *property_name)
980 {
981   GParamSpec *param_spec;
982   GtkWidget  *label;
983   gint        value;
984 
985   g_return_val_if_fail (G_IS_OBJECT (config), NULL);
986   g_return_val_if_fail (property_name != NULL, NULL);
987 
988   param_spec = check_param_spec (config, property_name,
989                                  G_TYPE_PARAM_ENUM, G_STRFUNC);
990   if (! param_spec)
991     return NULL;
992 
993   g_object_get (config,
994                 property_name, &value,
995                 NULL);
996 
997   label = gimp_enum_label_new (param_spec->value_type, value);
998 
999   set_param_spec (G_OBJECT (label), label, param_spec);
1000 
1001   connect_notify (config, property_name,
1002                   G_CALLBACK (gimp_prop_enum_label_notify),
1003                   label);
1004 
1005   return label;
1006 }
1007 
1008 static void
gimp_prop_enum_label_notify(GObject * config,GParamSpec * param_spec,GtkWidget * label)1009 gimp_prop_enum_label_notify (GObject    *config,
1010                              GParamSpec *param_spec,
1011                              GtkWidget  *label)
1012 {
1013   gint value;
1014 
1015   g_object_get (config,
1016                 param_spec->name, &value,
1017                 NULL);
1018 
1019   gimp_enum_label_set_value (GIMP_ENUM_LABEL (label), value);
1020 }
1021 
1022 
1023 /**
1024  * gimp_prop_boolean_radio_frame_new:
1025  * @config:        Object to which property is attached.
1026  * @property_name: Name of boolean property controlled by the radio buttons.
1027  * @title:         Label for the frame.
1028  * @true_text:     Label for the button corresponding to %TRUE.
1029  * @false_text:    Label for the button corresponding to %FALSE.
1030  *
1031  * Creates a pair of radio buttons which function to set and display
1032  * the specified boolean property.
1033  * If @title is #NULL, the @property_name's nick will be used as label
1034  * of the returned frame.
1035  *
1036  * Return value: A #GimpFrame containing the radio buttons.
1037  *
1038  * Since: 2.4
1039  */
1040 GtkWidget *
gimp_prop_boolean_radio_frame_new(GObject * config,const gchar * property_name,const gchar * title,const gchar * true_text,const gchar * false_text)1041 gimp_prop_boolean_radio_frame_new (GObject     *config,
1042                                    const gchar *property_name,
1043                                    const gchar *title,
1044                                    const gchar *true_text,
1045                                    const gchar *false_text)
1046 {
1047   GParamSpec *param_spec;
1048   GtkWidget  *frame;
1049   GtkWidget  *button;
1050   gboolean    value;
1051 
1052   g_return_val_if_fail (G_IS_OBJECT (config), NULL);
1053   g_return_val_if_fail (property_name != NULL, NULL);
1054 
1055   param_spec = check_param_spec_w (config, property_name,
1056                                    G_TYPE_PARAM_BOOLEAN, G_STRFUNC);
1057   if (! param_spec)
1058     return NULL;
1059 
1060   if (! title)
1061     title = g_param_spec_get_nick (param_spec);
1062 
1063   g_object_get (config,
1064                 property_name, &value,
1065                 NULL);
1066 
1067   frame =
1068     gimp_int_radio_group_new (TRUE, title,
1069                               G_CALLBACK (gimp_prop_radio_button_callback),
1070                               config, value,
1071 
1072                               false_text, FALSE, &button,
1073                               true_text,  TRUE,  NULL,
1074 
1075                               NULL);
1076 
1077   set_radio_spec (G_OBJECT (button), param_spec);
1078 
1079   connect_notify (config, property_name,
1080                   G_CALLBACK (gimp_prop_radio_button_notify),
1081                   button);
1082 
1083   g_object_set_data (G_OBJECT (frame), "radio-button", button);
1084 
1085   return frame;
1086 }
1087 
1088 /**
1089  * gimp_prop_enum_stock_box_new:
1090  * @config:        Object to which property is attached.
1091  * @property_name: Name of enum property controlled by the radio buttons.
1092  * @stock_prefix:  The prefix of the group of stock ids to use.
1093  * @minimum:       Smallest value of enum to be included.
1094  * @maximum:       Largest value of enum to be included.
1095  *
1096  * Creates a horizontal box of radio buttons with stock icons, which
1097  * function to set and display the value of the specified Enum
1098  * property.  The stock_id for each icon is created by appending the
1099  * enum_value's nick to the given @stock_prefix.  See
1100  * gimp_enum_stock_box_new() for more information.
1101  *
1102  * Return value: A #libgimpwidgets-gimpenumstockbox containing the radio buttons.
1103  *
1104  * Since: 2.4
1105  *
1106  * Deprecated: 2.10
1107  */
1108 GtkWidget *
gimp_prop_enum_stock_box_new(GObject * config,const gchar * property_name,const gchar * stock_prefix,gint minimum,gint maximum)1109 gimp_prop_enum_stock_box_new (GObject     *config,
1110                               const gchar *property_name,
1111                               const gchar *stock_prefix,
1112                               gint         minimum,
1113                               gint         maximum)
1114 {
1115   return gimp_prop_enum_icon_box_new (config, property_name,
1116                                       stock_prefix, minimum, maximum);
1117 }
1118 
1119 /**
1120  * gimp_prop_enum_icon_box_new:
1121  * @config:        Object to which property is attached.
1122  * @property_name: Name of enum property controlled by the radio buttons.
1123  * @icon_prefix:   The prefix of the group of icon names to use.
1124  * @minimum:       Smallest value of enum to be included.
1125  * @maximum:       Largest value of enum to be included.
1126  *
1127  * Creates a horizontal box of radio buttons with named icons, which
1128  * function to set and display the value of the specified Enum
1129  * property.  The icon name for each icon is created by appending the
1130  * enum_value's nick to the given @icon_prefix.  See
1131  * gimp_enum_icon_box_new() for more information.
1132  *
1133  * Return value: A #libgimpwidgets-gimpenumiconbox containing the radio buttons.
1134  *
1135  * Since: 2.10
1136  */
1137 GtkWidget *
gimp_prop_enum_icon_box_new(GObject * config,const gchar * property_name,const gchar * icon_prefix,gint minimum,gint maximum)1138 gimp_prop_enum_icon_box_new (GObject     *config,
1139                              const gchar *property_name,
1140                              const gchar *icon_prefix,
1141                              gint         minimum,
1142                              gint         maximum)
1143 {
1144   GParamSpec *param_spec;
1145   GtkWidget  *box;
1146   GtkWidget  *button;
1147   gint        value;
1148 
1149   g_return_val_if_fail (G_IS_OBJECT (config), NULL);
1150   g_return_val_if_fail (property_name != NULL, NULL);
1151 
1152   param_spec = check_param_spec_w (config, property_name,
1153                                    G_TYPE_PARAM_ENUM, G_STRFUNC);
1154   if (! param_spec)
1155     return NULL;
1156 
1157   g_object_get (config,
1158                 property_name, &value,
1159                 NULL);
1160 
1161   if (minimum != maximum)
1162     {
1163       box = gimp_enum_icon_box_new_with_range (param_spec->value_type,
1164                                                minimum, maximum,
1165                                                icon_prefix,
1166                                                GTK_ICON_SIZE_MENU,
1167                                                G_CALLBACK (gimp_prop_radio_button_callback),
1168                                                config,
1169                                                &button);
1170     }
1171   else
1172     {
1173       box = gimp_enum_icon_box_new (param_spec->value_type,
1174                                     icon_prefix,
1175                                     GTK_ICON_SIZE_MENU,
1176                                     G_CALLBACK (gimp_prop_radio_button_callback),
1177                                     config,
1178                                     &button);
1179     }
1180 
1181   gimp_int_radio_group_set_active (GTK_RADIO_BUTTON (button), value);
1182 
1183   set_radio_spec (G_OBJECT (button), param_spec);
1184 
1185   connect_notify (config, property_name,
1186                   G_CALLBACK (gimp_prop_radio_button_notify),
1187                   button);
1188 
1189   return box;
1190 }
1191 
1192 static void
gimp_prop_radio_button_callback(GtkWidget * widget,GObject * config)1193 gimp_prop_radio_button_callback (GtkWidget *widget,
1194                                  GObject   *config)
1195 {
1196   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
1197     {
1198       GParamSpec *param_spec;
1199       gint        value;
1200       gint        v;
1201 
1202       param_spec = get_param_spec (G_OBJECT (widget));
1203       if (! param_spec)
1204         return;
1205 
1206       value = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
1207                                                   "gimp-item-data"));
1208 
1209       g_object_get (config, param_spec->name, &v, NULL);
1210 
1211       if (v != value)
1212         g_object_set (config, param_spec->name, value, NULL);
1213     }
1214 }
1215 
1216 static void
gimp_prop_radio_button_notify(GObject * config,GParamSpec * param_spec,GtkWidget * button)1217 gimp_prop_radio_button_notify (GObject    *config,
1218                                GParamSpec *param_spec,
1219                                GtkWidget  *button)
1220 {
1221   gint value;
1222 
1223   g_object_get (config,
1224                 param_spec->name, &value,
1225                 NULL);
1226 
1227   gimp_int_radio_group_set_active (GTK_RADIO_BUTTON (button), value);
1228 }
1229 
1230 
1231 /*****************/
1232 /*  adjustments  */
1233 /*****************/
1234 
1235 static void   gimp_prop_adjustment_callback (GtkAdjustment *adjustment,
1236                                              GObject       *config);
1237 static void   gimp_prop_adjustment_notify   (GObject       *config,
1238                                              GParamSpec    *param_spec,
1239                                              GtkAdjustment *adjustment);
1240 
1241 /**
1242  * gimp_prop_spin_button_new:
1243  * @config:            Object to which property is attached.
1244  * @property_name:     Name of double property controlled by the spin button.
1245  * @step_increment:    Step size.
1246  * @page_increment:    Page size.
1247  * @digits:            Number of digits after decimal point to display.
1248  *
1249  * Creates a spin button to set and display the value of the
1250  * specified double property.
1251  *
1252  * Return value: A new #libgimpwidgets-gimpspinbutton.
1253  *
1254  * Since: 2.4
1255  */
1256 GtkWidget *
gimp_prop_spin_button_new(GObject * config,const gchar * property_name,gdouble step_increment,gdouble page_increment,gint digits)1257 gimp_prop_spin_button_new (GObject     *config,
1258                            const gchar *property_name,
1259                            gdouble      step_increment,
1260                            gdouble      page_increment,
1261                            gint         digits)
1262 {
1263   GParamSpec    *param_spec;
1264   GtkWidget     *spinbutton;
1265   GtkAdjustment *adjustment;
1266   gdouble        value;
1267   gdouble        lower;
1268   gdouble        upper;
1269 
1270   param_spec = find_param_spec (config, property_name, G_STRFUNC);
1271   if (! param_spec)
1272     return NULL;
1273 
1274   if (! get_numeric_values (config,
1275                             param_spec, &value, &lower, &upper, G_STRFUNC))
1276     return NULL;
1277 
1278   if (! G_IS_PARAM_SPEC_DOUBLE (param_spec))
1279     digits = 0;
1280 
1281   adjustment = (GtkAdjustment *)
1282     gtk_adjustment_new (value, lower, upper,
1283                         step_increment, page_increment, 0);
1284 
1285   spinbutton = gimp_spin_button_new (adjustment, step_increment, digits);
1286   gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
1287 
1288   set_param_spec (G_OBJECT (adjustment), spinbutton, param_spec);
1289 
1290   g_signal_connect (adjustment, "value-changed",
1291                     G_CALLBACK (gimp_prop_adjustment_callback),
1292                     config);
1293 
1294   connect_notify (config, property_name,
1295                   G_CALLBACK (gimp_prop_adjustment_notify),
1296                   adjustment);
1297 
1298   return spinbutton;
1299 }
1300 
1301 /**
1302  * gimp_prop_hscale_new:
1303  * @config:         Object to which property is attached.
1304  * @property_name:  Name of integer or double property controlled by the scale.
1305  * @step_increment: Step size.
1306  * @page_increment: Page size.
1307  * @digits:         Number of digits after decimal point to display.
1308  *
1309  * Creates a horizontal scale to control the value of the specified
1310  * integer or double property.
1311  *
1312  * Return value: A new #GtkScale.
1313  *
1314  * Since: 2.4
1315  */
1316 GtkWidget *
gimp_prop_hscale_new(GObject * config,const gchar * property_name,gdouble step_increment,gdouble page_increment,gint digits)1317 gimp_prop_hscale_new (GObject     *config,
1318                       const gchar *property_name,
1319                       gdouble      step_increment,
1320                       gdouble      page_increment,
1321                       gint         digits)
1322 {
1323   GParamSpec    *param_spec;
1324   GtkWidget     *scale;
1325   GtkAdjustment *adjustment;
1326   gdouble        value;
1327   gdouble        lower;
1328   gdouble        upper;
1329 
1330   param_spec = find_param_spec (config, property_name, G_STRFUNC);
1331   if (! param_spec)
1332     return NULL;
1333 
1334   if (! get_numeric_values (config,
1335                             param_spec, &value, &lower, &upper, G_STRFUNC))
1336     return NULL;
1337 
1338   if (! G_IS_PARAM_SPEC_DOUBLE (param_spec))
1339     digits = 0;
1340 
1341   adjustment = (GtkAdjustment *)
1342     gtk_adjustment_new (value, lower, upper,
1343                         step_increment, page_increment, 0.0);
1344 
1345   scale = g_object_new (GTK_TYPE_SCALE,
1346                         "orientation", GTK_ORIENTATION_HORIZONTAL,
1347                         "adjustment",  adjustment,
1348                         "digits",      digits,
1349                         NULL);
1350 
1351   set_param_spec (G_OBJECT (adjustment), scale, param_spec);
1352 
1353   g_signal_connect (adjustment, "value-changed",
1354                     G_CALLBACK (gimp_prop_adjustment_callback),
1355                     config);
1356 
1357   connect_notify (config, property_name,
1358                   G_CALLBACK (gimp_prop_adjustment_notify),
1359                   adjustment);
1360 
1361   return scale;
1362 }
1363 
1364 /**
1365  * gimp_prop_scale_entry_new:
1366  * @config:         Object to which property is attached.
1367  * @property_name:  Name of double property controlled by the spin button.
1368  * @table:          The #GtkTable the widgets will be attached to.
1369  * @column:         The column to start with.
1370  * @row:            The row to attach the widgets.
1371  * @label:          The text for the #GtkLabel which will appear left of
1372  *                  the #GtkHScale.
1373  * @step_increment: Step size.
1374  * @page_increment: Page size.
1375  * @digits:         Number of digits after decimal point to display.
1376  * @limit_scale:    %FALSE if the range of possible values of the
1377  *                  GtkHScale should be the same as of the GtkSpinButton.
1378  * @lower_limit:    The scale's lower boundary if @scale_limits is %TRUE.
1379  * @upper_limit:    The scale's upper boundary if @scale_limits is %TRUE.
1380  *
1381  * Creates a #libgimpwidgets-gimpscaleentry (slider and spin button)
1382  * to set and display the value of the specified double property.  See
1383  * gimp_scale_entry_new() for more information.
1384  * If @label is #NULL, the @property_name's nick will be used as label
1385  * of the returned object.
1386  *
1387  * Note that the @scale_limits boolean is the inverse of
1388  * gimp_scale_entry_new()'s "constrain" parameter.
1389  *
1390  * Return value: The #GtkSpinButton's #GtkAdjustment.
1391  *
1392  * Since: 2.4
1393  */
1394 GtkObject *
gimp_prop_scale_entry_new(GObject * config,const gchar * property_name,GtkTable * table,gint column,gint row,const gchar * label,gdouble step_increment,gdouble page_increment,gint digits,gboolean limit_scale,gdouble lower_limit,gdouble upper_limit)1395 gimp_prop_scale_entry_new (GObject     *config,
1396                            const gchar *property_name,
1397                            GtkTable    *table,
1398                            gint         column,
1399                            gint         row,
1400                            const gchar *label,
1401                            gdouble      step_increment,
1402                            gdouble      page_increment,
1403                            gint         digits,
1404                            gboolean     limit_scale,
1405                            gdouble      lower_limit,
1406                            gdouble      upper_limit)
1407 {
1408   GParamSpec  *param_spec;
1409   GtkObject   *adjustment;
1410   const gchar *tooltip;
1411   gdouble      value;
1412   gdouble      lower;
1413   gdouble      upper;
1414 
1415   param_spec = find_param_spec (config, property_name, G_STRFUNC);
1416   if (! param_spec)
1417     return NULL;
1418 
1419   if (! get_numeric_values (config,
1420                             param_spec, &value, &lower, &upper, G_STRFUNC))
1421     return NULL;
1422 
1423   if (! label)
1424     label = g_param_spec_get_nick (param_spec);
1425 
1426   tooltip = g_param_spec_get_blurb (param_spec);
1427 
1428   if (! limit_scale)
1429     {
1430       adjustment = gimp_scale_entry_new (table, column, row,
1431                                          label, -1, -1,
1432                                          value, lower, upper,
1433                                          step_increment, page_increment,
1434                                          digits,
1435                                          TRUE, 0.0, 0.0,
1436                                          tooltip,
1437                                          NULL);
1438     }
1439   else
1440     {
1441       adjustment = gimp_scale_entry_new (table, column, row,
1442                                          label, -1, -1,
1443                                          value, lower_limit, upper_limit,
1444                                          step_increment, page_increment,
1445                                          digits,
1446                                          FALSE, lower, upper,
1447                                          tooltip,
1448                                          NULL);
1449     }
1450 
1451   set_param_spec (G_OBJECT (adjustment), NULL,  param_spec);
1452 
1453   g_signal_connect (adjustment, "value-changed",
1454                     G_CALLBACK (gimp_prop_adjustment_callback),
1455                     config);
1456 
1457   connect_notify (config, property_name,
1458                   G_CALLBACK (gimp_prop_adjustment_notify),
1459                   adjustment);
1460 
1461   return adjustment;
1462 }
1463 
1464 static void
gimp_prop_widget_set_factor(GtkWidget * widget,GtkAdjustment * adjustment,gdouble factor,gdouble step_increment,gdouble page_increment,gint digits)1465 gimp_prop_widget_set_factor (GtkWidget     *widget,
1466                              GtkAdjustment *adjustment,
1467                              gdouble        factor,
1468                              gdouble        step_increment,
1469                              gdouble        page_increment,
1470                              gint           digits)
1471 {
1472   gdouble *factor_store;
1473   gdouble  old_factor = 1.0;
1474   gdouble  f;
1475 
1476   g_return_if_fail (widget == NULL || GTK_IS_SPIN_BUTTON (widget));
1477   g_return_if_fail (widget != NULL || GTK_IS_ADJUSTMENT (adjustment));
1478   g_return_if_fail (factor != 0.0);
1479   g_return_if_fail (digits >= 0);
1480 
1481   if (! adjustment)
1482     adjustment = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (widget));
1483 
1484   g_return_if_fail (get_param_spec (G_OBJECT (adjustment)) != NULL);
1485 
1486   factor_store = g_object_get_data (G_OBJECT (adjustment),
1487                                     "gimp-prop-adjustment-factor");
1488   if (factor_store)
1489     {
1490       old_factor = *factor_store;
1491     }
1492   else
1493     {
1494       factor_store = g_new (gdouble, 1);
1495       g_object_set_data_full (G_OBJECT (adjustment),
1496                               "gimp-prop-adjustment-factor",
1497                               factor_store, (GDestroyNotify) g_free);
1498     }
1499 
1500   *factor_store = factor;
1501 
1502   f = factor / old_factor;
1503 
1504   if (step_increment <= 0)
1505     step_increment = f * gtk_adjustment_get_step_increment (adjustment);
1506 
1507   if (page_increment <= 0)
1508     page_increment = f * gtk_adjustment_get_page_increment (adjustment);
1509 
1510   gtk_adjustment_configure (adjustment,
1511                             f * gtk_adjustment_get_value (adjustment),
1512                             f * gtk_adjustment_get_lower (adjustment),
1513                             f * gtk_adjustment_get_upper (adjustment),
1514                             step_increment,
1515                             page_increment,
1516                             f * gtk_adjustment_get_page_size (adjustment));
1517 
1518   gtk_spin_button_set_digits (GTK_SPIN_BUTTON (widget), digits);
1519 }
1520 
1521 /**
1522  * gimp_prop_opacity_entry_new:
1523  * @config:        Object to which property is attached.
1524  * @property_name: Name of double property controlled by the spin button.
1525  * @table:         The #GtkTable the widgets will be attached to.
1526  * @column:        The column to start with.
1527  * @row:           The row to attach the widgets.
1528  * @label:         The text for the #GtkLabel which will appear left of the
1529  *                 #GtkHScale.
1530  *
1531  * Creates a #libgimpwidgets-gimpscaleentry (slider and spin button)
1532  * to set and display the value of the specified double property,
1533  * which should represent an "opacity" variable with range 0 to 100.
1534  * See gimp_scale_entry_new() for more information.
1535  *
1536  * Return value:  The #GtkSpinButton's #GtkAdjustment.
1537  *
1538  * Since: 2.4
1539  */
1540 GtkObject *
gimp_prop_opacity_entry_new(GObject * config,const gchar * property_name,GtkTable * table,gint column,gint row,const gchar * label)1541 gimp_prop_opacity_entry_new (GObject     *config,
1542                              const gchar *property_name,
1543                              GtkTable    *table,
1544                              gint         column,
1545                              gint         row,
1546                              const gchar *label)
1547 {
1548   GtkObject *adjustment;
1549 
1550   g_return_val_if_fail (G_IS_OBJECT (config), NULL);
1551   g_return_val_if_fail (property_name != NULL, NULL);
1552 
1553   adjustment = gimp_prop_scale_entry_new (config, property_name,
1554                                           table, column, row, label,
1555                                           0.01, 0.1, 1,
1556                                           FALSE, 0.0, 0.0);
1557 
1558   if (adjustment)
1559     {
1560       gimp_prop_widget_set_factor (GIMP_SCALE_ENTRY_SPINBUTTON (adjustment),
1561                                    GTK_ADJUSTMENT (adjustment),
1562                                    100.0, 0.0, 0.0, 1);
1563     }
1564 
1565   return adjustment;
1566 }
1567 
1568 
1569 static void
gimp_prop_adjustment_callback(GtkAdjustment * adjustment,GObject * config)1570 gimp_prop_adjustment_callback (GtkAdjustment *adjustment,
1571                                GObject       *config)
1572 {
1573   GParamSpec *param_spec;
1574   gdouble     value;
1575   gdouble    *factor;
1576 
1577   param_spec = get_param_spec (G_OBJECT (adjustment));
1578   if (! param_spec)
1579     return;
1580 
1581   value = gtk_adjustment_get_value (adjustment);
1582 
1583   factor = g_object_get_data (G_OBJECT (adjustment),
1584                               "gimp-prop-adjustment-factor");
1585   if (factor)
1586     value /= *factor;
1587 
1588   if (G_IS_PARAM_SPEC_INT (param_spec))
1589     {
1590       gint v;
1591 
1592       g_object_get (config, param_spec->name, &v, NULL);
1593 
1594       if (v != (gint) value)
1595         g_object_set (config, param_spec->name, (gint) value, NULL);
1596     }
1597   else if (G_IS_PARAM_SPEC_UINT (param_spec))
1598     {
1599       guint v;
1600 
1601       g_object_get (config, param_spec->name, &v, NULL);
1602 
1603       if (v != (guint) value)
1604         g_object_set (config, param_spec->name, (guint) value, NULL);
1605     }
1606   else if (G_IS_PARAM_SPEC_LONG (param_spec))
1607     {
1608       glong v;
1609 
1610       g_object_get (config, param_spec->name, &v, NULL);
1611 
1612       if (v != (glong) value)
1613         g_object_set (config, param_spec->name, (glong) value, NULL);
1614     }
1615   else if (G_IS_PARAM_SPEC_ULONG (param_spec))
1616     {
1617       gulong v;
1618 
1619       g_object_get (config, param_spec->name, &v, NULL);
1620 
1621       if (v != (gulong) value)
1622         g_object_set (config, param_spec->name, (gulong) value, NULL);
1623     }
1624   else if (G_IS_PARAM_SPEC_INT64 (param_spec))
1625     {
1626       gint64 v;
1627 
1628       g_object_get (config, param_spec->name, &v, NULL);
1629 
1630       if (v != (gint64) value)
1631         g_object_set (config, param_spec->name, (gint64) value, NULL);
1632     }
1633   else if (G_IS_PARAM_SPEC_UINT64 (param_spec))
1634     {
1635       guint64 v;
1636 
1637       g_object_get (config, param_spec->name, &v, NULL);
1638 
1639       if (v != (guint64) value)
1640         g_object_set (config, param_spec->name, (guint64) value, NULL);
1641     }
1642   else if (G_IS_PARAM_SPEC_DOUBLE (param_spec))
1643     {
1644       gdouble v;
1645 
1646       g_object_get (config, param_spec->name, &v, NULL);
1647 
1648       if (v != value)
1649         g_object_set (config, param_spec->name, value, NULL);
1650     }
1651 }
1652 
1653 static void
gimp_prop_adjustment_notify(GObject * config,GParamSpec * param_spec,GtkAdjustment * adjustment)1654 gimp_prop_adjustment_notify (GObject       *config,
1655                              GParamSpec    *param_spec,
1656                              GtkAdjustment *adjustment)
1657 {
1658   gdouble  value;
1659   gdouble *factor;
1660 
1661   if (G_IS_PARAM_SPEC_INT (param_spec))
1662     {
1663       gint int_value;
1664 
1665       g_object_get (config, param_spec->name, &int_value, NULL);
1666 
1667       value = int_value;
1668     }
1669   else if (G_IS_PARAM_SPEC_UINT (param_spec))
1670     {
1671       guint uint_value;
1672 
1673       g_object_get (config, param_spec->name, &uint_value, NULL);
1674 
1675       value = uint_value;
1676     }
1677   else if (G_IS_PARAM_SPEC_LONG (param_spec))
1678     {
1679       glong long_value;
1680 
1681       g_object_get (config, param_spec->name, &long_value, NULL);
1682 
1683       value = long_value;
1684     }
1685   else if (G_IS_PARAM_SPEC_ULONG (param_spec))
1686     {
1687       gulong ulong_value;
1688 
1689       g_object_get (config, param_spec->name, &ulong_value, NULL);
1690 
1691       value = ulong_value;
1692     }
1693   else if (G_IS_PARAM_SPEC_INT64 (param_spec))
1694     {
1695       gint64 int64_value;
1696 
1697       g_object_get (config, param_spec->name, &int64_value, NULL);
1698 
1699       value = int64_value;
1700     }
1701   else if (G_IS_PARAM_SPEC_UINT64 (param_spec))
1702     {
1703       guint64 uint64_value;
1704 
1705       g_object_get (config, param_spec->name, &uint64_value, NULL);
1706 
1707 #if defined _MSC_VER && (_MSC_VER < 1300)
1708       value = (gint64) uint64_value;
1709 #else
1710       value = uint64_value;
1711 #endif
1712     }
1713   else if (G_IS_PARAM_SPEC_DOUBLE (param_spec))
1714     {
1715       g_object_get (config, param_spec->name, &value, NULL);
1716     }
1717   else
1718     {
1719       g_warning ("%s: unhandled param spec of type %s",
1720                  G_STRFUNC, G_PARAM_SPEC_TYPE_NAME (param_spec));
1721       return;
1722     }
1723 
1724   factor = g_object_get_data (G_OBJECT (adjustment),
1725                               "gimp-prop-adjustment-factor");
1726   if (factor)
1727     value *= *factor;
1728 
1729   if (gtk_adjustment_get_value (adjustment) != value)
1730     {
1731       g_signal_handlers_block_by_func (adjustment,
1732                                        gimp_prop_adjustment_callback,
1733                                        config);
1734 
1735       gtk_adjustment_set_value (adjustment, value);
1736 
1737       g_signal_handlers_unblock_by_func (adjustment,
1738                                          gimp_prop_adjustment_callback,
1739                                          config);
1740     }
1741 }
1742 
1743 
1744 /*************/
1745 /*  memsize  */
1746 /*************/
1747 
1748 static void   gimp_prop_memsize_callback (GimpMemsizeEntry *entry,
1749                                           GObject          *config);
1750 static void   gimp_prop_memsize_notify   (GObject          *config,
1751                                           GParamSpec       *param_spec,
1752                                           GimpMemsizeEntry *entry);
1753 
1754 /**
1755  * gimp_prop_memsize_entry_new:
1756  * @config:        Object to which property is attached.
1757  * @property_name: Name of memsize property.
1758  *
1759  * Creates a #GimpMemsizeEntry (spin button and option menu) to set
1760  * and display the value of the specified memsize property.  See
1761  * gimp_memsize_entry_new() for more information.
1762  *
1763  * Return value:  A new #GimpMemsizeEntry.
1764  *
1765  * Since: 2.4
1766  */
1767 GtkWidget *
gimp_prop_memsize_entry_new(GObject * config,const gchar * property_name)1768 gimp_prop_memsize_entry_new (GObject     *config,
1769                              const gchar *property_name)
1770 {
1771   GParamSpec       *param_spec;
1772   GParamSpecUInt64 *uint64_spec;
1773   GtkWidget        *entry;
1774   guint64           value;
1775 
1776   g_return_val_if_fail (G_IS_OBJECT (config), NULL);
1777   g_return_val_if_fail (property_name != NULL, NULL);
1778 
1779   param_spec = check_param_spec_w (config, property_name,
1780                                    GIMP_TYPE_PARAM_MEMSIZE, G_STRFUNC);
1781   if (! param_spec)
1782     return NULL;
1783 
1784   g_object_get (config,
1785                 property_name, &value,
1786                 NULL);
1787 
1788   uint64_spec = G_PARAM_SPEC_UINT64 (param_spec);
1789 
1790   g_return_val_if_fail (uint64_spec->minimum <= GIMP_MAX_MEMSIZE, NULL);
1791   g_return_val_if_fail (uint64_spec->maximum <= GIMP_MAX_MEMSIZE, NULL);
1792 
1793   entry = gimp_memsize_entry_new (value,
1794                                   uint64_spec->minimum,
1795                                   uint64_spec->maximum);
1796 
1797   set_param_spec (G_OBJECT (entry),
1798                   GIMP_MEMSIZE_ENTRY (entry)->spinbutton,
1799                   param_spec);
1800 
1801   g_signal_connect (entry, "value-changed",
1802                     G_CALLBACK (gimp_prop_memsize_callback),
1803                     config);
1804 
1805   connect_notify (config, property_name,
1806                   G_CALLBACK (gimp_prop_memsize_notify),
1807                   entry);
1808 
1809   return entry;
1810 }
1811 
1812 
1813 static void
gimp_prop_memsize_callback(GimpMemsizeEntry * entry,GObject * config)1814 gimp_prop_memsize_callback (GimpMemsizeEntry *entry,
1815                             GObject          *config)
1816 {
1817   GParamSpec *param_spec;
1818   guint64     value;
1819   guint64     v;
1820 
1821   param_spec = get_param_spec (G_OBJECT (entry));
1822   if (! param_spec)
1823     return;
1824 
1825   g_return_if_fail (G_IS_PARAM_SPEC_UINT64 (param_spec));
1826 
1827   value = gimp_memsize_entry_get_value (entry);
1828 
1829   g_object_get (config, param_spec->name, &v, NULL);
1830 
1831   if (v != value)
1832     g_object_set (config, param_spec->name, value, NULL);
1833 }
1834 
1835 static void
gimp_prop_memsize_notify(GObject * config,GParamSpec * param_spec,GimpMemsizeEntry * entry)1836 gimp_prop_memsize_notify (GObject          *config,
1837                           GParamSpec       *param_spec,
1838                           GimpMemsizeEntry *entry)
1839 {
1840   guint64  value;
1841 
1842   g_return_if_fail (G_IS_PARAM_SPEC_UINT64 (param_spec));
1843 
1844   g_object_get (config,
1845                 param_spec->name, &value,
1846                 NULL);
1847 
1848   if (entry->value != value)
1849     {
1850       g_signal_handlers_block_by_func (entry,
1851                                        gimp_prop_memsize_callback,
1852                                        config);
1853 
1854       gimp_memsize_entry_set_value (entry, value);
1855 
1856       g_signal_handlers_unblock_by_func (entry,
1857                                          gimp_prop_memsize_callback,
1858                                          config);
1859     }
1860 }
1861 
1862 
1863 /***********/
1864 /*  label  */
1865 /***********/
1866 
1867 static void   gimp_prop_label_notify (GObject    *config,
1868                                       GParamSpec *param_spec,
1869                                       GtkWidget  *label);
1870 
1871 /**
1872  * gimp_prop_label_new:
1873  * @config:        Object to which property is attached.
1874  * @property_name: Name of string property.
1875  *
1876  * Creates a #GtkLabel to display the value of the specified property.
1877  * The property should be a string property or at least transformable
1878  * to a string.  If the user should be able to edit the string, use
1879  * gimp_prop_entry_new() instead.
1880  *
1881  * Return value:  A new #GtkLabel widget.
1882  *
1883  * Since: 2.4
1884  */
1885 GtkWidget *
gimp_prop_label_new(GObject * config,const gchar * property_name)1886 gimp_prop_label_new (GObject     *config,
1887                      const gchar *property_name)
1888 {
1889   GParamSpec *param_spec;
1890   GtkWidget  *label;
1891 
1892   g_return_val_if_fail (G_IS_OBJECT (config), NULL);
1893   g_return_val_if_fail (property_name != NULL, NULL);
1894 
1895   param_spec = find_param_spec (config, property_name, G_STRFUNC);
1896 
1897   if (! param_spec)
1898     return NULL;
1899 
1900   if (! g_value_type_transformable (param_spec->value_type, G_TYPE_STRING))
1901     {
1902       g_warning ("%s: property '%s' of %s is not transformable to string",
1903                  G_STRLOC,
1904                  param_spec->name,
1905                  g_type_name (param_spec->owner_type));
1906       return NULL;
1907     }
1908 
1909   label = gtk_label_new (NULL);
1910 
1911   set_param_spec (G_OBJECT (label), label, param_spec);
1912 
1913   connect_notify (config, property_name,
1914                   G_CALLBACK (gimp_prop_label_notify),
1915                   label);
1916 
1917   gimp_prop_label_notify (config, param_spec, label);
1918 
1919   return label;
1920 }
1921 
1922 static void
gimp_prop_label_notify(GObject * config,GParamSpec * param_spec,GtkWidget * label)1923 gimp_prop_label_notify (GObject    *config,
1924                         GParamSpec *param_spec,
1925                         GtkWidget  *label)
1926 {
1927   GValue  value = G_VALUE_INIT;
1928 
1929   g_value_init (&value, param_spec->value_type);
1930 
1931   g_object_get_property (config, param_spec->name, &value);
1932 
1933   if (G_VALUE_HOLDS_STRING (&value))
1934     {
1935       const gchar *str = g_value_get_string (&value);
1936 
1937       gtk_label_set_text (GTK_LABEL (label), str ? str : "");
1938     }
1939   else
1940     {
1941       GValue       str_value = G_VALUE_INIT;
1942       const gchar *str;
1943 
1944       g_value_init (&str_value, G_TYPE_STRING);
1945       g_value_transform (&value, &str_value);
1946 
1947       str = g_value_get_string (&str_value);
1948 
1949       gtk_label_set_text (GTK_LABEL (label), str ? str : "");
1950 
1951       g_value_unset (&str_value);
1952     }
1953 
1954   g_value_unset (&value);
1955 }
1956 
1957 
1958 /***********/
1959 /*  entry  */
1960 /***********/
1961 
1962 static void   gimp_prop_entry_callback (GtkWidget  *entry,
1963                                         GObject    *config);
1964 static void   gimp_prop_entry_notify   (GObject    *config,
1965                                         GParamSpec *param_spec,
1966                                         GtkWidget  *entry);
1967 
1968 /**
1969  * gimp_prop_entry_new:
1970  * @config:        Object to which property is attached.
1971  * @property_name: Name of string property.
1972  * @max_len:       Maximum allowed length of string.
1973  *
1974  * Creates a #GtkEntry to set and display the value of the specified
1975  * string property.
1976  *
1977  * Return value:  A new #GtkEntry widget.
1978  *
1979  * Since: 2.4
1980  */
1981 GtkWidget *
gimp_prop_entry_new(GObject * config,const gchar * property_name,gint max_len)1982 gimp_prop_entry_new (GObject     *config,
1983                      const gchar *property_name,
1984                      gint         max_len)
1985 {
1986   GParamSpec *param_spec;
1987   GtkWidget  *entry;
1988   gchar      *value;
1989 
1990   g_return_val_if_fail (G_IS_OBJECT (config), NULL);
1991   g_return_val_if_fail (property_name != NULL, NULL);
1992 
1993   param_spec = check_param_spec (config, property_name,
1994                                  G_TYPE_PARAM_STRING, G_STRFUNC);
1995   if (! param_spec)
1996     return NULL;
1997 
1998   g_object_get (config,
1999                 property_name, &value,
2000                 NULL);
2001 
2002   entry = gtk_entry_new ();
2003   gtk_entry_set_text (GTK_ENTRY (entry), value ? value : "");
2004 
2005   g_free (value);
2006 
2007   if (max_len > 0)
2008     gtk_entry_set_max_length (GTK_ENTRY (entry), max_len);
2009 
2010   gtk_editable_set_editable (GTK_EDITABLE (entry),
2011                              param_spec->flags & G_PARAM_WRITABLE);
2012 
2013   set_param_spec (G_OBJECT (entry), entry, param_spec);
2014 
2015   g_signal_connect (entry, "changed",
2016                     G_CALLBACK (gimp_prop_entry_callback),
2017                     config);
2018 
2019   connect_notify (config, property_name,
2020                   G_CALLBACK (gimp_prop_entry_notify),
2021                   entry);
2022 
2023   return entry;
2024 }
2025 
2026 static void
gimp_prop_entry_callback(GtkWidget * entry,GObject * config)2027 gimp_prop_entry_callback (GtkWidget *entry,
2028                           GObject   *config)
2029 {
2030   GParamSpec  *param_spec;
2031   const gchar *value;
2032   gchar       *v;
2033 
2034   param_spec = get_param_spec (G_OBJECT (entry));
2035   if (! param_spec)
2036     return;
2037 
2038   value = gtk_entry_get_text (GTK_ENTRY (entry));
2039 
2040   g_object_get (config, param_spec->name, &v, NULL);
2041 
2042   if (g_strcmp0 (v, value))
2043     {
2044       g_signal_handlers_block_by_func (config,
2045                                        gimp_prop_entry_notify,
2046                                        entry);
2047 
2048       g_object_set (config, param_spec->name, value, NULL);
2049 
2050       g_signal_handlers_unblock_by_func (config,
2051                                          gimp_prop_entry_notify,
2052                                          entry);
2053     }
2054 
2055   g_free (v);
2056 }
2057 
2058 static void
gimp_prop_entry_notify(GObject * config,GParamSpec * param_spec,GtkWidget * entry)2059 gimp_prop_entry_notify (GObject    *config,
2060                         GParamSpec *param_spec,
2061                         GtkWidget  *entry)
2062 {
2063   gchar *value;
2064 
2065   g_object_get (config,
2066                 param_spec->name, &value,
2067                 NULL);
2068 
2069   g_signal_handlers_block_by_func (entry,
2070                                    gimp_prop_entry_callback,
2071                                    config);
2072 
2073   gtk_entry_set_text (GTK_ENTRY (entry), value ? value : "");
2074 
2075   g_signal_handlers_unblock_by_func (entry,
2076                                      gimp_prop_entry_callback,
2077                                      config);
2078 
2079   g_free (value);
2080 }
2081 
2082 
2083 /*****************/
2084 /*  text buffer  */
2085 /*****************/
2086 
2087 static void   gimp_prop_text_buffer_callback (GtkTextBuffer *text_buffer,
2088                                               GObject       *config);
2089 static void   gimp_prop_text_buffer_notify   (GObject       *config,
2090                                               GParamSpec    *param_spec,
2091                                               GtkTextBuffer *text_buffer);
2092 
2093 /**
2094  * gimp_prop_text_buffer_new:
2095  * @config:        Object to which property is attached.
2096  * @property_name: Name of string property.
2097  * @max_len:       Maximum allowed length of text (in characters).
2098  *
2099  * Creates a #GtkTextBuffer to set and display the value of the
2100  * specified string property.  Unless the string is expected to
2101  * contain multiple lines or a large amount of text, use
2102  * gimp_prop_entry_new() instead.  See #GtkTextView for information on
2103  * how to insert a text buffer into a visible widget.
2104  *
2105  * If @max_len is 0 or negative, the text buffer allows an unlimited
2106  * number of characters to be entered.
2107  *
2108  * Return value:  A new #GtkTextBuffer.
2109  *
2110  * Since: 2.4
2111  */
2112 GtkTextBuffer *
gimp_prop_text_buffer_new(GObject * config,const gchar * property_name,gint max_len)2113 gimp_prop_text_buffer_new (GObject     *config,
2114                            const gchar *property_name,
2115                            gint         max_len)
2116 {
2117   GParamSpec    *param_spec;
2118   GtkTextBuffer *text_buffer;
2119   gchar         *value;
2120 
2121   g_return_val_if_fail (G_IS_OBJECT (config), NULL);
2122   g_return_val_if_fail (property_name != NULL, NULL);
2123 
2124   param_spec = check_param_spec_w (config, property_name,
2125                                    G_TYPE_PARAM_STRING, G_STRFUNC);
2126   if (! param_spec)
2127     return NULL;
2128 
2129   g_object_get (config,
2130                 property_name, &value,
2131                 NULL);
2132 
2133   text_buffer = gtk_text_buffer_new (NULL);
2134   gtk_text_buffer_set_text (text_buffer, value ? value : "", -1);
2135 
2136   g_free (value);
2137 
2138   if (max_len > 0)
2139     g_object_set_data (G_OBJECT (text_buffer), "max-len",
2140                        GINT_TO_POINTER (max_len));
2141 
2142   set_param_spec (G_OBJECT (text_buffer), NULL, param_spec);
2143 
2144   g_signal_connect (text_buffer, "changed",
2145                     G_CALLBACK (gimp_prop_text_buffer_callback),
2146                     config);
2147 
2148   connect_notify (config, property_name,
2149                   G_CALLBACK (gimp_prop_text_buffer_notify),
2150                   text_buffer);
2151 
2152   return text_buffer;
2153 }
2154 
2155 static void
gimp_prop_text_buffer_callback(GtkTextBuffer * text_buffer,GObject * config)2156 gimp_prop_text_buffer_callback (GtkTextBuffer *text_buffer,
2157                                 GObject       *config)
2158 {
2159   GParamSpec  *param_spec;
2160   GtkTextIter  start_iter;
2161   GtkTextIter  end_iter;
2162   gchar       *text;
2163   gint         max_len;
2164 
2165   param_spec = get_param_spec (G_OBJECT (text_buffer));
2166   if (! param_spec)
2167     return;
2168 
2169   gtk_text_buffer_get_bounds (text_buffer, &start_iter, &end_iter);
2170   text = gtk_text_buffer_get_text (text_buffer, &start_iter, &end_iter, FALSE);
2171 
2172   max_len = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (text_buffer),
2173                                                 "max-length"));
2174 
2175   if (max_len > 0 && g_utf8_strlen (text, -1) > max_len)
2176     {
2177       g_message (dngettext (GETTEXT_PACKAGE "-libgimp",
2178                             "This text input field is limited to %d character.",
2179                             "This text input field is limited to %d characters.",
2180                             max_len), max_len);
2181 
2182       gtk_text_buffer_get_iter_at_offset (text_buffer,
2183                                           &start_iter, max_len - 1);
2184       gtk_text_buffer_get_end_iter (text_buffer, &end_iter);
2185 
2186       /*  this calls us recursively, but in the else branch  */
2187       gtk_text_buffer_delete (text_buffer, &start_iter, &end_iter);
2188     }
2189   else
2190     {
2191       gchar *v;
2192 
2193       g_object_get (config, param_spec->name, &v, NULL);
2194 
2195       if (g_strcmp0 (v, text))
2196         {
2197           g_signal_handlers_block_by_func (config,
2198                                            gimp_prop_text_buffer_notify,
2199                                            text_buffer);
2200 
2201           g_object_set (config, param_spec->name, text,  NULL);
2202 
2203           g_signal_handlers_unblock_by_func (config,
2204                                              gimp_prop_text_buffer_notify,
2205                                              text_buffer);
2206         }
2207 
2208       g_free (v);
2209     }
2210 
2211   g_free (text);
2212 }
2213 
2214 static void
gimp_prop_text_buffer_notify(GObject * config,GParamSpec * param_spec,GtkTextBuffer * text_buffer)2215 gimp_prop_text_buffer_notify (GObject       *config,
2216                               GParamSpec    *param_spec,
2217                               GtkTextBuffer *text_buffer)
2218 {
2219   gchar *value;
2220 
2221   g_object_get (config,
2222                 param_spec->name, &value,
2223                 NULL);
2224 
2225   g_signal_handlers_block_by_func (text_buffer,
2226                                    gimp_prop_text_buffer_callback,
2227                                    config);
2228 
2229   gtk_text_buffer_set_text (text_buffer, value ? value : "", -1);
2230 
2231   g_signal_handlers_unblock_by_func (text_buffer,
2232                                      gimp_prop_text_buffer_callback,
2233                                      config);
2234 
2235   g_free (value);
2236 }
2237 
2238 
2239 /***********************/
2240 /*  string combo box   */
2241 /***********************/
2242 
2243 static void   gimp_prop_string_combo_box_callback (GtkWidget   *widget,
2244                                                    GObject     *config);
2245 static void   gimp_prop_string_combo_box_notify   (GObject     *config,
2246                                                    GParamSpec  *param_spec,
2247                                                    GtkWidget   *widget);
2248 
2249 /**
2250  * gimp_prop_string_combo_box_new:
2251  * @config:        Object to which property is attached.
2252  * @property_name: Name of int property controlled by combo box.
2253  * @model:         #GtkTreeStore holding list of values
2254  * @id_column:     column in @store that holds string IDs
2255  * @label_column:  column in @store that holds labels to use in the combo-box
2256  *
2257  * Creates a #GimpStringComboBox widget to display and set the
2258  * specified property.  The contents of the widget are determined by
2259  * @store.
2260  *
2261  * Return value: The newly created #GimpStringComboBox widget.
2262  *
2263  * Since: 2.4
2264  */
2265 GtkWidget *
gimp_prop_string_combo_box_new(GObject * config,const gchar * property_name,GtkTreeModel * model,gint id_column,gint label_column)2266 gimp_prop_string_combo_box_new (GObject      *config,
2267                                 const gchar  *property_name,
2268                                 GtkTreeModel *model,
2269                                 gint          id_column,
2270                                 gint          label_column)
2271 {
2272   GParamSpec *param_spec;
2273   GtkWidget  *combo_box;
2274   gchar      *value;
2275 
2276   g_return_val_if_fail (G_IS_OBJECT (config), NULL);
2277   g_return_val_if_fail (property_name != NULL, NULL);
2278   g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
2279 
2280   param_spec = check_param_spec_w (config, property_name,
2281                                    G_TYPE_PARAM_STRING, G_STRFUNC);
2282   if (! param_spec)
2283     return NULL;
2284 
2285   g_object_get (config,
2286                 property_name, &value,
2287                 NULL);
2288 
2289   combo_box = gimp_string_combo_box_new (model, id_column, label_column);
2290 
2291   gimp_string_combo_box_set_active (GIMP_STRING_COMBO_BOX (combo_box), value);
2292 
2293   g_signal_connect (combo_box, "changed",
2294                     G_CALLBACK (gimp_prop_string_combo_box_callback),
2295                     config);
2296 
2297   set_param_spec (G_OBJECT (combo_box), combo_box, param_spec);
2298 
2299   connect_notify (config, property_name,
2300                   G_CALLBACK (gimp_prop_string_combo_box_notify),
2301                   combo_box);
2302 
2303   return combo_box;
2304 }
2305 
2306 static void
gimp_prop_string_combo_box_callback(GtkWidget * widget,GObject * config)2307 gimp_prop_string_combo_box_callback (GtkWidget *widget,
2308                                      GObject   *config)
2309 {
2310   GParamSpec  *param_spec;
2311   gchar       *value;
2312   gchar       *v;
2313 
2314   param_spec = get_param_spec (G_OBJECT (widget));
2315   if (! param_spec)
2316     return;
2317 
2318   value = gimp_string_combo_box_get_active (GIMP_STRING_COMBO_BOX (widget));
2319 
2320   g_object_get (config, param_spec->name, &v, NULL);
2321 
2322   if (g_strcmp0 (v, value))
2323     g_object_set (config, param_spec->name, value, NULL);
2324 
2325   g_free (value);
2326   g_free (v);
2327 }
2328 
2329 static void
gimp_prop_string_combo_box_notify(GObject * config,GParamSpec * param_spec,GtkWidget * combo_box)2330 gimp_prop_string_combo_box_notify (GObject    *config,
2331                                    GParamSpec *param_spec,
2332                                    GtkWidget  *combo_box)
2333 {
2334   gchar *value;
2335 
2336   g_object_get (config,
2337                 param_spec->name, &value,
2338                 NULL);
2339 
2340   g_signal_handlers_block_by_func (combo_box,
2341                                    gimp_prop_string_combo_box_callback,
2342                                    config);
2343 
2344   gimp_string_combo_box_set_active (GIMP_STRING_COMBO_BOX (combo_box), value);
2345 
2346   g_signal_handlers_unblock_by_func (combo_box,
2347                                      gimp_prop_string_combo_box_callback,
2348                                      config);
2349 
2350   g_free (value);
2351 }
2352 
2353 
2354 /*************************/
2355 /*  file chooser button  */
2356 /*************************/
2357 
2358 
2359 static GtkWidget * gimp_prop_file_chooser_button_setup    (GtkWidget      *button,
2360                                                            GObject        *config,
2361                                                            GParamSpec     *param_spec);
2362 static void        gimp_prop_file_chooser_button_callback (GtkFileChooser *button,
2363                                                            GObject        *config);
2364 static void        gimp_prop_file_chooser_button_notify   (GObject        *config,
2365                                                            GParamSpec     *param_spec,
2366                                                            GtkFileChooser *button);
2367 
2368 
2369 /**
2370  * gimp_prop_file_chooser_button_new:
2371  * @config:        object to which property is attached.
2372  * @property_name: name of path property.
2373  * @title:         the title of the browse dialog.
2374  * @action:        the open mode for the widget.
2375  *
2376  * Creates a #GtkFileChooserButton to edit the specified path property.
2377  *
2378  * Note that #GtkFileChooserButton implements the #GtkFileChooser
2379  * interface; you can use the #GtkFileChooser API with it.
2380  *
2381  * Return value:  A new #GtkFileChooserButton.
2382  *
2383  * Since: 2.4
2384  */
2385 GtkWidget *
gimp_prop_file_chooser_button_new(GObject * config,const gchar * property_name,const gchar * title,GtkFileChooserAction action)2386 gimp_prop_file_chooser_button_new (GObject              *config,
2387                                    const gchar          *property_name,
2388                                    const gchar          *title,
2389                                    GtkFileChooserAction  action)
2390 {
2391   GParamSpec *param_spec;
2392   GtkWidget  *button;
2393 
2394   g_return_val_if_fail (G_IS_OBJECT (config), NULL);
2395   g_return_val_if_fail (property_name != NULL, NULL);
2396 
2397   param_spec = check_param_spec_w (config, property_name,
2398                                    GIMP_TYPE_PARAM_CONFIG_PATH, G_STRFUNC);
2399   if (! param_spec)
2400     return NULL;
2401 
2402   button = gtk_file_chooser_button_new (title, action);
2403 
2404   return gimp_prop_file_chooser_button_setup (button, config, param_spec);
2405 }
2406 
2407 /**
2408  * gimp_prop_file_chooser_button_new_with_dialog:
2409  * @config:        object to which property is attached.
2410  * @property_name: name of path property.
2411  * @dialog:        the #GtkFileChooserDialog widget to use.
2412  *
2413  * Creates a #GtkFileChooserButton to edit the specified path property.
2414  *
2415  * The button uses @dialog as it's file-picking window. Note that @dialog
2416  * must be a #GtkFileChooserDialog (or subclass) and must not have
2417  * %GTK_DIALOG_DESTROY_WITH_PARENT set.
2418  *
2419  * Note that #GtkFileChooserButton implements the #GtkFileChooser
2420  * interface; you can use the #GtkFileChooser API with it.
2421  *
2422  * Return value:  A new #GtkFileChooserButton.
2423  *
2424  * Since: 2.4
2425  */
2426 GtkWidget *
gimp_prop_file_chooser_button_new_with_dialog(GObject * config,const gchar * property_name,GtkWidget * dialog)2427 gimp_prop_file_chooser_button_new_with_dialog (GObject     *config,
2428                                                const gchar *property_name,
2429                                                GtkWidget   *dialog)
2430 {
2431   GParamSpec *param_spec;
2432   GtkWidget  *button;
2433 
2434   g_return_val_if_fail (G_IS_OBJECT (config), NULL);
2435   g_return_val_if_fail (property_name != NULL, NULL);
2436   g_return_val_if_fail (GTK_IS_FILE_CHOOSER_DIALOG (dialog), NULL);
2437 
2438   param_spec = check_param_spec_w (config, property_name,
2439                                    GIMP_TYPE_PARAM_CONFIG_PATH, G_STRFUNC);
2440   if (! param_spec)
2441     return NULL;
2442 
2443   button = gtk_file_chooser_button_new_with_dialog (dialog);
2444 
2445   return gimp_prop_file_chooser_button_setup (button, config, param_spec);
2446 }
2447 
2448 static GtkWidget *
gimp_prop_file_chooser_button_setup(GtkWidget * button,GObject * config,GParamSpec * param_spec)2449 gimp_prop_file_chooser_button_setup (GtkWidget  *button,
2450                                      GObject    *config,
2451                                      GParamSpec *param_spec)
2452 {
2453   gchar *value;
2454   GFile *file = NULL;
2455 
2456   g_object_get (config,
2457                 param_spec->name, &value,
2458                 NULL);
2459 
2460   if (value)
2461     {
2462       file = gimp_file_new_for_config_path (value, NULL);
2463       g_free (value);
2464     }
2465 
2466   if (file)
2467     {
2468       gchar *basename = g_file_get_basename (file);
2469 
2470       if (basename && basename[0] == '.')
2471         gtk_file_chooser_set_show_hidden (GTK_FILE_CHOOSER (button), TRUE);
2472 
2473       g_free (basename);
2474 
2475       gtk_file_chooser_set_file (GTK_FILE_CHOOSER (button), file, NULL);
2476       g_object_unref (file);
2477     }
2478 
2479   set_param_spec (G_OBJECT (button), button, param_spec);
2480 
2481   g_signal_connect (button, "file-set",
2482                     G_CALLBACK (gimp_prop_file_chooser_button_callback),
2483                     config);
2484 
2485   connect_notify (config, param_spec->name,
2486                   G_CALLBACK (gimp_prop_file_chooser_button_notify),
2487                   button);
2488 
2489   return button;
2490 }
2491 
2492 static void
gimp_prop_file_chooser_button_callback(GtkFileChooser * button,GObject * config)2493 gimp_prop_file_chooser_button_callback (GtkFileChooser *button,
2494                                         GObject        *config)
2495 {
2496   GParamSpec *param_spec;
2497   GFile      *file;
2498   gchar      *value = NULL;
2499   gchar      *v;
2500 
2501   param_spec = get_param_spec (G_OBJECT (button));
2502   if (! param_spec)
2503     return;
2504 
2505   file = gtk_file_chooser_get_file (button);
2506 
2507   if (file)
2508     {
2509       value = gimp_file_get_config_path (file, NULL);
2510       g_object_unref (file);
2511     }
2512 
2513   g_object_get (config, param_spec->name, &v, NULL);
2514 
2515   if (g_strcmp0 (v, value))
2516     {
2517       g_signal_handlers_block_by_func (config,
2518                                        gimp_prop_file_chooser_button_notify,
2519                                        button);
2520 
2521       g_object_set (config, param_spec->name, value, NULL);
2522 
2523       g_signal_handlers_unblock_by_func (config,
2524                                          gimp_prop_file_chooser_button_notify,
2525                                          button);
2526     }
2527 
2528   g_free (value);
2529   g_free (v);
2530 }
2531 
2532 static void
gimp_prop_file_chooser_button_notify(GObject * config,GParamSpec * param_spec,GtkFileChooser * button)2533 gimp_prop_file_chooser_button_notify (GObject        *config,
2534                                       GParamSpec     *param_spec,
2535                                       GtkFileChooser *button)
2536 {
2537   gchar *value;
2538   GFile *file = NULL;
2539 
2540   g_object_get (config,
2541                 param_spec->name, &value,
2542                 NULL);
2543 
2544   if (value)
2545     {
2546       file = gimp_file_new_for_config_path (value, NULL);
2547       g_free (value);
2548     }
2549 
2550   g_signal_handlers_block_by_func (button,
2551                                    gimp_prop_file_chooser_button_callback,
2552                                    config);
2553 
2554   if (file)
2555     {
2556       gtk_file_chooser_set_file (button, file, NULL);
2557       g_object_unref (file);
2558     }
2559   else
2560     {
2561       gtk_file_chooser_unselect_all (button);
2562     }
2563 
2564   g_signal_handlers_unblock_by_func (button,
2565                                      gimp_prop_file_chooser_button_callback,
2566                                      config);
2567 }
2568 
2569 
2570 /*****************/
2571 /*  path editor  */
2572 /*****************/
2573 
2574 static void   gimp_prop_path_editor_path_callback     (GimpPathEditor *editor,
2575                                                        GObject        *config);
2576 static void   gimp_prop_path_editor_writable_callback (GimpPathEditor *editor,
2577                                                        GObject        *config);
2578 static void   gimp_prop_path_editor_path_notify       (GObject        *config,
2579                                                        GParamSpec     *param_spec,
2580                                                        GimpPathEditor *editor);
2581 static void   gimp_prop_path_editor_writable_notify   (GObject        *config,
2582                                                        GParamSpec     *param_spec,
2583                                                        GimpPathEditor *editor);
2584 
2585 GtkWidget *
gimp_prop_path_editor_new(GObject * config,const gchar * path_property_name,const gchar * writable_property_name,const gchar * filesel_title)2586 gimp_prop_path_editor_new (GObject     *config,
2587                            const gchar *path_property_name,
2588                            const gchar *writable_property_name,
2589                            const gchar *filesel_title)
2590 {
2591   GParamSpec *path_param_spec;
2592   GParamSpec *writable_param_spec = NULL;
2593   GtkWidget  *editor;
2594   gchar      *value;
2595   gchar      *filename;
2596 
2597   g_return_val_if_fail (G_IS_OBJECT (config), NULL);
2598   g_return_val_if_fail (path_property_name != NULL, NULL);
2599 
2600   path_param_spec = check_param_spec_w (config, path_property_name,
2601                                         GIMP_TYPE_PARAM_CONFIG_PATH, G_STRFUNC);
2602   if (! path_param_spec)
2603     return NULL;
2604 
2605   if (writable_property_name)
2606     {
2607       writable_param_spec = check_param_spec_w (config, writable_property_name,
2608                                                 GIMP_TYPE_PARAM_CONFIG_PATH,
2609                                                 G_STRFUNC);
2610       if (! writable_param_spec)
2611         return NULL;
2612     }
2613 
2614   g_object_get (config,
2615                 path_property_name, &value,
2616                 NULL);
2617 
2618   filename = value ? gimp_config_path_expand (value, TRUE, NULL) : NULL;
2619   g_free (value);
2620 
2621   editor = gimp_path_editor_new (filesel_title, filename);
2622   g_free (filename);
2623 
2624   if (writable_property_name)
2625     {
2626       g_object_get (config,
2627                     writable_property_name, &value,
2628                     NULL);
2629 
2630       filename = value ? gimp_config_path_expand (value, TRUE, NULL) : NULL;
2631       g_free (value);
2632 
2633       gimp_path_editor_set_writable_path (GIMP_PATH_EDITOR (editor), filename);
2634       g_free (filename);
2635     }
2636 
2637   g_object_set_data (G_OBJECT (editor), "gimp-config-param-spec-path",
2638                      path_param_spec);
2639 
2640   g_signal_connect (editor, "path-changed",
2641                     G_CALLBACK (gimp_prop_path_editor_path_callback),
2642                     config);
2643 
2644   connect_notify (config, path_property_name,
2645                   G_CALLBACK (gimp_prop_path_editor_path_notify),
2646                   editor);
2647 
2648   if (writable_property_name)
2649     {
2650       g_object_set_data (G_OBJECT (editor), "gimp-config-param-spec-writable",
2651                          writable_param_spec);
2652 
2653       g_signal_connect (editor, "writable-changed",
2654                         G_CALLBACK (gimp_prop_path_editor_writable_callback),
2655                         config);
2656 
2657       connect_notify (config, writable_property_name,
2658                       G_CALLBACK (gimp_prop_path_editor_writable_notify),
2659                       editor);
2660     }
2661 
2662   return editor;
2663 }
2664 
2665 static void
gimp_prop_path_editor_path_callback(GimpPathEditor * editor,GObject * config)2666 gimp_prop_path_editor_path_callback (GimpPathEditor *editor,
2667                                      GObject        *config)
2668 {
2669   GParamSpec *path_param_spec;
2670   GParamSpec *writable_param_spec;
2671   gchar      *value;
2672   gchar      *utf8;
2673 
2674   path_param_spec     = g_object_get_data (G_OBJECT (editor),
2675                                            "gimp-config-param-spec-path");
2676   writable_param_spec = g_object_get_data (G_OBJECT (editor),
2677                                            "gimp-config-param-spec-writable");
2678   if (! path_param_spec)
2679     return;
2680 
2681   value = gimp_path_editor_get_path (editor);
2682   utf8 = value ? gimp_config_path_unexpand (value, TRUE, NULL) : NULL;
2683   g_free (value);
2684 
2685   g_signal_handlers_block_by_func (config,
2686                                    gimp_prop_path_editor_path_notify,
2687                                    editor);
2688 
2689   g_object_set (config,
2690                 path_param_spec->name, utf8,
2691                 NULL);
2692 
2693   g_signal_handlers_unblock_by_func (config,
2694                                      gimp_prop_path_editor_path_notify,
2695                                      editor);
2696 
2697   g_free (utf8);
2698 
2699   if (writable_param_spec)
2700     {
2701       value = gimp_path_editor_get_writable_path (editor);
2702       utf8 = value ? gimp_config_path_unexpand (value, TRUE, NULL) : NULL;
2703       g_free (value);
2704 
2705       g_signal_handlers_block_by_func (config,
2706                                        gimp_prop_path_editor_writable_notify,
2707                                        editor);
2708 
2709       g_object_set (config,
2710                     writable_param_spec->name, utf8,
2711                     NULL);
2712 
2713       g_signal_handlers_unblock_by_func (config,
2714                                          gimp_prop_path_editor_writable_notify,
2715                                          editor);
2716 
2717       g_free (utf8);
2718     }
2719 }
2720 
2721 static void
gimp_prop_path_editor_writable_callback(GimpPathEditor * editor,GObject * config)2722 gimp_prop_path_editor_writable_callback (GimpPathEditor *editor,
2723                                          GObject        *config)
2724 {
2725   GParamSpec *param_spec;
2726   gchar      *value;
2727   gchar      *utf8;
2728 
2729   param_spec = g_object_get_data (G_OBJECT (editor),
2730                                   "gimp-config-param-spec-writable");
2731   if (! param_spec)
2732     return;
2733 
2734   value = gimp_path_editor_get_writable_path (editor);
2735   utf8 = value ? gimp_config_path_unexpand (value, TRUE, NULL) : NULL;
2736   g_free (value);
2737 
2738   g_signal_handlers_block_by_func (config,
2739                                    gimp_prop_path_editor_writable_notify,
2740                                    editor);
2741 
2742   g_object_set (config,
2743                 param_spec->name, utf8,
2744                 NULL);
2745 
2746   g_signal_handlers_unblock_by_func (config,
2747                                      gimp_prop_path_editor_writable_notify,
2748                                      editor);
2749 
2750   g_free (utf8);
2751 }
2752 
2753 static void
gimp_prop_path_editor_path_notify(GObject * config,GParamSpec * param_spec,GimpPathEditor * editor)2754 gimp_prop_path_editor_path_notify (GObject        *config,
2755                                    GParamSpec     *param_spec,
2756                                    GimpPathEditor *editor)
2757 {
2758   gchar *value;
2759   gchar *filename;
2760 
2761   g_object_get (config,
2762                 param_spec->name, &value,
2763                 NULL);
2764 
2765   filename = value ? gimp_config_path_expand (value, TRUE, NULL) : NULL;
2766   g_free (value);
2767 
2768   g_signal_handlers_block_by_func (editor,
2769                                    gimp_prop_path_editor_path_callback,
2770                                    config);
2771 
2772   gimp_path_editor_set_path (editor, filename);
2773 
2774   g_signal_handlers_unblock_by_func (editor,
2775                                      gimp_prop_path_editor_path_callback,
2776                                      config);
2777 
2778   g_free (filename);
2779 }
2780 
2781 static void
gimp_prop_path_editor_writable_notify(GObject * config,GParamSpec * param_spec,GimpPathEditor * editor)2782 gimp_prop_path_editor_writable_notify (GObject        *config,
2783                                        GParamSpec     *param_spec,
2784                                        GimpPathEditor *editor)
2785 {
2786   gchar *value;
2787   gchar *filename;
2788 
2789   g_object_get (config,
2790                 param_spec->name, &value,
2791                 NULL);
2792 
2793   filename = value ? gimp_config_path_expand (value, TRUE, NULL) : NULL;
2794   g_free (value);
2795 
2796   g_signal_handlers_block_by_func (editor,
2797                                    gimp_prop_path_editor_writable_callback,
2798                                    config);
2799 
2800   gimp_path_editor_set_writable_path (editor, filename);
2801 
2802   g_signal_handlers_unblock_by_func (editor,
2803                                      gimp_prop_path_editor_writable_callback,
2804                                      config);
2805 
2806   g_free (filename);
2807 }
2808 
2809 
2810 /***************/
2811 /*  sizeentry  */
2812 /***************/
2813 
2814 static void   gimp_prop_size_entry_callback    (GimpSizeEntry *entry,
2815                                                 GObject       *config);
2816 static void   gimp_prop_size_entry_notify      (GObject       *config,
2817                                                 GParamSpec    *param_spec,
2818                                                 GimpSizeEntry *entry);
2819 static void   gimp_prop_size_entry_notify_unit (GObject       *config,
2820                                                 GParamSpec    *param_spec,
2821                                                 GimpSizeEntry *entry);
2822 static gint   gimp_prop_size_entry_num_chars   (gdouble        lower,
2823                                                 gdouble        upper);
2824 
2825 
2826 /**
2827  * gimp_prop_size_entry_new:
2828  * @config:             Object to which property is attached.
2829  * @property_name:      Name of int or double property.
2830  * @property_is_pixel:  When %TRUE, the property value is in pixels,
2831  *                      and in the selected unit otherwise.
2832  * @unit_property_name: Name of unit property.
2833  * @unit_format:        A printf-like unit-format string as is used with
2834  *                      gimp_unit_menu_new().
2835  * @update_policy:      How the automatic pixel <-> real-world-unit
2836  *                      calculations should be done.
2837  * @resolution:         The resolution (in dpi) for the field.
2838  *
2839  * Creates a #GimpSizeEntry to set and display the specified double or
2840  * int property, and its associated unit property.  Note that this
2841  * function is only suitable for creating a size entry holding a
2842  * single value.  Use gimp_prop_coordinates_new() to create a size
2843  * entry holding two values.
2844  *
2845  * Return value:  A new #GimpSizeEntry widget.
2846  *
2847  * Since: 2.4
2848  */
2849 GtkWidget *
gimp_prop_size_entry_new(GObject * config,const gchar * property_name,gboolean property_is_pixel,const gchar * unit_property_name,const gchar * unit_format,GimpSizeEntryUpdatePolicy update_policy,gdouble resolution)2850 gimp_prop_size_entry_new (GObject                   *config,
2851                           const gchar               *property_name,
2852                           gboolean                   property_is_pixel,
2853                           const gchar               *unit_property_name,
2854                           const gchar               *unit_format,
2855                           GimpSizeEntryUpdatePolicy  update_policy,
2856                           gdouble                    resolution)
2857 {
2858   GtkWidget  *entry;
2859   GParamSpec *param_spec;
2860   GParamSpec *unit_param_spec;
2861   gboolean    show_pixels;
2862   gboolean    show_percent;
2863   gdouble     value;
2864   gdouble     lower;
2865   gdouble     upper;
2866   GimpUnit    unit_value;
2867 
2868   param_spec = find_param_spec (config, property_name, G_STRFUNC);
2869   if (! param_spec)
2870     return NULL;
2871 
2872   if (! get_numeric_values (config,
2873                             param_spec, &value, &lower, &upper, G_STRFUNC))
2874     return NULL;
2875 
2876   if (unit_property_name)
2877     {
2878       GValue value = G_VALUE_INIT;
2879 
2880       unit_param_spec = check_param_spec_w (config, unit_property_name,
2881                                             GIMP_TYPE_PARAM_UNIT, G_STRFUNC);
2882       if (! unit_param_spec)
2883         return NULL;
2884 
2885       g_value_init (&value, unit_param_spec->value_type);
2886 
2887       g_value_set_int (&value, GIMP_UNIT_PIXEL);
2888       show_pixels = (g_param_value_validate (unit_param_spec,
2889                                              &value) == FALSE);
2890 
2891       g_value_set_int (&value, GIMP_UNIT_PERCENT);
2892       show_percent = (g_param_value_validate (unit_param_spec,
2893                                               &value) == FALSE);
2894 
2895       g_value_unset (&value);
2896 
2897       g_object_get (config,
2898                     unit_property_name, &unit_value,
2899                     NULL);
2900     }
2901   else
2902     {
2903       unit_param_spec = NULL;
2904       unit_value      = GIMP_UNIT_INCH;
2905       show_pixels     = FALSE;
2906       show_percent    = FALSE;
2907     }
2908 
2909   entry = gimp_size_entry_new (1, unit_value, unit_format,
2910                                show_pixels, show_percent, FALSE,
2911                                gimp_prop_size_entry_num_chars (lower, upper) + 1 +
2912                                gimp_unit_get_scaled_digits (unit_value, resolution),
2913                                update_policy);
2914   gtk_table_set_col_spacing (GTK_TABLE (entry), 1, 2);
2915 
2916   set_param_spec (NULL,
2917                   gimp_size_entry_get_help_widget (GIMP_SIZE_ENTRY (entry), 0),
2918                   param_spec);
2919 
2920   if (unit_param_spec)
2921     set_param_spec (NULL, GIMP_SIZE_ENTRY (entry)->unitmenu, unit_param_spec);
2922 
2923   gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (entry), unit_value);
2924 
2925   if (update_policy == GIMP_SIZE_ENTRY_UPDATE_SIZE)
2926     gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (entry), 0,
2927                                     resolution, FALSE);
2928 
2929   gimp_size_entry_set_value_boundaries (GIMP_SIZE_ENTRY (entry), 0,
2930                                         lower, upper);
2931 
2932   g_object_set_data (G_OBJECT (entry), "value-is-pixel",
2933                      GINT_TO_POINTER (property_is_pixel ? TRUE : FALSE));
2934 
2935   if (property_is_pixel)
2936     gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (entry), 0, value);
2937   else
2938     gimp_size_entry_set_value (GIMP_SIZE_ENTRY (entry), 0, value);
2939 
2940   g_object_set_data (G_OBJECT (entry), "gimp-config-param-spec",
2941                      param_spec);
2942 
2943   g_signal_connect (entry, "refval-changed",
2944                     G_CALLBACK (gimp_prop_size_entry_callback),
2945                     config);
2946   g_signal_connect (entry, "value-changed",
2947                     G_CALLBACK (gimp_prop_size_entry_callback),
2948                     config);
2949 
2950   connect_notify (config, property_name,
2951                   G_CALLBACK (gimp_prop_size_entry_notify),
2952                   entry);
2953 
2954   if (unit_property_name)
2955     {
2956       g_object_set_data (G_OBJECT (entry), "gimp-config-param-spec-unit",
2957                          unit_param_spec);
2958 
2959       g_signal_connect (entry, "unit-changed",
2960                         G_CALLBACK (gimp_prop_size_entry_callback),
2961                         config);
2962 
2963       connect_notify (config, unit_property_name,
2964                       G_CALLBACK (gimp_prop_size_entry_notify_unit),
2965                       entry);
2966     }
2967 
2968   return entry;
2969 }
2970 
2971 static void
gimp_prop_size_entry_callback(GimpSizeEntry * entry,GObject * config)2972 gimp_prop_size_entry_callback (GimpSizeEntry *entry,
2973                                GObject       *config)
2974 {
2975   GParamSpec *param_spec;
2976   GParamSpec *unit_param_spec;
2977   gdouble     value;
2978   gboolean    value_is_pixel;
2979   GimpUnit    unit_value;
2980 
2981   param_spec = g_object_get_data (G_OBJECT (entry), "gimp-config-param-spec");
2982   if (! param_spec)
2983     return;
2984 
2985   unit_param_spec = g_object_get_data (G_OBJECT (entry),
2986                                        "gimp-config-param-spec-unit");
2987 
2988   value_is_pixel = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (entry),
2989                                                        "value-is-pixel"));
2990 
2991   if (value_is_pixel)
2992     value = gimp_size_entry_get_refval (entry, 0);
2993   else
2994     value = gimp_size_entry_get_value (entry, 0);
2995 
2996   unit_value = gimp_size_entry_get_unit (entry);
2997 
2998   if (unit_param_spec)
2999     {
3000       GimpUnit  old_unit;
3001 
3002       g_object_get (config,
3003                     unit_param_spec->name, &old_unit,
3004                     NULL);
3005 
3006       if (unit_value == old_unit)
3007         unit_param_spec = NULL;
3008     }
3009 
3010   if (G_IS_PARAM_SPEC_INT (param_spec))
3011     {
3012       g_object_set (config,
3013                     param_spec->name, ROUND (value),
3014 
3015                     unit_param_spec ?
3016                     unit_param_spec->name : NULL, unit_value,
3017 
3018                     NULL);
3019     }
3020   else if (G_IS_PARAM_SPEC_DOUBLE (param_spec))
3021     {
3022       g_object_set (config,
3023                     param_spec->name, value,
3024 
3025                     unit_param_spec ?
3026                     unit_param_spec->name : NULL, unit_value,
3027 
3028                     NULL);
3029     }
3030 }
3031 
3032 static void
gimp_prop_size_entry_notify(GObject * config,GParamSpec * param_spec,GimpSizeEntry * entry)3033 gimp_prop_size_entry_notify (GObject       *config,
3034                              GParamSpec    *param_spec,
3035                              GimpSizeEntry *entry)
3036 {
3037   gdouble  value;
3038   gdouble  entry_value;
3039   gboolean value_is_pixel;
3040 
3041   if (G_IS_PARAM_SPEC_INT (param_spec))
3042     {
3043       gint int_value;
3044 
3045       g_object_get (config,
3046                     param_spec->name, &int_value,
3047                     NULL);
3048 
3049       value = int_value;
3050     }
3051   else
3052     {
3053       g_object_get (config,
3054                     param_spec->name, &value,
3055                     NULL);
3056     }
3057 
3058   value_is_pixel = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (entry),
3059                                                        "value-is-pixel"));
3060 
3061   if (value_is_pixel)
3062     entry_value = gimp_size_entry_get_refval (entry, 0);
3063   else
3064     entry_value = gimp_size_entry_get_value (entry, 0);
3065 
3066   if (value != entry_value)
3067     {
3068       g_signal_handlers_block_by_func (entry,
3069                                        gimp_prop_size_entry_callback,
3070                                        config);
3071 
3072       if (value_is_pixel)
3073         gimp_size_entry_set_refval (entry, 0, value);
3074       else
3075         gimp_size_entry_set_value (entry, 0, value);
3076 
3077       g_signal_handlers_unblock_by_func (entry,
3078                                          gimp_prop_size_entry_callback,
3079                                          config);
3080     }
3081 }
3082 
3083 static void
gimp_prop_size_entry_notify_unit(GObject * config,GParamSpec * param_spec,GimpSizeEntry * entry)3084 gimp_prop_size_entry_notify_unit (GObject       *config,
3085                                   GParamSpec    *param_spec,
3086                                   GimpSizeEntry *entry)
3087 {
3088   GimpUnit value;
3089 
3090   g_object_get (config,
3091                 param_spec->name, &value,
3092                 NULL);
3093 
3094   if (value != gimp_size_entry_get_unit (entry))
3095     {
3096       g_signal_handlers_block_by_func (entry,
3097                                        gimp_prop_size_entry_callback,
3098                                        config);
3099 
3100       gimp_size_entry_set_unit (entry, value);
3101 
3102       g_signal_handlers_unblock_by_func (entry,
3103                                          gimp_prop_size_entry_callback,
3104                                          config);
3105     }
3106 }
3107 
3108 static gint
gimp_prop_size_entry_num_chars(gdouble lower,gdouble upper)3109 gimp_prop_size_entry_num_chars (gdouble lower,
3110                                 gdouble upper)
3111 {
3112   gint lower_chars = log (fabs (lower)) / log (10);
3113   gint upper_chars = log (fabs (upper)) / log (10);
3114 
3115   if (lower < 0.0)
3116     lower_chars++;
3117 
3118   if (upper < 0.0)
3119     upper_chars++;
3120 
3121   return MAX (lower_chars, upper_chars);
3122 }
3123 
3124 
3125 /*****************/
3126 /*  coordinates  */
3127 /*****************/
3128 
3129 static void   gimp_prop_coordinates_callback    (GimpSizeEntry *entry,
3130                                                  GObject       *config);
3131 static void   gimp_prop_coordinates_notify_x    (GObject       *config,
3132                                                  GParamSpec    *param_spec,
3133                                                  GimpSizeEntry *entry);
3134 static void   gimp_prop_coordinates_notify_y    (GObject       *config,
3135                                                  GParamSpec    *param_spec,
3136                                                  GimpSizeEntry *entry);
3137 static void   gimp_prop_coordinates_notify_unit (GObject       *config,
3138                                                  GParamSpec    *param_spec,
3139                                                  GimpSizeEntry *entry);
3140 
3141 
3142 /**
3143  * gimp_prop_coordinates_new:
3144  * @config:             Object to which property is attached.
3145  * @x_property_name:    Name of int or double property for X coordinate.
3146  * @y_property_name:    Name of int or double property for Y coordinate.
3147  * @unit_property_name: Name of unit property.
3148  * @unit_format:        A printf-like unit-format string as is used with
3149  *                      gimp_unit_menu_new().
3150  * @update_policy:      How the automatic pixel <-> real-world-unit
3151  *                      calculations should be done.
3152  * @xresolution:        The resolution (in dpi) for the X coordinate.
3153  * @yresolution:        The resolution (in dpi) for the Y coordinate.
3154  * @has_chainbutton:    Whether to add a chainbutton to the size entry.
3155  *
3156  * Creates a #GimpSizeEntry to set and display two double or int
3157  * properties, which will usually represent X and Y coordinates, and
3158  * their associated unit property.
3159  *
3160  * Return value:  A new #GimpSizeEntry widget.
3161  *
3162  * Since: 2.4
3163  */
3164 GtkWidget *
gimp_prop_coordinates_new(GObject * config,const gchar * x_property_name,const gchar * y_property_name,const gchar * unit_property_name,const gchar * unit_format,GimpSizeEntryUpdatePolicy update_policy,gdouble xresolution,gdouble yresolution,gboolean has_chainbutton)3165 gimp_prop_coordinates_new (GObject                   *config,
3166                            const gchar               *x_property_name,
3167                            const gchar               *y_property_name,
3168                            const gchar               *unit_property_name,
3169                            const gchar               *unit_format,
3170                            GimpSizeEntryUpdatePolicy  update_policy,
3171                            gdouble                    xresolution,
3172                            gdouble                    yresolution,
3173                            gboolean                   has_chainbutton)
3174 {
3175   GtkWidget *entry;
3176   GtkWidget *chainbutton = NULL;
3177 
3178   entry = gimp_size_entry_new (2, GIMP_UNIT_INCH, unit_format,
3179                                FALSE, FALSE, TRUE, 10,
3180                                update_policy);
3181 
3182   if (has_chainbutton)
3183     {
3184       chainbutton = gimp_chain_button_new (GIMP_CHAIN_BOTTOM);
3185       gtk_table_attach_defaults (GTK_TABLE (entry), chainbutton, 1, 3, 3, 4);
3186       gtk_widget_show (chainbutton);
3187     }
3188 
3189   if (! gimp_prop_coordinates_connect (config,
3190                                        x_property_name,
3191                                        y_property_name,
3192                                        unit_property_name,
3193                                        entry,
3194                                        chainbutton,
3195                                        xresolution,
3196                                        yresolution))
3197     {
3198       gtk_widget_destroy (entry);
3199       return NULL;
3200     }
3201 
3202   return entry;
3203 }
3204 
3205 gboolean
gimp_prop_coordinates_connect(GObject * config,const gchar * x_property_name,const gchar * y_property_name,const gchar * unit_property_name,GtkWidget * entry,GtkWidget * chainbutton,gdouble xresolution,gdouble yresolution)3206 gimp_prop_coordinates_connect (GObject     *config,
3207                                const gchar *x_property_name,
3208                                const gchar *y_property_name,
3209                                const gchar *unit_property_name,
3210                                GtkWidget   *entry,
3211                                GtkWidget   *chainbutton,
3212                                gdouble      xresolution,
3213                                gdouble      yresolution)
3214 {
3215   GParamSpec *x_param_spec;
3216   GParamSpec *y_param_spec;
3217   GParamSpec *unit_param_spec;
3218   gdouble     x_value, x_lower, x_upper;
3219   gdouble     y_value, y_lower, y_upper;
3220   GimpUnit    unit_value;
3221   gdouble    *old_x_value;
3222   gdouble    *old_y_value;
3223   GimpUnit   *old_unit_value;
3224   gboolean    chain_checked;
3225 
3226   g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (entry), FALSE);
3227   g_return_val_if_fail (GIMP_SIZE_ENTRY (entry)->number_of_fields == 2, FALSE);
3228   g_return_val_if_fail (chainbutton == NULL ||
3229                         GIMP_IS_CHAIN_BUTTON (chainbutton), FALSE);
3230 
3231   x_param_spec = find_param_spec (config, x_property_name, G_STRFUNC);
3232   if (! x_param_spec)
3233     return FALSE;
3234 
3235   y_param_spec = find_param_spec (config, y_property_name, G_STRFUNC);
3236   if (! y_param_spec)
3237     return FALSE;
3238 
3239   if (! get_numeric_values (config, x_param_spec,
3240                             &x_value, &x_lower, &x_upper, G_STRFUNC) ||
3241       ! get_numeric_values (config, y_param_spec,
3242                             &y_value, &y_lower, &y_upper, G_STRFUNC))
3243     return FALSE;
3244 
3245   if (unit_property_name)
3246     {
3247       unit_param_spec = check_param_spec_w (config, unit_property_name,
3248                                             GIMP_TYPE_PARAM_UNIT, G_STRFUNC);
3249       if (! unit_param_spec)
3250         return FALSE;
3251 
3252       g_object_get (config,
3253                     unit_property_name, &unit_value,
3254                     NULL);
3255     }
3256   else
3257     {
3258       unit_param_spec = NULL;
3259       unit_value      = GIMP_UNIT_INCH;
3260     }
3261 
3262   set_param_spec (NULL,
3263                   gimp_size_entry_get_help_widget (GIMP_SIZE_ENTRY (entry), 0),
3264                   x_param_spec);
3265   set_param_spec (NULL,
3266                   gimp_size_entry_get_help_widget (GIMP_SIZE_ENTRY (entry), 1),
3267                   y_param_spec);
3268 
3269   if (unit_param_spec)
3270     set_param_spec (NULL,
3271                     GIMP_SIZE_ENTRY (entry)->unitmenu, unit_param_spec);
3272 
3273   gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (entry), unit_value);
3274 
3275   switch (GIMP_SIZE_ENTRY (entry)->update_policy)
3276     {
3277     case GIMP_SIZE_ENTRY_UPDATE_SIZE:
3278       gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (entry), 0,
3279                                       xresolution, FALSE);
3280       gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (entry), 1,
3281                                       yresolution, FALSE);
3282       chain_checked = (ABS (x_value - y_value) < 1);
3283       break;
3284 
3285     case GIMP_SIZE_ENTRY_UPDATE_RESOLUTION:
3286       chain_checked = (ABS (x_value - y_value) < GIMP_MIN_RESOLUTION);
3287       break;
3288 
3289     default:
3290       chain_checked = (x_value == y_value);
3291       break;
3292     }
3293 
3294   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (entry), 0,
3295                                          x_lower, x_upper);
3296   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (entry), 1,
3297                                          y_lower, y_upper);
3298 
3299   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (entry), 0, x_value);
3300   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (entry), 1, y_value);
3301 
3302   g_object_set_data (G_OBJECT (entry), "gimp-config-param-spec-x",
3303                      x_param_spec);
3304   g_object_set_data (G_OBJECT (entry), "gimp-config-param-spec-y",
3305                      y_param_spec);
3306 
3307   old_x_value  = g_new0 (gdouble, 1);
3308   *old_x_value = x_value;
3309   g_object_set_data_full (G_OBJECT (entry), "old-x-value",
3310                           old_x_value,
3311                           (GDestroyNotify) g_free);
3312 
3313   old_y_value  = g_new0 (gdouble, 1);
3314   *old_y_value = y_value;
3315   g_object_set_data_full (G_OBJECT (entry), "old-y-value",
3316                           old_y_value,
3317                           (GDestroyNotify) g_free);
3318 
3319   if (chainbutton)
3320     {
3321       if (chain_checked)
3322         gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chainbutton), TRUE);
3323 
3324       g_object_set_data (G_OBJECT (entry), "chainbutton", chainbutton);
3325     }
3326 
3327   g_signal_connect (entry, "value-changed",
3328                     G_CALLBACK (gimp_prop_coordinates_callback),
3329                     config);
3330   g_signal_connect (entry, "refval-changed",
3331                     G_CALLBACK (gimp_prop_coordinates_callback),
3332                     config);
3333 
3334   connect_notify (config, x_property_name,
3335                   G_CALLBACK (gimp_prop_coordinates_notify_x),
3336                   entry);
3337   connect_notify (config, y_property_name,
3338                   G_CALLBACK (gimp_prop_coordinates_notify_y),
3339                   entry);
3340 
3341   if (unit_property_name)
3342     {
3343       g_object_set_data (G_OBJECT (entry), "gimp-config-param-spec-unit",
3344                          unit_param_spec);
3345 
3346       old_unit_value  = g_new0 (GimpUnit, 1);
3347       *old_unit_value = unit_value;
3348       g_object_set_data_full (G_OBJECT (entry), "old-unit-value",
3349                               old_unit_value,
3350                               (GDestroyNotify) g_free);
3351 
3352       g_signal_connect (entry, "unit-changed",
3353                         G_CALLBACK (gimp_prop_coordinates_callback),
3354                         config);
3355 
3356       connect_notify (config, unit_property_name,
3357                       G_CALLBACK (gimp_prop_coordinates_notify_unit),
3358                       entry);
3359     }
3360 
3361   return TRUE;
3362 }
3363 
3364 static void
gimp_prop_coordinates_callback(GimpSizeEntry * entry,GObject * config)3365 gimp_prop_coordinates_callback (GimpSizeEntry *entry,
3366                                 GObject       *config)
3367 {
3368   GParamSpec *x_param_spec;
3369   GParamSpec *y_param_spec;
3370   GParamSpec *unit_param_spec;
3371   gdouble     x_value;
3372   gdouble     y_value;
3373   GimpUnit    unit_value;
3374   gdouble    *old_x_value;
3375   gdouble    *old_y_value;
3376   GimpUnit   *old_unit_value;
3377   gboolean    backwards;
3378 
3379   x_param_spec = g_object_get_data (G_OBJECT (entry),
3380                                     "gimp-config-param-spec-x");
3381   y_param_spec = g_object_get_data (G_OBJECT (entry),
3382                                     "gimp-config-param-spec-y");
3383 
3384   if (! x_param_spec || ! y_param_spec)
3385     return;
3386 
3387   unit_param_spec = g_object_get_data (G_OBJECT (entry),
3388                                        "gimp-config-param-spec-unit");
3389 
3390   x_value    = gimp_size_entry_get_refval (entry, 0);
3391   y_value    = gimp_size_entry_get_refval (entry, 1);
3392   unit_value = gimp_size_entry_get_unit (entry);
3393 
3394   old_x_value    = g_object_get_data (G_OBJECT (entry), "old-x-value");
3395   old_y_value    = g_object_get_data (G_OBJECT (entry), "old-y-value");
3396   old_unit_value = g_object_get_data (G_OBJECT (entry), "old-unit-value");
3397 
3398   if (! old_x_value || ! old_y_value || (unit_param_spec && ! old_unit_value))
3399     return;
3400 
3401   /*
3402    * FIXME: if the entry was created using gimp_coordinates_new, then
3403    * the chain button is handled automatically and the following block
3404    * of code is unnecessary (and, in fact, redundant).
3405    */
3406   if (x_value != y_value)
3407     {
3408       GtkWidget *chainbutton;
3409 
3410       chainbutton = g_object_get_data (G_OBJECT (entry), "chainbutton");
3411 
3412       if (chainbutton &&
3413           gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (chainbutton)) &&
3414           ! g_object_get_data (G_OBJECT (chainbutton), "constrains-ratio"))
3415         {
3416           if (x_value != *old_x_value)
3417             y_value = x_value;
3418           else if (y_value != *old_y_value)
3419             x_value = y_value;
3420         }
3421     }
3422 
3423   backwards = (*old_x_value == x_value);
3424 
3425   if (*old_x_value == x_value &&
3426       *old_y_value == y_value &&
3427       (old_unit_value == NULL || *old_unit_value == unit_value))
3428     return;
3429 
3430   *old_x_value = x_value;
3431   *old_y_value = y_value;
3432 
3433   if (old_unit_value)
3434     *old_unit_value = unit_value;
3435 
3436   if (unit_param_spec)
3437     g_object_set (config,
3438                   unit_param_spec->name, unit_value,
3439                   NULL);
3440 
3441   if (G_IS_PARAM_SPEC_INT (x_param_spec) &&
3442       G_IS_PARAM_SPEC_INT (y_param_spec))
3443     {
3444       if (backwards)
3445         g_object_set (config,
3446                       y_param_spec->name, ROUND (y_value),
3447                       x_param_spec->name, ROUND (x_value),
3448                       NULL);
3449       else
3450         g_object_set (config,
3451                       x_param_spec->name, ROUND (x_value),
3452                       y_param_spec->name, ROUND (y_value),
3453                       NULL);
3454 
3455     }
3456   else if (G_IS_PARAM_SPEC_DOUBLE (x_param_spec) &&
3457            G_IS_PARAM_SPEC_DOUBLE (y_param_spec))
3458     {
3459       if (backwards)
3460         g_object_set (config,
3461                       y_param_spec->name, y_value,
3462                       x_param_spec->name, x_value,
3463                       NULL);
3464       else
3465         g_object_set (config,
3466                       x_param_spec->name, x_value,
3467                       y_param_spec->name, y_value,
3468                       NULL);
3469     }
3470 }
3471 
3472 static void
gimp_prop_coordinates_notify_x(GObject * config,GParamSpec * param_spec,GimpSizeEntry * entry)3473 gimp_prop_coordinates_notify_x (GObject       *config,
3474                                 GParamSpec    *param_spec,
3475                                 GimpSizeEntry *entry)
3476 {
3477   gdouble value;
3478 
3479   if (G_IS_PARAM_SPEC_INT (param_spec))
3480     {
3481       gint int_value;
3482 
3483       g_object_get (config,
3484                     param_spec->name, &int_value,
3485                     NULL);
3486 
3487       value = int_value;
3488     }
3489   else
3490     {
3491       g_object_get (config,
3492                     param_spec->name, &value,
3493                     NULL);
3494     }
3495 
3496   if (value != gimp_size_entry_get_refval (entry, 0))
3497     {
3498       gdouble *old_x_value = g_object_get_data (G_OBJECT (entry),
3499                                                 "old-x-value");
3500 
3501       g_signal_handlers_block_by_func (entry,
3502                                        gimp_prop_coordinates_callback,
3503                                        config);
3504 
3505       gimp_size_entry_set_refval (entry, 0, value);
3506 
3507       if (old_x_value)
3508         *old_x_value = value;
3509 
3510       g_signal_emit_by_name (entry, "value-changed",
3511                              gimp_size_entry_get_value (entry, 0));
3512 
3513       g_signal_handlers_unblock_by_func (entry,
3514                                          gimp_prop_coordinates_callback,
3515                                          config);
3516     }
3517 }
3518 
3519 static void
gimp_prop_coordinates_notify_y(GObject * config,GParamSpec * param_spec,GimpSizeEntry * entry)3520 gimp_prop_coordinates_notify_y (GObject       *config,
3521                                 GParamSpec    *param_spec,
3522                                 GimpSizeEntry *entry)
3523 {
3524   gdouble value;
3525 
3526   if (G_IS_PARAM_SPEC_INT (param_spec))
3527     {
3528       gint int_value;
3529 
3530       g_object_get (config,
3531                     param_spec->name, &int_value,
3532                     NULL);
3533 
3534       value = int_value;
3535     }
3536   else
3537     {
3538       g_object_get (config,
3539                     param_spec->name, &value,
3540                     NULL);
3541     }
3542 
3543   if (value != gimp_size_entry_get_refval (entry, 1))
3544     {
3545       gdouble *old_y_value = g_object_get_data (G_OBJECT (entry),
3546                                                 "old-y-value");
3547 
3548       g_signal_handlers_block_by_func (entry,
3549                                        gimp_prop_coordinates_callback,
3550                                        config);
3551 
3552       gimp_size_entry_set_refval (entry, 1, value);
3553 
3554       if (old_y_value)
3555         *old_y_value = value;
3556 
3557       g_signal_emit_by_name (entry, "value-changed",
3558                              gimp_size_entry_get_value (entry, 1));
3559 
3560       g_signal_handlers_unblock_by_func (entry,
3561                                          gimp_prop_coordinates_callback,
3562                                          config);
3563     }
3564 }
3565 
3566 static void
gimp_prop_coordinates_notify_unit(GObject * config,GParamSpec * param_spec,GimpSizeEntry * entry)3567 gimp_prop_coordinates_notify_unit (GObject       *config,
3568                                    GParamSpec    *param_spec,
3569                                    GimpSizeEntry *entry)
3570 {
3571   GimpUnit value;
3572 
3573   g_object_get (config,
3574                 param_spec->name, &value,
3575                 NULL);
3576 
3577   if (value != gimp_size_entry_get_unit (entry))
3578     {
3579       g_signal_handlers_block_by_func (entry,
3580                                        gimp_prop_coordinates_callback,
3581                                        config);
3582 
3583       gimp_size_entry_set_unit (entry, value);
3584 
3585       g_signal_handlers_unblock_by_func (entry,
3586                                          gimp_prop_coordinates_callback,
3587                                          config);
3588     }
3589 }
3590 
3591 
3592 /****************/
3593 /*  color area  */
3594 /****************/
3595 
3596 static void   gimp_prop_color_area_callback (GtkWidget  *widget,
3597                                              GObject    *config);
3598 static void   gimp_prop_color_area_notify   (GObject    *config,
3599                                              GParamSpec *param_spec,
3600                                              GtkWidget  *area);
3601 
3602 /**
3603  * gimp_prop_color_area_new:
3604  * @config:        Object to which property is attached.
3605  * @property_name: Name of RGB property.
3606  * @width:         Width of color area.
3607  * @height:        Height of color area.
3608  * @type:          How transparency is represented.
3609  *
3610  * Creates a #GimpColorArea to set and display the value of an RGB
3611  * property.
3612  *
3613  * Return value:  A new #GimpColorArea widget.
3614  *
3615  * Since: 2.4
3616  */
3617 GtkWidget *
gimp_prop_color_area_new(GObject * config,const gchar * property_name,gint width,gint height,GimpColorAreaType type)3618 gimp_prop_color_area_new (GObject           *config,
3619                           const gchar       *property_name,
3620                           gint               width,
3621                           gint               height,
3622                           GimpColorAreaType  type)
3623 {
3624   GParamSpec *param_spec;
3625   GtkWidget  *area;
3626   GimpRGB    *value;
3627 
3628   param_spec = check_param_spec_w (config, property_name,
3629                                    GIMP_TYPE_PARAM_RGB, G_STRFUNC);
3630   if (! param_spec)
3631     return NULL;
3632 
3633   g_object_get (config,
3634                 property_name, &value,
3635                 NULL);
3636 
3637   area = gimp_color_area_new (value, type,
3638                               GDK_BUTTON1_MASK | GDK_BUTTON2_MASK);
3639   gtk_widget_set_size_request (area, width, height);
3640 
3641   g_free (value);
3642 
3643   set_param_spec (G_OBJECT (area), area, param_spec);
3644 
3645   g_signal_connect (area, "color-changed",
3646                     G_CALLBACK (gimp_prop_color_area_callback),
3647                     config);
3648 
3649   connect_notify (config, property_name,
3650                   G_CALLBACK (gimp_prop_color_area_notify),
3651                   area);
3652 
3653   return area;
3654 }
3655 
3656 static void
gimp_prop_color_area_callback(GtkWidget * area,GObject * config)3657 gimp_prop_color_area_callback (GtkWidget *area,
3658                                GObject   *config)
3659 {
3660   GParamSpec *param_spec;
3661   GimpRGB     value;
3662 
3663   param_spec = get_param_spec (G_OBJECT (area));
3664   if (! param_spec)
3665     return;
3666 
3667   gimp_color_area_get_color (GIMP_COLOR_AREA (area), &value);
3668 
3669   g_signal_handlers_block_by_func (config,
3670                                    gimp_prop_color_area_notify,
3671                                    area);
3672 
3673   g_object_set (config,
3674                 param_spec->name, &value,
3675                 NULL);
3676 
3677   g_signal_handlers_unblock_by_func (config,
3678                                      gimp_prop_color_area_notify,
3679                                      area);
3680 }
3681 
3682 static void
gimp_prop_color_area_notify(GObject * config,GParamSpec * param_spec,GtkWidget * area)3683 gimp_prop_color_area_notify (GObject    *config,
3684                              GParamSpec *param_spec,
3685                              GtkWidget  *area)
3686 {
3687   GimpRGB *value;
3688 
3689   g_object_get (config,
3690                 param_spec->name, &value,
3691                 NULL);
3692 
3693   g_signal_handlers_block_by_func (area,
3694                                    gimp_prop_color_area_callback,
3695                                    config);
3696 
3697   gimp_color_area_set_color (GIMP_COLOR_AREA (area), value);
3698 
3699   g_free (value);
3700 
3701   g_signal_handlers_unblock_by_func (area,
3702                                      gimp_prop_color_area_callback,
3703                                      config);
3704 }
3705 
3706 
3707 /********************/
3708 /*  unit combo box  */
3709 /********************/
3710 
3711 static void   gimp_prop_unit_combo_box_callback (GtkWidget  *combo,
3712                                                  GObject    *config);
3713 static void   gimp_prop_unit_combo_box_notify   (GObject    *config,
3714                                                  GParamSpec *param_spec,
3715                                                  GtkWidget  *combo);
3716 
3717 /**
3718  * gimp_prop_unit_combo_box_new:
3719  * @config:        Object to which property is attached.
3720  * @property_name: Name of Unit property.
3721  *
3722  * Creates a #GimpUnitComboBox to set and display the value of a Unit
3723  * property.  See gimp_unit_combo_box_new() for more information.
3724  *
3725  * Return value:  A new #GimpUnitComboBox widget.
3726  *
3727  * Since: 2.8
3728  */
3729 GtkWidget *
gimp_prop_unit_combo_box_new(GObject * config,const gchar * property_name)3730 gimp_prop_unit_combo_box_new (GObject     *config,
3731                               const gchar *property_name)
3732 {
3733   GParamSpec   *param_spec;
3734   GtkWidget    *combo;
3735   GtkTreeModel *model;
3736   GimpUnit      unit;
3737   GValue        value = G_VALUE_INIT;
3738   gboolean      show_pixels;
3739   gboolean      show_percent;
3740 
3741   param_spec = check_param_spec_w (config, property_name,
3742                                    GIMP_TYPE_PARAM_UNIT, G_STRFUNC);
3743   if (! param_spec)
3744     return NULL;
3745 
3746   g_value_init (&value, param_spec->value_type);
3747 
3748   g_value_set_int (&value, GIMP_UNIT_PIXEL);
3749   show_pixels = (g_param_value_validate (param_spec, &value) == FALSE);
3750 
3751   g_value_set_int (&value, GIMP_UNIT_PERCENT);
3752   show_percent = (g_param_value_validate (param_spec, &value) == FALSE);
3753 
3754   g_value_unset (&value);
3755 
3756   g_object_get (config,
3757                 property_name, &unit,
3758                 NULL);
3759 
3760   combo = gimp_unit_combo_box_new ();
3761   model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
3762   gimp_unit_store_set_has_pixels (GIMP_UNIT_STORE (model), show_pixels);
3763   gimp_unit_store_set_has_percent (GIMP_UNIT_STORE (model), show_percent);
3764 
3765   gimp_unit_combo_box_set_active (GIMP_UNIT_COMBO_BOX (combo), unit);
3766 
3767   set_param_spec (G_OBJECT (combo), combo, param_spec);
3768 
3769   g_signal_connect (combo, "changed",
3770                     G_CALLBACK (gimp_prop_unit_combo_box_callback),
3771                     config);
3772 
3773   connect_notify (config, property_name,
3774                   G_CALLBACK (gimp_prop_unit_combo_box_notify),
3775                   combo);
3776 
3777   return combo;
3778 }
3779 
3780 static void
gimp_prop_unit_combo_box_callback(GtkWidget * combo,GObject * config)3781 gimp_prop_unit_combo_box_callback (GtkWidget *combo,
3782                                    GObject   *config)
3783 {
3784   GParamSpec *param_spec;
3785   GimpUnit    value;
3786   GimpUnit    v;
3787 
3788   param_spec = get_param_spec (G_OBJECT (combo));
3789   if (! param_spec)
3790     return;
3791 
3792   value = gimp_unit_combo_box_get_active (GIMP_UNIT_COMBO_BOX (combo));
3793 
3794   g_object_get (config, param_spec->name, &v, NULL);
3795 
3796   if (v != value)
3797     {
3798       /* FIXME gimp_unit_menu_update (menu, &unit); */
3799 
3800       g_signal_handlers_block_by_func (config,
3801                                        gimp_prop_unit_combo_box_notify,
3802                                        combo);
3803 
3804       g_object_set (config, param_spec->name, value, NULL);
3805 
3806       g_signal_handlers_unblock_by_func (config,
3807                                          gimp_prop_unit_combo_box_notify,
3808                                          combo);
3809     }
3810 }
3811 
3812 static void
gimp_prop_unit_combo_box_notify(GObject * config,GParamSpec * param_spec,GtkWidget * combo)3813 gimp_prop_unit_combo_box_notify (GObject    *config,
3814                                  GParamSpec *param_spec,
3815                                  GtkWidget  *combo)
3816 {
3817   GimpUnit  unit;
3818 
3819   g_object_get (config,
3820                 param_spec->name, &unit,
3821                 NULL);
3822 
3823   g_signal_handlers_block_by_func (combo,
3824                                    gimp_prop_unit_combo_box_callback,
3825                                    config);
3826 
3827   gimp_unit_combo_box_set_active (GIMP_UNIT_COMBO_BOX (combo), unit);
3828 
3829   /* FIXME gimp_unit_menu_update (menu, &unit); */
3830 
3831   g_signal_handlers_unblock_by_func (combo,
3832                                      gimp_prop_unit_combo_box_callback,
3833                                      config);
3834 }
3835 
3836 
3837 /***************/
3838 /*  unit menu  */
3839 /***************/
3840 
3841 static void   gimp_prop_unit_menu_callback (GtkWidget  *menu,
3842                                             GObject    *config);
3843 static void   gimp_prop_unit_menu_notify   (GObject    *config,
3844                                             GParamSpec *param_spec,
3845                                             GtkWidget  *menu);
3846 
3847 /**
3848  * gimp_prop_unit_menu_new:
3849  * @config:        Object to which property is attached.
3850  * @property_name: Name of Unit property.
3851  * @unit_format:   A printf-like format string which is used to create
3852  *                 the unit strings.
3853  *
3854  * Creates a #GimpUnitMenu to set and display the value of a Unit
3855  * property.  See gimp_unit_menu_new() for more information.
3856  *
3857  * Return value:  A new #GimpUnitMenu widget.
3858  *
3859  * Since: 2.4
3860  *
3861  * Deprecated: 2.10
3862  */
3863 GtkWidget *
gimp_prop_unit_menu_new(GObject * config,const gchar * property_name,const gchar * unit_format)3864 gimp_prop_unit_menu_new (GObject     *config,
3865                          const gchar *property_name,
3866                          const gchar *unit_format)
3867 {
3868   GParamSpec *param_spec;
3869   GtkWidget  *menu;
3870   GimpUnit    unit;
3871   GValue      value = G_VALUE_INIT;
3872   gboolean    show_pixels;
3873   gboolean    show_percent;
3874 
3875   param_spec = check_param_spec_w (config, property_name,
3876                                    GIMP_TYPE_PARAM_UNIT, G_STRFUNC);
3877   if (! param_spec)
3878     return NULL;
3879 
3880   g_value_init (&value, param_spec->value_type);
3881 
3882   g_value_set_int (&value, GIMP_UNIT_PIXEL);
3883   show_pixels = (g_param_value_validate (param_spec, &value) == FALSE);
3884 
3885   g_value_set_int (&value, GIMP_UNIT_PERCENT);
3886   show_percent = (g_param_value_validate (param_spec, &value) == FALSE);
3887 
3888   g_value_unset (&value);
3889 
3890   g_object_get (config,
3891                 property_name, &unit,
3892                 NULL);
3893 
3894   menu = gimp_unit_menu_new (unit_format,
3895                              unit, show_pixels, show_percent, TRUE);
3896 
3897   set_param_spec (G_OBJECT (menu), menu, param_spec);
3898 
3899   g_signal_connect (menu, "unit-changed",
3900                     G_CALLBACK (gimp_prop_unit_menu_callback),
3901                     config);
3902 
3903   connect_notify (config, property_name,
3904                   G_CALLBACK (gimp_prop_unit_menu_notify),
3905                   menu);
3906 
3907   return menu;
3908 }
3909 
3910 static void
gimp_prop_unit_menu_callback(GtkWidget * menu,GObject * config)3911 gimp_prop_unit_menu_callback (GtkWidget *menu,
3912                               GObject   *config)
3913 {
3914   GParamSpec *param_spec;
3915   GimpUnit    unit;
3916 
3917   param_spec = get_param_spec (G_OBJECT (menu));
3918   if (! param_spec)
3919     return;
3920 
3921   gimp_unit_menu_update (menu, &unit);
3922 
3923   g_signal_handlers_block_by_func (config,
3924                                    gimp_prop_unit_menu_notify,
3925                                    menu);
3926 
3927   g_object_set (config,
3928                 param_spec->name, unit,
3929                 NULL);
3930 
3931   g_signal_handlers_unblock_by_func (config,
3932                                      gimp_prop_unit_menu_notify,
3933                                      menu);
3934 }
3935 
3936 static void
gimp_prop_unit_menu_notify(GObject * config,GParamSpec * param_spec,GtkWidget * menu)3937 gimp_prop_unit_menu_notify (GObject    *config,
3938                             GParamSpec *param_spec,
3939                             GtkWidget  *menu)
3940 {
3941   GimpUnit  unit;
3942 
3943   g_object_get (config,
3944                 param_spec->name, &unit,
3945                 NULL);
3946 
3947   g_signal_handlers_block_by_func (menu,
3948                                    gimp_prop_unit_menu_callback,
3949                                    config);
3950 
3951   gimp_unit_menu_set_unit (GIMP_UNIT_MENU (menu), unit);
3952   gimp_unit_menu_update (menu, &unit);
3953 
3954   g_signal_handlers_unblock_by_func (menu,
3955                                      gimp_prop_unit_menu_callback,
3956                                      config);
3957 }
3958 
3959 
3960 /***************/
3961 /*  icon name  */
3962 /***************/
3963 
3964 static void   gimp_prop_icon_image_notify (GObject    *config,
3965                                            GParamSpec *param_spec,
3966                                            GtkWidget  *image);
3967 
3968 /**
3969  * gimp_prop_stock_image_new:
3970  * @config:        Object to which property is attached.
3971  * @property_name: Name of string property.
3972  * @icon_size:     Size of desired stock image.
3973  *
3974  * Creates a widget to display a stock image representing the value of the
3975  * specified string property, which should encode a Stock ID.
3976  * See gtk_image_new_from_stock() for more information.
3977  *
3978  * Return value:  A new #GtkImage widget.
3979  *
3980  * Since: 2.4
3981  *
3982  * Deprecated: 2.10
3983  */
3984 GtkWidget *
gimp_prop_stock_image_new(GObject * config,const gchar * property_name,GtkIconSize icon_size)3985 gimp_prop_stock_image_new (GObject     *config,
3986                            const gchar *property_name,
3987                            GtkIconSize  icon_size)
3988 {
3989   return gimp_prop_icon_image_new (config, property_name, icon_size);
3990 }
3991 
3992 /**
3993  * gimp_prop_icon_image_new:
3994  * @config:        Object to which property is attached.
3995  * @property_name: Name of string property.
3996  * @icon_size:     Size of desired icon image.
3997  *
3998  * Creates a widget to display a icon image representing the value of the
3999  * specified string property, which should encode an icon name.
4000  * See gtk_image_new_from_icon_name() for more information.
4001  *
4002  * Return value:  A new #GtkImage widget.
4003  *
4004  * Since: 2.10
4005  */
4006 GtkWidget *
gimp_prop_icon_image_new(GObject * config,const gchar * property_name,GtkIconSize icon_size)4007 gimp_prop_icon_image_new (GObject     *config,
4008                           const gchar *property_name,
4009                           GtkIconSize  icon_size)
4010 {
4011   GParamSpec *param_spec;
4012   GtkWidget  *image;
4013   gchar      *icon_name;
4014 
4015   param_spec = check_param_spec (config, property_name,
4016                                  G_TYPE_PARAM_STRING, G_STRFUNC);
4017   if (! param_spec)
4018     return NULL;
4019 
4020   g_object_get (config,
4021                 property_name, &icon_name,
4022                 NULL);
4023 
4024   image = gtk_image_new_from_icon_name (icon_name, icon_size);
4025 
4026   if (icon_name)
4027     g_free (icon_name);
4028 
4029   set_param_spec (G_OBJECT (image), image, param_spec);
4030 
4031   connect_notify (config, property_name,
4032                   G_CALLBACK (gimp_prop_icon_image_notify),
4033                   image);
4034 
4035   return image;
4036 }
4037 
4038 static void
gimp_prop_icon_image_notify(GObject * config,GParamSpec * param_spec,GtkWidget * image)4039 gimp_prop_icon_image_notify (GObject    *config,
4040                              GParamSpec *param_spec,
4041                              GtkWidget  *image)
4042 {
4043   gchar       *icon_name;
4044   GtkIconSize  icon_size;
4045 
4046   g_object_get (config,
4047                 param_spec->name, &icon_name,
4048                 NULL);
4049 
4050   gtk_image_get_icon_name (GTK_IMAGE (image), NULL, &icon_size);
4051   gtk_image_set_from_icon_name (GTK_IMAGE (image), icon_name, icon_size);
4052 
4053   if (icon_name)
4054     g_free (icon_name);
4055 }
4056 
4057 
4058 /**************/
4059 /*  expander  */
4060 /**************/
4061 
4062 static void   gimp_prop_expanded_notify (GtkExpander *expander,
4063                                          GParamSpec  *param_spec,
4064                                          GObject     *config);
4065 static void   gimp_prop_expander_notify (GObject     *config,
4066                                          GParamSpec  *param_spec,
4067                                          GtkExpander *expander);
4068 
4069 
4070 /**
4071  * gimp_prop_expander_new:
4072  * @config:        Object to which property is attached.
4073  * @property_name: Name of boolean property.
4074  * @label:         Label for expander.
4075  *
4076  * Creates a #GtkExpander controlled by the specified boolean property.
4077  * A value of %TRUE for the property corresponds to the expanded state
4078  * for the widget.
4079  * If @label is #NULL, the @property_name's nick will be used as label
4080  * of the returned widget.
4081  *
4082  * Return value:  A new #GtkExpander widget.
4083  *
4084  * Since: 2.4
4085  */
4086 GtkWidget *
gimp_prop_expander_new(GObject * config,const gchar * property_name,const gchar * label)4087 gimp_prop_expander_new (GObject     *config,
4088                         const gchar *property_name,
4089                         const gchar *label)
4090 {
4091   GParamSpec *param_spec;
4092   GtkWidget  *expander;
4093   gboolean    value;
4094 
4095   param_spec = check_param_spec_w (config, property_name,
4096                                    G_TYPE_PARAM_BOOLEAN, G_STRFUNC);
4097   if (! param_spec)
4098     return NULL;
4099 
4100   if (! label)
4101     label = g_param_spec_get_nick (param_spec);
4102 
4103   g_object_get (config,
4104                 property_name, &value,
4105                 NULL);
4106 
4107   expander = g_object_new (GTK_TYPE_EXPANDER,
4108                            "label",    label,
4109                            "expanded", value,
4110                            NULL);
4111 
4112   set_param_spec (G_OBJECT (expander), expander, param_spec);
4113 
4114   g_signal_connect (expander, "notify::expanded",
4115                     G_CALLBACK (gimp_prop_expanded_notify),
4116                     config);
4117 
4118   connect_notify (config, property_name,
4119                   G_CALLBACK (gimp_prop_expander_notify),
4120                   expander);
4121 
4122   return expander;
4123 }
4124 
4125 static void
gimp_prop_expanded_notify(GtkExpander * expander,GParamSpec * param_spec,GObject * config)4126 gimp_prop_expanded_notify (GtkExpander *expander,
4127                            GParamSpec  *param_spec,
4128                            GObject     *config)
4129 {
4130   param_spec = get_param_spec (G_OBJECT (expander));
4131   if (! param_spec)
4132     return;
4133 
4134   g_object_set (config,
4135                 param_spec->name, gtk_expander_get_expanded (expander),
4136                 NULL);
4137 }
4138 
4139 static void
gimp_prop_expander_notify(GObject * config,GParamSpec * param_spec,GtkExpander * expander)4140 gimp_prop_expander_notify (GObject     *config,
4141                            GParamSpec  *param_spec,
4142                            GtkExpander *expander)
4143 {
4144   gboolean value;
4145 
4146   g_object_get (config,
4147                 param_spec->name, &value,
4148                 NULL);
4149 
4150   if (gtk_expander_get_expanded (expander) != value)
4151     {
4152       g_signal_handlers_block_by_func (expander,
4153                                        gimp_prop_expanded_notify,
4154                                        config);
4155 
4156       gtk_expander_set_expanded (expander, value);
4157 
4158       g_signal_handlers_unblock_by_func (expander,
4159                                          gimp_prop_expanded_notify,
4160                                          config);
4161     }
4162 }
4163 
4164 
4165 /*******************************/
4166 /*  private utility functions  */
4167 /*******************************/
4168 
4169 static GQuark gimp_prop_widgets_param_spec_quark (void) G_GNUC_CONST;
4170 
4171 #define PARAM_SPEC_QUARK (gimp_prop_widgets_param_spec_quark ())
4172 
4173 static GQuark
gimp_prop_widgets_param_spec_quark(void)4174 gimp_prop_widgets_param_spec_quark (void)
4175 {
4176   static GQuark param_spec_quark = 0;
4177 
4178   if (! param_spec_quark)
4179     param_spec_quark = g_quark_from_static_string ("gimp-config-param-spec");
4180 
4181   return param_spec_quark;
4182 }
4183 
4184 static void
set_param_spec(GObject * object,GtkWidget * widget,GParamSpec * param_spec)4185 set_param_spec (GObject     *object,
4186                 GtkWidget   *widget,
4187                 GParamSpec  *param_spec)
4188 {
4189   if (object)
4190     {
4191       g_object_set_qdata (object, PARAM_SPEC_QUARK, param_spec);
4192     }
4193 
4194   if (widget)
4195     {
4196       const gchar *blurb = g_param_spec_get_blurb (param_spec);
4197 
4198       if (blurb)
4199         gimp_help_set_help_data (widget, blurb, NULL);
4200     }
4201 }
4202 
4203 static void
set_radio_spec(GObject * object,GParamSpec * param_spec)4204 set_radio_spec (GObject    *object,
4205                 GParamSpec *param_spec)
4206 {
4207   GSList *list;
4208 
4209   for (list = gtk_radio_button_get_group (GTK_RADIO_BUTTON (object));
4210        list;
4211        list = g_slist_next (list))
4212     {
4213       set_param_spec (list->data, NULL, param_spec);
4214     }
4215 }
4216 
4217 static GParamSpec *
get_param_spec(GObject * object)4218 get_param_spec (GObject *object)
4219 {
4220   return g_object_get_qdata (object, PARAM_SPEC_QUARK);
4221 }
4222 
4223 static GParamSpec *
find_param_spec(GObject * object,const gchar * property_name,const gchar * strloc)4224 find_param_spec (GObject     *object,
4225                  const gchar *property_name,
4226                  const gchar *strloc)
4227 {
4228   GParamSpec *param_spec;
4229 
4230   param_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (object),
4231                                              property_name);
4232 
4233   if (! param_spec)
4234     g_warning ("%s: %s has no property named '%s'",
4235                strloc,
4236                g_type_name (G_TYPE_FROM_INSTANCE (object)),
4237                property_name);
4238 
4239   return param_spec;
4240 }
4241 
4242 static GParamSpec *
check_param_spec(GObject * object,const gchar * property_name,GType type,const gchar * strloc)4243 check_param_spec (GObject     *object,
4244                   const gchar *property_name,
4245                   GType        type,
4246                   const gchar *strloc)
4247 {
4248   GParamSpec *param_spec;
4249 
4250   param_spec = find_param_spec (object, property_name, strloc);
4251 
4252   if (param_spec && ! g_type_is_a (G_TYPE_FROM_INSTANCE (param_spec), type))
4253     {
4254       g_warning ("%s: property '%s' of %s is not a %s",
4255                  strloc,
4256                  param_spec->name,
4257                  g_type_name (param_spec->owner_type),
4258                  g_type_name (type));
4259       return NULL;
4260     }
4261 
4262   return param_spec;
4263 }
4264 
4265 static GParamSpec *
check_param_spec_w(GObject * object,const gchar * property_name,GType type,const gchar * strloc)4266 check_param_spec_w (GObject     *object,
4267                     const gchar *property_name,
4268                     GType        type,
4269                     const gchar *strloc)
4270 {
4271   GParamSpec *param_spec;
4272 
4273   param_spec = check_param_spec (object, property_name, type, strloc);
4274 
4275   if (param_spec &&
4276       (param_spec->flags & G_PARAM_WRITABLE) == 0)
4277     {
4278       g_warning ("%s: property '%s' of %s is not writable",
4279                  strloc,
4280                  param_spec->name,
4281                  g_type_name (param_spec->owner_type));
4282       return NULL;
4283     }
4284 
4285   return param_spec;
4286 }
4287 
4288 static gboolean
get_numeric_values(GObject * object,GParamSpec * param_spec,gdouble * value,gdouble * lower,gdouble * upper,const gchar * strloc)4289 get_numeric_values (GObject     *object,
4290                     GParamSpec  *param_spec,
4291                     gdouble     *value,
4292                     gdouble     *lower,
4293                     gdouble     *upper,
4294                     const gchar *strloc)
4295 {
4296   if (G_IS_PARAM_SPEC_INT (param_spec))
4297     {
4298       GParamSpecInt *int_spec = G_PARAM_SPEC_INT (param_spec);
4299       gint           int_value;
4300 
4301       g_object_get (object, param_spec->name, &int_value, NULL);
4302 
4303       *value = int_value;
4304       *lower = int_spec->minimum;
4305       *upper = int_spec->maximum;
4306     }
4307   else if (G_IS_PARAM_SPEC_UINT (param_spec))
4308     {
4309       GParamSpecUInt *uint_spec = G_PARAM_SPEC_UINT (param_spec);
4310       guint           uint_value;
4311 
4312       g_object_get (object, param_spec->name, &uint_value, NULL);
4313 
4314       *value = uint_value;
4315       *lower = uint_spec->minimum;
4316       *upper = uint_spec->maximum;
4317     }
4318   else if (G_IS_PARAM_SPEC_DOUBLE (param_spec))
4319     {
4320       GParamSpecDouble *double_spec = G_PARAM_SPEC_DOUBLE (param_spec);
4321 
4322       g_object_get (object, param_spec->name, value, NULL);
4323 
4324       *lower = double_spec->minimum;
4325       *upper = double_spec->maximum;
4326     }
4327   else
4328     {
4329       g_warning ("%s: property '%s' of %s is not numeric",
4330                  strloc,
4331                  param_spec->name,
4332                  g_type_name (G_TYPE_FROM_INSTANCE (object)));
4333       return FALSE;
4334     }
4335 
4336   return TRUE;
4337 }
4338 
4339 static void
connect_notify(GObject * config,const gchar * property_name,GCallback callback,gpointer callback_data)4340 connect_notify (GObject     *config,
4341                 const gchar *property_name,
4342                 GCallback    callback,
4343                 gpointer     callback_data)
4344 {
4345   gchar *notify_name;
4346 
4347   notify_name = g_strconcat ("notify::", property_name, NULL);
4348 
4349   g_signal_connect_object (config, notify_name, callback, callback_data, 0);
4350 
4351   g_free (notify_name);
4352 }
4353