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