1 /* LIBGIMP - The GIMP Library
2 * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
3 *
4 * gimpwidgets.c
5 * Copyright (C) 2000 Michael Natterer <mitch@gimp.org>
6 *
7 * This library is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 3 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see
19 * <https://www.gnu.org/licenses/>.
20 */
21
22 #include "config.h"
23
24 #include <gegl.h>
25 #include <gtk/gtk.h>
26
27 #include "libgimpcolor/gimpcolor.h"
28 #include "libgimpmath/gimpmath.h"
29 #include "libgimpbase/gimpbase.h"
30
31 #include "gimpwidgets.h"
32
33 #include "libgimp/libgimp-intl.h"
34
35
36 /* hack: declare prototype here instead of #undef GIMP_DISABLE_DEPRECATED */
37 void gimp_toggle_button_sensitive_update (GtkToggleButton *toggle_button);
38
39
40 /**
41 * SECTION: gimpwidgets
42 * @title: GimpWidgets
43 * @short_description: A collection of convenient widget constructors,
44 * standard callbacks and helper functions.
45 *
46 * A collection of convenient widget constructors, standard callbacks
47 * and helper functions.
48 **/
49
50
51 /**
52 * gimp_radio_group_new:
53 * @in_frame: %TRUE if you want a #GtkFrame around the radio button group.
54 * @frame_title: The title of the Frame or %NULL if you don't want a title.
55 * @...: A %NULL-terminated @va_list describing the radio buttons.
56 *
57 * Convenience function to create a group of radio buttons embedded into
58 * a #GtkFrame or #GtkVBox.
59 *
60 * Returns: A #GtkFrame or #GtkVBox (depending on @in_frame).
61 **/
62 GtkWidget *
gimp_radio_group_new(gboolean in_frame,const gchar * frame_title,...)63 gimp_radio_group_new (gboolean in_frame,
64 const gchar *frame_title,
65
66 /* specify radio buttons as va_list:
67 * const gchar *label,
68 * GCallback callback,
69 * gpointer callback_data,
70 * gpointer item_data,
71 * GtkWidget **widget_ptr,
72 * gboolean active,
73 */
74
75 ...)
76 {
77 GtkWidget *vbox;
78 GtkWidget *button;
79 GSList *group;
80
81 /* radio button variables */
82 const gchar *label;
83 GCallback callback;
84 gpointer callback_data;
85 gpointer item_data;
86 GtkWidget **widget_ptr;
87 gboolean active;
88
89 va_list args;
90
91 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
92
93 group = NULL;
94
95 /* create the radio buttons */
96 va_start (args, frame_title);
97 label = va_arg (args, const gchar *);
98 while (label)
99 {
100 callback = va_arg (args, GCallback);
101 callback_data = va_arg (args, gpointer);
102 item_data = va_arg (args, gpointer);
103 widget_ptr = va_arg (args, GtkWidget **);
104 active = va_arg (args, gboolean);
105
106 if (label != (gpointer) 1)
107 button = gtk_radio_button_new_with_mnemonic (group, label);
108 else
109 button = gtk_radio_button_new (group);
110
111 group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
112 gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
113
114 if (item_data)
115 {
116 g_object_set_data (G_OBJECT (button), "gimp-item-data", item_data);
117
118 /* backward compatibility */
119 g_object_set_data (G_OBJECT (button), "user_data", item_data);
120 }
121
122 if (widget_ptr)
123 *widget_ptr = button;
124
125 if (active)
126 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
127
128 g_signal_connect (button, "toggled",
129 callback,
130 callback_data);
131
132 gtk_widget_show (button);
133
134 label = va_arg (args, const gchar *);
135 }
136 va_end (args);
137
138 if (in_frame)
139 {
140 GtkWidget *frame;
141
142 frame = gimp_frame_new (frame_title);
143 gtk_container_add (GTK_CONTAINER (frame), vbox);
144 gtk_widget_show (vbox);
145
146 return frame;
147 }
148
149 return vbox;
150 }
151
152 /**
153 * gimp_radio_group_new2:
154 * @in_frame: %TRUE if you want a #GtkFrame around the
155 * radio button group.
156 * @frame_title: The title of the Frame or %NULL if you don't want
157 * a title.
158 * @radio_button_callback: The callback each button's "toggled" signal will
159 * be connected with.
160 * @radio_button_callback_data:
161 * The data which will be passed to g_signal_connect().
162 * @initial: The @item_data of the initially pressed radio button.
163 * @...: A %NULL-terminated @va_list describing
164 * the radio buttons.
165 *
166 * Convenience function to create a group of radio buttons embedded into
167 * a #GtkFrame or #GtkVBox.
168 *
169 * Returns: A #GtkFrame or #GtkVBox (depending on @in_frame).
170 **/
171 GtkWidget *
gimp_radio_group_new2(gboolean in_frame,const gchar * frame_title,GCallback radio_button_callback,gpointer callback_data,gpointer initial,...)172 gimp_radio_group_new2 (gboolean in_frame,
173 const gchar *frame_title,
174 GCallback radio_button_callback,
175 gpointer callback_data,
176 gpointer initial, /* item_data */
177
178 /* specify radio buttons as va_list:
179 * const gchar *label,
180 * gpointer item_data,
181 * GtkWidget **widget_ptr,
182 */
183
184 ...)
185 {
186 GtkWidget *vbox;
187 GtkWidget *button;
188 GSList *group;
189
190 /* radio button variables */
191 const gchar *label;
192 gpointer item_data;
193 GtkWidget **widget_ptr;
194
195 va_list args;
196
197 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
198
199 group = NULL;
200
201 /* create the radio buttons */
202 va_start (args, initial);
203 label = va_arg (args, const gchar *);
204
205 while (label)
206 {
207 item_data = va_arg (args, gpointer);
208 widget_ptr = va_arg (args, GtkWidget **);
209
210 if (label != (gpointer) 1)
211 button = gtk_radio_button_new_with_mnemonic (group, label);
212 else
213 button = gtk_radio_button_new (group);
214
215 group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
216 gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
217
218 if (item_data)
219 {
220 g_object_set_data (G_OBJECT (button), "gimp-item-data", item_data);
221
222 /* backward compatibility */
223 g_object_set_data (G_OBJECT (button), "user_data", item_data);
224 }
225
226 if (widget_ptr)
227 *widget_ptr = button;
228
229 if (initial == item_data)
230 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
231
232 g_signal_connect (button, "toggled",
233 radio_button_callback,
234 callback_data);
235
236 gtk_widget_show (button);
237
238 label = va_arg (args, const gchar *);
239 }
240 va_end (args);
241
242 if (in_frame)
243 {
244 GtkWidget *frame;
245
246 frame = gimp_frame_new (frame_title);
247 gtk_container_add (GTK_CONTAINER (frame), vbox);
248 gtk_widget_show (vbox);
249
250 return frame;
251 }
252
253 return vbox;
254 }
255
256 /**
257 * gimp_int_radio_group_new:
258 * @in_frame: %TRUE if you want a #GtkFrame around the
259 * radio button group.
260 * @frame_title: The title of the Frame or %NULL if you don't want
261 * a title.
262 * @radio_button_callback: The callback each button's "toggled" signal will
263 * be connected with.
264 * @radio_button_callback_data:
265 * The data which will be passed to g_signal_connect().
266 * @initial: The @item_data of the initially pressed radio button.
267 * @...: A %NULL-terminated @va_list describing
268 * the radio buttons.
269 *
270 * Convenience function to create a group of radio buttons embedded into
271 * a #GtkFrame or #GtkVBox. This function does the same thing as
272 * gimp_radio_group_new2(), but it takes integers as @item_data instead of
273 * pointers, since that is a very common case (mapping an enum to a radio
274 * group).
275 *
276 * Returns: A #GtkFrame or #GtkVBox (depending on @in_frame).
277 **/
278 GtkWidget *
gimp_int_radio_group_new(gboolean in_frame,const gchar * frame_title,GCallback radio_button_callback,gpointer callback_data,gint initial,...)279 gimp_int_radio_group_new (gboolean in_frame,
280 const gchar *frame_title,
281 GCallback radio_button_callback,
282 gpointer callback_data,
283 gint initial, /* item_data */
284
285 /* specify radio buttons as va_list:
286 * const gchar *label,
287 * gint item_data,
288 * GtkWidget **widget_ptr,
289 */
290
291 ...)
292 {
293 GtkWidget *vbox;
294 GtkWidget *button;
295 GSList *group;
296
297 /* radio button variables */
298 const gchar *label;
299 gint item_data;
300 gpointer item_ptr;
301 GtkWidget **widget_ptr;
302
303 va_list args;
304
305 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
306
307 group = NULL;
308
309 /* create the radio buttons */
310 va_start (args, initial);
311 label = va_arg (args, const gchar *);
312
313 while (label)
314 {
315 item_data = va_arg (args, gint);
316 widget_ptr = va_arg (args, GtkWidget **);
317
318 item_ptr = GINT_TO_POINTER (item_data);
319
320 if (label != GINT_TO_POINTER (1))
321 button = gtk_radio_button_new_with_mnemonic (group, label);
322 else
323 button = gtk_radio_button_new (group);
324
325 group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
326 gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
327
328 if (item_data)
329 {
330 g_object_set_data (G_OBJECT (button), "gimp-item-data", item_ptr);
331
332 /* backward compatibility */
333 g_object_set_data (G_OBJECT (button), "user_data", item_ptr);
334 }
335
336 if (widget_ptr)
337 *widget_ptr = button;
338
339 if (initial == item_data)
340 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
341
342 g_signal_connect (button, "toggled",
343 radio_button_callback,
344 callback_data);
345
346 gtk_widget_show (button);
347
348 label = va_arg (args, const gchar *);
349 }
350 va_end (args);
351
352 if (in_frame)
353 {
354 GtkWidget *frame;
355
356 frame = gimp_frame_new (frame_title);
357 gtk_container_add (GTK_CONTAINER (frame), vbox);
358 gtk_widget_show (vbox);
359
360 return frame;
361 }
362
363 return vbox;
364 }
365
366 /**
367 * gimp_radio_group_set_active:
368 * @radio_button: Pointer to a #GtkRadioButton.
369 * @item_data: The @item_data of the radio button you want to select.
370 *
371 * Calls gtk_toggle_button_set_active() with the radio button that was
372 * created with a matching @item_data.
373 **/
374 void
gimp_radio_group_set_active(GtkRadioButton * radio_button,gpointer item_data)375 gimp_radio_group_set_active (GtkRadioButton *radio_button,
376 gpointer item_data)
377 {
378 GtkWidget *button;
379 GSList *group;
380
381 g_return_if_fail (GTK_IS_RADIO_BUTTON (radio_button));
382
383 for (group = gtk_radio_button_get_group (radio_button);
384 group;
385 group = g_slist_next (group))
386 {
387 button = GTK_WIDGET (group->data);
388
389 if (g_object_get_data (G_OBJECT (button), "gimp-item-data") == item_data)
390 {
391 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
392 return;
393 }
394 }
395 }
396
397 /**
398 * gimp_int_radio_group_set_active:
399 * @radio_button: Pointer to a #GtkRadioButton.
400 * @item_data: The @item_data of the radio button you want to select.
401 *
402 * Calls gtk_toggle_button_set_active() with the radio button that was created
403 * with a matching @item_data. This function does the same thing as
404 * gimp_radio_group_set_active(), but takes integers as @item_data instead
405 * of pointers.
406 **/
407 void
gimp_int_radio_group_set_active(GtkRadioButton * radio_button,gint item_data)408 gimp_int_radio_group_set_active (GtkRadioButton *radio_button,
409 gint item_data)
410 {
411 g_return_if_fail (GTK_IS_RADIO_BUTTON (radio_button));
412
413 gimp_radio_group_set_active (radio_button, GINT_TO_POINTER (item_data));
414 }
415
416 /**
417 * gimp_spin_button_new:
418 * @adjustment: Returns the spinbutton's #GtkAdjustment.
419 * @value: The initial value of the spinbutton.
420 * @lower: The lower boundary.
421 * @upper: The upper boundary.
422 * @step_increment: The spinbutton's step increment.
423 * @page_increment: The spinbutton's page increment (mouse button 2).
424 * @page_size: Ignored, spin buttons must always have a zero page size.
425 * @climb_rate: The spinbutton's climb rate.
426 * @digits: The spinbutton's number of decimal digits.
427 *
428 * This function is a shortcut for gtk_adjustment_new() and a
429 * subsequent gtk_spin_button_new(). It also calls
430 * gtk_spin_button_set_numeric() so that non-numeric text cannot be
431 * entered.
432 *
433 * Deprecated: 2.10: Use gtk_spin_button_new() instead.
434 *
435 * Returns: A #GtkSpinButton and its #GtkAdjustment.
436 **/
437 GtkWidget *
gimp_spin_button_new(GtkObject ** adjustment,gdouble value,gdouble lower,gdouble upper,gdouble step_increment,gdouble page_increment,gdouble page_size,gdouble climb_rate,guint digits)438 gimp_spin_button_new (GtkObject **adjustment, /* return value */
439 gdouble value,
440 gdouble lower,
441 gdouble upper,
442 gdouble step_increment,
443 gdouble page_increment,
444 gdouble page_size,
445 gdouble climb_rate,
446 guint digits)
447 {
448 GtkWidget *spinbutton;
449
450 *adjustment = gtk_adjustment_new (value, lower, upper,
451 step_increment, page_increment, 0);
452
453 spinbutton = gimp_spin_button_new (GTK_ADJUSTMENT (*adjustment),
454 climb_rate, digits);
455
456 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
457
458 return spinbutton;
459 }
460
461 static void
gimp_random_seed_update(GtkWidget * widget,gpointer data)462 gimp_random_seed_update (GtkWidget *widget,
463 gpointer data)
464 {
465 GtkWidget *spinbutton = data;
466
467 /* Generate a new seed if the "New Seed" button was clicked or
468 * of the "Randomize" toggle is activated
469 */
470 if (! GTK_IS_TOGGLE_BUTTON (widget) ||
471 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
472 {
473 gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinbutton),
474 (guint) g_random_int ());
475 }
476 }
477
478 /**
479 * gimp_random_seed_new:
480 * @seed: A pointer to the variable which stores the random seed.
481 * @random_seed: A pointer to a boolean indicating whether seed should be
482 * initialised randomly or not.
483 *
484 * Creates a widget that allows the user to control how the random number
485 * generator is initialized.
486 *
487 * Returns: A #GtkHBox containing a #GtkSpinButton for the seed and
488 * a #GtkButton for setting a random seed.
489 **/
490 GtkWidget *
gimp_random_seed_new(guint * seed,gboolean * random_seed)491 gimp_random_seed_new (guint *seed,
492 gboolean *random_seed)
493 {
494 GtkWidget *hbox;
495 GtkWidget *toggle;
496 GtkWidget *spinbutton;
497 GtkAdjustment *adj;
498 GtkWidget *button;
499
500 g_return_val_if_fail (seed != NULL, NULL);
501 g_return_val_if_fail (random_seed != NULL, NULL);
502
503 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
504
505 /* If we're being asked to generate a random seed, generate one. */
506 if (*random_seed)
507 *seed = g_random_int ();
508
509 adj = (GtkAdjustment *)
510 gtk_adjustment_new (*seed, 0, (guint32) -1, 1, 10, 0);
511 spinbutton = gimp_spin_button_new (adj, 1.0, 0);
512 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
513 gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
514 gtk_widget_show (spinbutton);
515
516 g_signal_connect (adj, "value-changed",
517 G_CALLBACK (gimp_uint_adjustment_update),
518 seed);
519
520 gimp_help_set_help_data (spinbutton,
521 _("Use this value for random number generator "
522 "seed - this allows you to repeat a "
523 "given \"random\" operation"), NULL);
524
525 button = gtk_button_new_with_mnemonic (_("_New Seed"));
526 gtk_misc_set_padding (GTK_MISC (gtk_bin_get_child (GTK_BIN (button))), 2, 0);
527 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
528 gtk_widget_show (button);
529
530 /* Send spinbutton as data so that we can change the value in
531 * gimp_random_seed_update()
532 */
533 g_signal_connect (button, "clicked",
534 G_CALLBACK (gimp_random_seed_update),
535 spinbutton);
536
537 gimp_help_set_help_data (button,
538 _("Seed random number generator with a generated "
539 "random number"),
540 NULL);
541
542 toggle = gtk_check_button_new_with_mnemonic (_("_Randomize"));
543 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), *random_seed);
544 gtk_box_pack_start (GTK_BOX (hbox), toggle, FALSE, FALSE, 0);
545 gtk_widget_show (toggle);
546
547 g_signal_connect (toggle, "toggled",
548 G_CALLBACK (gimp_toggle_button_update),
549 random_seed);
550
551 /* Need to create a new seed when the "Randomize" toggle is activated */
552 g_signal_connect (toggle, "toggled",
553 G_CALLBACK (gimp_random_seed_update),
554 spinbutton);
555
556 g_object_set_data (G_OBJECT (hbox), "spinbutton", spinbutton);
557 g_object_set_data (G_OBJECT (hbox), "button", button);
558 g_object_set_data (G_OBJECT (hbox), "toggle", toggle);
559
560 g_object_bind_property (toggle, "active",
561 spinbutton, "sensitive",
562 G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
563 g_object_bind_property (toggle, "active",
564 button, "sensitive",
565 G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
566
567 return hbox;
568 }
569
570 typedef struct
571 {
572 GimpChainButton *chainbutton;
573 gboolean chain_constrains_ratio;
574 gdouble orig_x;
575 gdouble orig_y;
576 gdouble last_x;
577 gdouble last_y;
578 } GimpCoordinatesData;
579
580 static void
gimp_coordinates_callback(GtkWidget * widget,GimpCoordinatesData * data)581 gimp_coordinates_callback (GtkWidget *widget,
582 GimpCoordinatesData *data)
583 {
584 gdouble new_x;
585 gdouble new_y;
586
587 new_x = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 0);
588 new_y = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 1);
589
590 if (gimp_chain_button_get_active (data->chainbutton))
591 {
592 if (data->chain_constrains_ratio)
593 {
594 if ((data->orig_x != 0) && (data->orig_y != 0))
595 {
596 g_signal_handlers_block_by_func (widget,
597 gimp_coordinates_callback,
598 data);
599
600 if (ROUND (new_x) != ROUND (data->last_x))
601 {
602 data->last_x = new_x;
603 new_y = (new_x * data->orig_y) / data->orig_x;
604
605 gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (widget), 1,
606 new_y);
607 data->last_y
608 = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 1);
609 }
610 else if (ROUND (new_y) != ROUND (data->last_y))
611 {
612 data->last_y = new_y;
613 new_x = (new_y * data->orig_x) / data->orig_y;
614
615 gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (widget), 0,
616 new_x);
617 data->last_x
618 = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 0);
619 }
620
621 g_signal_handlers_unblock_by_func (widget,
622 gimp_coordinates_callback,
623 data);
624 }
625 }
626 else
627 {
628 if (new_x != data->last_x)
629 {
630 gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (widget), 1, new_x);
631 data->last_y = data->last_x
632 = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 1);
633 }
634 else if (new_y != data->last_y)
635 {
636 gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (widget), 0, new_y);
637 data->last_x = data->last_y
638 = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 0);
639 }
640 }
641 }
642 else
643 {
644 if (new_x != data->last_x)
645 data->last_x = new_x;
646 if (new_y != data->last_y)
647 data->last_y = new_y;
648 }
649 }
650
651 static void
gimp_coordinates_data_free(GimpCoordinatesData * data)652 gimp_coordinates_data_free (GimpCoordinatesData *data)
653 {
654 g_slice_free (GimpCoordinatesData, data);
655 }
656
657 static void
gimp_coordinates_chainbutton_toggled(GimpChainButton * button,GimpSizeEntry * entry)658 gimp_coordinates_chainbutton_toggled (GimpChainButton *button,
659 GimpSizeEntry *entry)
660 {
661 if (gimp_chain_button_get_active (button))
662 {
663 GimpCoordinatesData *data;
664
665 data = g_object_get_data (G_OBJECT (entry), "coordinates-data");
666
667 data->orig_x = gimp_size_entry_get_refval (entry, 0);
668 data->orig_y = gimp_size_entry_get_refval (entry, 1);
669 }
670 }
671
672 /**
673 * gimp_coordinates_new:
674 * @unit: The initial unit of the #GimpUnitMenu.
675 * @unit_format: A printf-like unit-format string as is used with
676 * gimp_unit_menu_new().
677 * @menu_show_pixels: %TRUE if the #GimpUnitMenu should contain an item
678 * for GIMP_UNIT_PIXEL.
679 * @menu_show_percent: %TRUE if the #GimpUnitMenu should contain an item
680 * for GIMP_UNIT_PERCENT.
681 * @spinbutton_width: The horizontal size of the #GimpSizeEntry's
682 * #GtkSpinButton's.
683 * @update_policy: The update policy for the #GimpSizeEntry.
684 * @chainbutton_active: %TRUE if the attached #GimpChainButton should be
685 * active.
686 * @chain_constrains_ratio: %TRUE if the chainbutton should constrain the
687 * fields' aspect ratio. If %FALSE, the values will
688 * be constrained.
689 * @xlabel: The label for the X coordinate.
690 * @x: The initial value of the X coordinate.
691 * @xres: The horizontal resolution in DPI.
692 * @lower_boundary_x: The lower boundary of the X coordinate.
693 * @upper_boundary_x: The upper boundary of the X coordinate.
694 * @xsize_0: The X value which will be treated as 0%.
695 * @xsize_100: The X value which will be treated as 100%.
696 * @ylabel: The label for the Y coordinate.
697 * @y: The initial value of the Y coordinate.
698 * @yres: The vertical resolution in DPI.
699 * @lower_boundary_y: The lower boundary of the Y coordinate.
700 * @upper_boundary_y: The upper boundary of the Y coordinate.
701 * @ysize_0: The Y value which will be treated as 0%.
702 * @ysize_100: The Y value which will be treated as 100%.
703 *
704 * Convenience function that creates a #GimpSizeEntry with two fields for x/y
705 * coordinates/sizes with a #GimpChainButton attached to constrain either the
706 * two fields' values or the ratio between them.
707 *
708 * Returns: The new #GimpSizeEntry.
709 **/
710 GtkWidget *
gimp_coordinates_new(GimpUnit unit,const gchar * unit_format,gboolean menu_show_pixels,gboolean menu_show_percent,gint spinbutton_width,GimpSizeEntryUpdatePolicy update_policy,gboolean chainbutton_active,gboolean chain_constrains_ratio,const gchar * xlabel,gdouble x,gdouble xres,gdouble lower_boundary_x,gdouble upper_boundary_x,gdouble xsize_0,gdouble xsize_100,const gchar * ylabel,gdouble y,gdouble yres,gdouble lower_boundary_y,gdouble upper_boundary_y,gdouble ysize_0,gdouble ysize_100)711 gimp_coordinates_new (GimpUnit unit,
712 const gchar *unit_format,
713 gboolean menu_show_pixels,
714 gboolean menu_show_percent,
715 gint spinbutton_width,
716 GimpSizeEntryUpdatePolicy update_policy,
717
718 gboolean chainbutton_active,
719 gboolean chain_constrains_ratio,
720
721 const gchar *xlabel,
722 gdouble x,
723 gdouble xres,
724 gdouble lower_boundary_x,
725 gdouble upper_boundary_x,
726 gdouble xsize_0, /* % */
727 gdouble xsize_100, /* % */
728
729 const gchar *ylabel,
730 gdouble y,
731 gdouble yres,
732 gdouble lower_boundary_y,
733 gdouble upper_boundary_y,
734 gdouble ysize_0, /* % */
735 gdouble ysize_100 /* % */)
736 {
737 GimpCoordinatesData *data;
738 GtkAdjustment *adjustment;
739 GtkWidget *spinbutton;
740 GtkWidget *sizeentry;
741 GtkWidget *chainbutton;
742
743 adjustment = (GtkAdjustment *) gtk_adjustment_new (1, 0, 1, 1, 10, 0);
744 spinbutton = gimp_spin_button_new (adjustment, 1.0, 2);
745 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
746
747 if (spinbutton_width > 0)
748 {
749 if (spinbutton_width < 17)
750 gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), spinbutton_width);
751 else
752 gtk_widget_set_size_request (spinbutton, spinbutton_width, -1);
753 }
754
755 sizeentry = gimp_size_entry_new (1, unit, unit_format,
756 menu_show_pixels,
757 menu_show_percent,
758 FALSE,
759 spinbutton_width,
760 update_policy);
761 gtk_table_set_col_spacing (GTK_TABLE (sizeentry), 0, 4);
762 gtk_table_set_col_spacing (GTK_TABLE (sizeentry), 2, 4);
763 gimp_size_entry_add_field (GIMP_SIZE_ENTRY (sizeentry),
764 GTK_SPIN_BUTTON (spinbutton), NULL);
765 gtk_table_attach_defaults (GTK_TABLE (sizeentry), spinbutton, 1, 2, 0, 1);
766 gtk_widget_show (spinbutton);
767
768 gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (sizeentry),
769 (update_policy == GIMP_SIZE_ENTRY_UPDATE_RESOLUTION) ||
770 (menu_show_pixels == FALSE) ?
771 GIMP_UNIT_INCH : GIMP_UNIT_PIXEL);
772
773 gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (sizeentry), 0, xres, TRUE);
774 gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (sizeentry), 1, yres, TRUE);
775 gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (sizeentry), 0,
776 lower_boundary_x, upper_boundary_x);
777 gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (sizeentry), 1,
778 lower_boundary_y, upper_boundary_y);
779
780 if (menu_show_percent)
781 {
782 gimp_size_entry_set_size (GIMP_SIZE_ENTRY (sizeentry), 0,
783 xsize_0, xsize_100);
784 gimp_size_entry_set_size (GIMP_SIZE_ENTRY (sizeentry), 1,
785 ysize_0, ysize_100);
786 }
787
788 gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (sizeentry), 0, x);
789 gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (sizeentry), 1, y);
790
791 gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (sizeentry),
792 xlabel, 0, 0, 0.0);
793 gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (sizeentry),
794 ylabel, 1, 0, 0.0);
795
796 chainbutton = gimp_chain_button_new (GIMP_CHAIN_RIGHT);
797
798 if (chainbutton_active)
799 gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chainbutton), TRUE);
800
801 gtk_table_attach (GTK_TABLE (sizeentry), chainbutton, 2, 3, 0, 2,
802 GTK_SHRINK | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
803 gtk_widget_show (chainbutton);
804
805 data = g_slice_new (GimpCoordinatesData);
806
807 data->chainbutton = GIMP_CHAIN_BUTTON (chainbutton);
808 data->chain_constrains_ratio = chain_constrains_ratio;
809 data->orig_x = x;
810 data->orig_y = y;
811 data->last_x = x;
812 data->last_y = y;
813
814 g_object_set_data_full (G_OBJECT (sizeentry), "coordinates-data",
815 data,
816 (GDestroyNotify) gimp_coordinates_data_free);
817
818 g_signal_connect (sizeentry, "value-changed",
819 G_CALLBACK (gimp_coordinates_callback),
820 data);
821
822 g_object_set_data (G_OBJECT (sizeentry), "chainbutton", chainbutton);
823
824 g_signal_connect (chainbutton, "toggled",
825 G_CALLBACK (gimp_coordinates_chainbutton_toggled),
826 sizeentry);
827
828 return sizeentry;
829 }
830
831
832 /*
833 * Standard Callbacks
834 */
835
836 /**
837 * gimp_toggle_button_sensitive_update:
838 * @toggle_button: The #GtkToggleButton the "set_sensitive" and
839 * "inverse_sensitive" lists are attached to.
840 *
841 * If you attached a pointer to a #GtkWidget with g_object_set_data() and
842 * the "set_sensitive" key to the #GtkToggleButton, the sensitive state of
843 * the attached widget will be set according to the toggle button's
844 * "active" state.
845 *
846 * You can attach an arbitrary list of widgets by attaching another
847 * "set_sensitive" data pointer to the first widget (and so on...).
848 *
849 * This function can also set the sensitive state according to the toggle
850 * button's inverse "active" state by attaching widgets with the
851 * "inverse_sensitive" key.
852 *
853 * Deprecated: use g_object_bind_property() instead of using the
854 * "set_sensitive" and "inverse_sensitive" data pointers.
855 **/
856 void
gimp_toggle_button_sensitive_update(GtkToggleButton * toggle_button)857 gimp_toggle_button_sensitive_update (GtkToggleButton *toggle_button)
858 {
859 GtkWidget *set_sensitive;
860 gboolean active;
861
862 active = gtk_toggle_button_get_active (toggle_button);
863
864 set_sensitive =
865 g_object_get_data (G_OBJECT (toggle_button), "set_sensitive");
866 while (set_sensitive)
867 {
868 gtk_widget_set_sensitive (set_sensitive, active);
869 set_sensitive =
870 g_object_get_data (G_OBJECT (set_sensitive), "set_sensitive");
871 }
872
873 set_sensitive =
874 g_object_get_data (G_OBJECT (toggle_button), "inverse_sensitive");
875 while (set_sensitive)
876 {
877 gtk_widget_set_sensitive (set_sensitive, ! active);
878 set_sensitive =
879 g_object_get_data (G_OBJECT (set_sensitive), "inverse_sensitive");
880 }
881 }
882
883 /**
884 * gimp_toggle_button_update:
885 * @widget: A #GtkToggleButton.
886 * @data: A pointer to a #gint variable which will store the value of
887 * gtk_toggle_button_get_active().
888 *
889 * Note that this function calls gimp_toggle_button_sensitive_update()
890 * which is a deprecated hack you shouldn't use. See that function's
891 * documentation for a proper replacement of its functionality.
892 **/
893 void
gimp_toggle_button_update(GtkWidget * widget,gpointer data)894 gimp_toggle_button_update (GtkWidget *widget,
895 gpointer data)
896 {
897 gint *toggle_val = (gint *) data;
898
899 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
900 *toggle_val = TRUE;
901 else
902 *toggle_val = FALSE;
903
904 gimp_toggle_button_sensitive_update (GTK_TOGGLE_BUTTON (widget));
905 }
906
907 /**
908 * gimp_radio_button_update:
909 * @widget: A #GtkRadioButton.
910 * @data: A pointer to a #gint variable which will store the value of
911 * GPOINTER_TO_INT (g_object_get_data (@widget, "gimp-item-data")).
912 *
913 * Note that this function calls gimp_toggle_button_sensitive_update()
914 * which is a deprecated hack you shouldn't use. See that function's
915 * documentation for a proper replacement of its functionality.
916 **/
917 void
gimp_radio_button_update(GtkWidget * widget,gpointer data)918 gimp_radio_button_update (GtkWidget *widget,
919 gpointer data)
920 {
921 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
922 {
923 gint *toggle_val = (gint *) data;
924
925 *toggle_val = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
926 "gimp-item-data"));
927 }
928
929 gimp_toggle_button_sensitive_update (GTK_TOGGLE_BUTTON (widget));
930 }
931
932 /**
933 * gimp_int_adjustment_update:
934 * @adjustment: A #GtkAdjustment.
935 * @data: A pointer to a #gint variable which will store the
936 * @adjustment's value.
937 *
938 * Note that the #GtkAdjustment's value (which is a #gdouble) will be
939 * rounded with RINT().
940 **/
941 void
gimp_int_adjustment_update(GtkAdjustment * adjustment,gpointer data)942 gimp_int_adjustment_update (GtkAdjustment *adjustment,
943 gpointer data)
944 {
945 gint *val = (gint *) data;
946
947 *val = RINT (gtk_adjustment_get_value (adjustment));
948 }
949
950 /**
951 * gimp_uint_adjustment_update:
952 * @adjustment: A #GtkAdjustment.
953 * @data: A pointer to a #guint variable which will store the
954 * @adjustment's value.
955 *
956 * Note that the #GtkAdjustment's value (which is a #gdouble) will be rounded
957 * with (#guint) (value + 0.5).
958 **/
959 void
gimp_uint_adjustment_update(GtkAdjustment * adjustment,gpointer data)960 gimp_uint_adjustment_update (GtkAdjustment *adjustment,
961 gpointer data)
962 {
963 guint *val = (guint *) data;
964
965 *val = (guint) (gtk_adjustment_get_value (adjustment) + 0.5);
966 }
967
968 /**
969 * gimp_float_adjustment_update:
970 * @adjustment: A #GtkAdjustment.
971 * @data: A pointer to a #gfloat variable which will store the
972 * @adjustment's value.
973 **/
974 void
gimp_float_adjustment_update(GtkAdjustment * adjustment,gpointer data)975 gimp_float_adjustment_update (GtkAdjustment *adjustment,
976 gpointer data)
977 {
978 gfloat *val = (gfloat *) data;
979
980 *val = gtk_adjustment_get_value (adjustment);
981
982 }
983
984 /**
985 * gimp_double_adjustment_update:
986 * @adjustment: A #GtkAdjustment.
987 * @data: A pointer to a #gdouble variable which will store the
988 * @adjustment's value.
989 **/
990 void
gimp_double_adjustment_update(GtkAdjustment * adjustment,gpointer data)991 gimp_double_adjustment_update (GtkAdjustment *adjustment,
992 gpointer data)
993 {
994 gdouble *val = (gdouble *) data;
995
996 *val = gtk_adjustment_get_value (adjustment);
997 }
998