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