1 /* LIBGIMP - The GIMP Library
2  * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
3  *
4  * gimpsizeentry.c
5  * Copyright (C) 1999-2000 Sven Neumann <sven@gimp.org>
6  *                         Michael Natterer <mitch@gimp.org>
7  *
8  * This library is free software: you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 3 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library.  If not, see
20  * <https://www.gnu.org/licenses/>.
21  */
22 
23 #include "config.h"
24 
25 #include <string.h>
26 
27 #include <gegl.h>
28 #include <gtk/gtk.h>
29 
30 #include "libgimpbase/gimpbase.h"
31 
32 #include "gimpwidgets.h"
33 
34 #include "gimpeevl.h"
35 #include "gimpsizeentry.h"
36 
37 
38 /**
39  * SECTION: gimpsizeentry
40  * @title: GimpSizeEntry
41  * @short_description: Widget for entering pixel values and resolutions.
42  * @see_also: #GimpUnit, #GimpUnitComboBox, gimp_coordinates_new()
43  *
44  * This widget is used to enter pixel distances/sizes and resolutions.
45  *
46  * You can specify the number of fields the widget should provide. For
47  * each field automatic mappings are performed between the field's
48  * "reference value" and its "value".
49  *
50  * There is a #GimpUnitComboBox right of the entry fields which lets
51  * you specify the #GimpUnit of the displayed values.
52  *
53  * For each field, there can be one or two #GtkSpinButton's to enter
54  * "value" and "reference value". If you specify @show_refval as
55  * #FALSE in gimp_size_entry_new() there will be only one
56  * #GtkSpinButton and the #GimpUnitComboBox will contain an item for
57  * selecting GIMP_UNIT_PIXEL.
58  *
59  * The "reference value" is either of GIMP_UNIT_PIXEL or dpi,
60  * depending on which #GimpSizeEntryUpdatePolicy you specify in
61  * gimp_size_entry_new().  The "value" is either the size in pixels
62  * mapped to the size in a real-world-unit (see #GimpUnit) or the dpi
63  * value mapped to pixels per real-world-unit.
64  **/
65 
66 
67 #define SIZE_MAX_VALUE 500000.0
68 
69 #define GIMP_SIZE_ENTRY_DIGITS(unit) (MIN (gimp_unit_get_digits (unit), 5) + 1)
70 
71 
72 enum
73 {
74   VALUE_CHANGED,
75   REFVAL_CHANGED,
76   UNIT_CHANGED,
77   LAST_SIGNAL
78 };
79 
80 
81 struct _GimpSizeEntryField
82 {
83   GimpSizeEntry *gse;
84 
85   gdouble        resolution;
86   gdouble        lower;
87   gdouble        upper;
88 
89   GtkAdjustment *value_adjustment;
90   GtkWidget     *value_spinbutton;
91   gdouble        value;
92   gdouble        min_value;
93   gdouble        max_value;
94 
95   GtkAdjustment *refval_adjustment;
96   GtkWidget     *refval_spinbutton;
97   gdouble        refval;
98   gdouble        min_refval;
99   gdouble        max_refval;
100   gint           refval_digits;
101 
102   gint           stop_recursion;
103 };
104 
105 
106 static void      gimp_size_entry_finalize            (GObject            *object);
107 static void      gimp_size_entry_update_value        (GimpSizeEntryField *gsef,
108                                                       gdouble             value);
109 static void      gimp_size_entry_value_callback      (GtkWidget          *widget,
110                                                       gpointer            data);
111 static void      gimp_size_entry_update_refval       (GimpSizeEntryField *gsef,
112                                                       gdouble             refval);
113 static void      gimp_size_entry_refval_callback     (GtkWidget          *widget,
114                                                       gpointer            data);
115 static void      gimp_size_entry_update_unit         (GimpSizeEntry      *gse,
116                                                       GimpUnit            unit);
117 static void      gimp_size_entry_unit_callback       (GtkWidget          *widget,
118                                                       GimpSizeEntry      *sizeentry);
119 static void      gimp_size_entry_attach_eevl         (GtkSpinButton      *spin_button,
120                                                       GimpSizeEntryField *gsef);
121 static gint      gimp_size_entry_eevl_input_callback (GtkSpinButton      *spinner,
122                                                       gdouble            *return_val,
123                                                       gpointer           *data);
124 static gboolean  gimp_size_entry_eevl_unit_resolver  (const gchar        *ident,
125                                                       GimpEevlQuantity   *factor,
126                                                       gdouble            *offset,
127                                                       gpointer            data);
128 
129 
130 G_DEFINE_TYPE (GimpSizeEntry, gimp_size_entry, GTK_TYPE_TABLE)
131 
132 #define parent_class gimp_size_entry_parent_class
133 
134 static guint gimp_size_entry_signals[LAST_SIGNAL] = { 0 };
135 
136 
137 static void
gimp_size_entry_class_init(GimpSizeEntryClass * klass)138 gimp_size_entry_class_init (GimpSizeEntryClass *klass)
139 {
140   GObjectClass *object_class = G_OBJECT_CLASS (klass);
141 
142   gimp_size_entry_signals[VALUE_CHANGED] =
143     g_signal_new ("value-changed",
144                   G_TYPE_FROM_CLASS (klass),
145                   G_SIGNAL_RUN_FIRST,
146                   G_STRUCT_OFFSET (GimpSizeEntryClass, value_changed),
147                   NULL, NULL,
148                   g_cclosure_marshal_VOID__VOID,
149                   G_TYPE_NONE, 0);
150 
151   gimp_size_entry_signals[REFVAL_CHANGED] =
152     g_signal_new ("refval-changed",
153                   G_TYPE_FROM_CLASS (klass),
154                   G_SIGNAL_RUN_FIRST,
155                   G_STRUCT_OFFSET (GimpSizeEntryClass, refval_changed),
156                   NULL, NULL,
157                   g_cclosure_marshal_VOID__VOID,
158                   G_TYPE_NONE, 0);
159 
160   gimp_size_entry_signals[UNIT_CHANGED] =
161     g_signal_new ("unit-changed",
162                   G_TYPE_FROM_CLASS (klass),
163                   G_SIGNAL_RUN_FIRST,
164                   G_STRUCT_OFFSET (GimpSizeEntryClass, unit_changed),
165                   NULL, NULL,
166                   g_cclosure_marshal_VOID__VOID,
167                   G_TYPE_NONE, 0);
168 
169   object_class->finalize = gimp_size_entry_finalize;
170 
171   klass->value_changed   = NULL;
172   klass->refval_changed  = NULL;
173   klass->unit_changed    = NULL;
174 }
175 
176 static void
gimp_size_entry_init(GimpSizeEntry * gse)177 gimp_size_entry_init (GimpSizeEntry *gse)
178 {
179   gse->fields            = NULL;
180   gse->number_of_fields  = 0;
181   gse->unitmenu          = NULL;
182   gse->unit              = GIMP_UNIT_PIXEL;
183   gse->menu_show_pixels  = TRUE;
184   gse->menu_show_percent = TRUE;
185   gse->show_refval       = FALSE;
186   gse->update_policy     = GIMP_SIZE_ENTRY_UPDATE_NONE;
187 }
188 
189 static void
gimp_size_entry_finalize(GObject * object)190 gimp_size_entry_finalize (GObject *object)
191 {
192   GimpSizeEntry *gse = GIMP_SIZE_ENTRY (object);
193 
194   if (gse->fields)
195     {
196       GSList *list;
197 
198       for (list = gse->fields; list; list = list->next)
199         g_slice_free (GimpSizeEntryField, list->data);
200 
201       g_slist_free (gse->fields);
202       gse->fields = NULL;
203     }
204 
205   G_OBJECT_CLASS (parent_class)->finalize (object);
206 }
207 
208 /**
209  * gimp_size_entry_new:
210  * @number_of_fields:  The number of input fields.
211  * @unit:              The initial unit.
212  * @unit_format:       A printf-like unit-format string as is used with
213  *                     gimp_unit_menu_new().
214  * @menu_show_pixels:  %TRUE if the unit menu should contain an item for
215  *                     GIMP_UNIT_PIXEL (ignored if the @update_policy is not
216  *                     GIMP_SIZE_ENTRY_UPDATE_NONE).
217  * @menu_show_percent: %TRUE if the unit menu should contain an item for
218  *                     GIMP_UNIT_PERCENT.
219  * @show_refval:       %TRUE if you want an extra "reference value"
220  *                     spinbutton per input field.
221  * @spinbutton_width:  The minimal horizontal size of the #GtkSpinButton's.
222  * @update_policy:     How the automatic pixel <-> real-world-unit
223  *                     calculations should be done.
224  *
225  * Creates a new #GimpSizeEntry widget.
226  *
227  * To have all automatic calculations performed correctly, set up the
228  * widget in the following order:
229  *
230  * 1. gimp_size_entry_new()
231  *
232  * 2. (for each additional input field) gimp_size_entry_add_field()
233  *
234  * 3. gimp_size_entry_set_unit()
235  *
236  * For each input field:
237  *
238  * 4. gimp_size_entry_set_resolution()
239  *
240  * 5. gimp_size_entry_set_refval_boundaries()
241  *    (or gimp_size_entry_set_value_boundaries())
242  *
243  * 6. gimp_size_entry_set_size()
244  *
245  * 7. gimp_size_entry_set_refval() (or gimp_size_entry_set_value())
246  *
247  * The #GimpSizeEntry is derived from #GtkTable and will have
248  * an empty border of one cell width on each side plus an empty column left
249  * of the #GimpUnitComboBox to allow the caller to add labels or a
250  * #GimpChainButton.
251  *
252  * Returns: A Pointer to the new #GimpSizeEntry widget.
253  **/
254 GtkWidget *
gimp_size_entry_new(gint number_of_fields,GimpUnit unit,const gchar * unit_format,gboolean menu_show_pixels,gboolean menu_show_percent,gboolean show_refval,gint spinbutton_width,GimpSizeEntryUpdatePolicy update_policy)255 gimp_size_entry_new (gint                       number_of_fields,
256                      GimpUnit                   unit,
257                      const gchar               *unit_format,
258                      gboolean                   menu_show_pixels,
259                      gboolean                   menu_show_percent,
260                      gboolean                   show_refval,
261                      gint                       spinbutton_width,
262                      GimpSizeEntryUpdatePolicy  update_policy)
263 {
264   GimpSizeEntry *gse;
265   GimpUnitStore *store;
266   gint           i;
267 
268   g_return_val_if_fail ((number_of_fields >= 0) && (number_of_fields <= 16),
269                         NULL);
270 
271   gse = g_object_new (GIMP_TYPE_SIZE_ENTRY, NULL);
272 
273   gse->number_of_fields = number_of_fields;
274   gse->unit             = unit;
275   gse->show_refval      = show_refval;
276   gse->update_policy    = update_policy;
277 
278   gtk_table_resize (GTK_TABLE (gse),
279                     1 + gse->show_refval + 2,
280                     number_of_fields + 1 + 3);
281 
282   /*  show the 'pixels' menu entry only if we are a 'size' sizeentry and
283    *  don't have the reference value spinbutton
284    */
285   if ((update_policy == GIMP_SIZE_ENTRY_UPDATE_RESOLUTION) ||
286       (show_refval == TRUE))
287     gse->menu_show_pixels = FALSE;
288   else
289     gse->menu_show_pixels = menu_show_pixels;
290 
291   /*  show the 'percent' menu entry only if we are a 'size' sizeentry
292    */
293   if (update_policy == GIMP_SIZE_ENTRY_UPDATE_RESOLUTION)
294     gse->menu_show_percent = FALSE;
295   else
296     gse->menu_show_percent = menu_show_percent;
297 
298   for (i = 0; i < number_of_fields; i++)
299     {
300       GimpSizeEntryField *gsef = g_slice_new0 (GimpSizeEntryField);
301       gint                digits;
302 
303       gse->fields = g_slist_append (gse->fields, gsef);
304 
305       gsef->gse               = gse;
306       gsef->resolution        = 1.0; /*  just to avoid division by zero  */
307       gsef->lower             = 0.0;
308       gsef->upper             = 100.0;
309       gsef->value             = 0;
310       gsef->min_value         = 0;
311       gsef->max_value         = SIZE_MAX_VALUE;
312       gsef->refval_adjustment = NULL;
313       gsef->value_adjustment  = NULL;
314       gsef->refval            = 0;
315       gsef->min_refval        = 0;
316       gsef->max_refval        = SIZE_MAX_VALUE;
317       gsef->refval_digits     =
318         (update_policy == GIMP_SIZE_ENTRY_UPDATE_SIZE) ? 0 : 3;
319       gsef->stop_recursion    = 0;
320 
321       digits = ((unit == GIMP_UNIT_PIXEL) ?
322                 gsef->refval_digits : ((unit == GIMP_UNIT_PERCENT) ?
323                                        2 : GIMP_SIZE_ENTRY_DIGITS (unit)));
324 
325       gsef->value_adjustment = (GtkAdjustment *)
326         gtk_adjustment_new (gsef->value,
327                             gsef->min_value, gsef->max_value,
328                             1.0, 10.0, 0.0);
329       gsef->value_spinbutton = gimp_spin_button_new (gsef->value_adjustment,
330                                                      1.0, digits);
331       gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (gsef->value_spinbutton),
332                                    TRUE);
333 
334       gimp_size_entry_attach_eevl (GTK_SPIN_BUTTON (gsef->value_spinbutton),
335                                    gsef);
336 
337       if (spinbutton_width > 0)
338         {
339           if (spinbutton_width < 17)
340             gtk_entry_set_width_chars (GTK_ENTRY (gsef->value_spinbutton),
341                                        spinbutton_width);
342           else
343             gtk_widget_set_size_request (gsef->value_spinbutton,
344                                          spinbutton_width, -1);
345         }
346 
347       gtk_table_attach_defaults (GTK_TABLE (gse), gsef->value_spinbutton,
348                                  i+1, i+2,
349                                  gse->show_refval+1, gse->show_refval+2);
350       g_signal_connect (gsef->value_adjustment, "value-changed",
351                         G_CALLBACK (gimp_size_entry_value_callback),
352                         gsef);
353 
354       gtk_widget_show (gsef->value_spinbutton);
355 
356       if (gse->show_refval)
357         {
358           gsef->refval_adjustment = (GtkAdjustment *)
359             gtk_adjustment_new (gsef->refval,
360                                 gsef->min_refval, gsef->max_refval,
361                                 1.0, 10.0, 0.0);
362           gsef->refval_spinbutton = gimp_spin_button_new (gsef->refval_adjustment,
363                                                           1.0,
364                                                           gsef->refval_digits);
365           gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (gsef->refval_spinbutton),
366                                        TRUE);
367 
368           gtk_widget_set_size_request (gsef->refval_spinbutton,
369                                        spinbutton_width, -1);
370           gtk_table_attach_defaults (GTK_TABLE (gse), gsef->refval_spinbutton,
371                                      i + 1, i + 2, 1, 2);
372           g_signal_connect (gsef->refval_adjustment,
373                             "value-changed",
374                             G_CALLBACK (gimp_size_entry_refval_callback),
375                             gsef);
376 
377           gtk_widget_show (gsef->refval_spinbutton);
378         }
379 
380       if (gse->menu_show_pixels && (unit == GIMP_UNIT_PIXEL) &&
381           ! gse->show_refval)
382         gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->value_spinbutton),
383                                     gsef->refval_digits);
384     }
385 
386   store = gimp_unit_store_new (gse->number_of_fields);
387   gimp_unit_store_set_has_pixels (store, gse->menu_show_pixels);
388   gimp_unit_store_set_has_percent (store, gse->menu_show_percent);
389 
390   if (unit_format)
391     {
392       gchar *short_format = g_strdup (unit_format);
393       gchar *p;
394 
395       p = strstr (short_format, "%s");
396       if (p)
397         strcpy (p, "%a");
398 
399       p = strstr (short_format, "%p");
400       if (p)
401         strcpy (p, "%a");
402 
403       g_object_set (store,
404                     "short-format", short_format,
405                     "long-format",  unit_format,
406                     NULL);
407 
408       g_free (short_format);
409     }
410 
411   gse->unitmenu = gimp_unit_combo_box_new_with_model (store);
412   g_object_unref (store);
413 
414   gimp_unit_combo_box_set_active (GIMP_UNIT_COMBO_BOX (gse->unitmenu), unit);
415 
416   gtk_table_attach (GTK_TABLE (gse), gse->unitmenu,
417                     i+2, i+3,
418                     gse->show_refval+1, gse->show_refval+2,
419                     GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
420   g_signal_connect (gse->unitmenu, "changed",
421                     G_CALLBACK (gimp_size_entry_unit_callback),
422                     gse);
423   gtk_widget_show (gse->unitmenu);
424 
425   return GTK_WIDGET (gse);
426 }
427 
428 
429 /**
430  * gimp_size_entry_add_field:
431  * @gse:               The sizeentry you want to add a field to.
432  * @value_spinbutton:  The spinbutton to display the field's value.
433  * @refval_spinbutton: The spinbutton to display the field's reference value.
434  *
435  * Adds an input field to the #GimpSizeEntry.
436  *
437  * The new input field will have the index 0. If you specified @show_refval
438  * as %TRUE in gimp_size_entry_new() you have to pass an additional
439  * #GtkSpinButton to hold the reference value. If @show_refval was %FALSE,
440  * @refval_spinbutton will be ignored.
441  **/
442 void
gimp_size_entry_add_field(GimpSizeEntry * gse,GtkSpinButton * value_spinbutton,GtkSpinButton * refval_spinbutton)443 gimp_size_entry_add_field  (GimpSizeEntry *gse,
444                             GtkSpinButton *value_spinbutton,
445                             GtkSpinButton *refval_spinbutton)
446 {
447   GimpSizeEntryField *gsef;
448   gint                digits;
449 
450   g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
451   g_return_if_fail (GTK_IS_SPIN_BUTTON (value_spinbutton));
452 
453   if (gse->show_refval)
454     {
455       g_return_if_fail (GTK_IS_SPIN_BUTTON (refval_spinbutton));
456     }
457 
458   gsef = g_slice_new0 (GimpSizeEntryField);
459 
460   gse->fields = g_slist_prepend (gse->fields, gsef);
461   gse->number_of_fields++;
462 
463   gsef->gse            = gse;
464   gsef->resolution     = 1.0; /*  just to avoid division by zero  */
465   gsef->lower          = 0.0;
466   gsef->upper          = 100.0;
467   gsef->value          = 0;
468   gsef->min_value      = 0;
469   gsef->max_value      = SIZE_MAX_VALUE;
470   gsef->refval         = 0;
471   gsef->min_refval     = 0;
472   gsef->max_refval     = SIZE_MAX_VALUE;
473   gsef->refval_digits  =
474     (gse->update_policy == GIMP_SIZE_ENTRY_UPDATE_SIZE) ? 0 : 3;
475   gsef->stop_recursion = 0;
476 
477   gsef->value_adjustment = gtk_spin_button_get_adjustment (value_spinbutton);
478   gsef->value_spinbutton = GTK_WIDGET (value_spinbutton);
479   g_signal_connect (gsef->value_adjustment, "value-changed",
480                     G_CALLBACK (gimp_size_entry_value_callback),
481                     gsef);
482 
483   gimp_size_entry_attach_eevl (GTK_SPIN_BUTTON (gsef->value_spinbutton),
484                                gsef);
485 
486   if (gse->show_refval)
487     {
488       gsef->refval_adjustment = gtk_spin_button_get_adjustment (refval_spinbutton);
489       gsef->refval_spinbutton = GTK_WIDGET (refval_spinbutton);
490       g_signal_connect (gsef->refval_adjustment, "value-changed",
491                         G_CALLBACK (gimp_size_entry_refval_callback),
492                         gsef);
493     }
494 
495   digits = ((gse->unit == GIMP_UNIT_PIXEL) ? gsef->refval_digits :
496             (gse->unit == GIMP_UNIT_PERCENT) ? 2 :
497             GIMP_SIZE_ENTRY_DIGITS (gse->unit));
498 
499   gtk_spin_button_set_digits (GTK_SPIN_BUTTON (value_spinbutton), digits);
500 
501   if (gse->menu_show_pixels &&
502       !gse->show_refval &&
503       (gse->unit == GIMP_UNIT_PIXEL))
504     {
505       gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->value_spinbutton),
506                                   gsef->refval_digits);
507     }
508 }
509 
510 /**
511  * gimp_size_entry_attach_label:
512  * @gse:       The sizeentry you want to add a label to.
513  * @text:      The text of the label.
514  * @row:       The row where the label will be attached.
515  * @column:    The column where the label will be attached.
516  * @alignment: The horizontal alignment of the label.
517  *
518  * Attaches a #GtkLabel to the #GimpSizeEntry (which is a #GtkTable).
519  *
520  * Returns: A pointer to the new #GtkLabel widget.
521  **/
522 GtkWidget *
gimp_size_entry_attach_label(GimpSizeEntry * gse,const gchar * text,gint row,gint column,gfloat alignment)523 gimp_size_entry_attach_label (GimpSizeEntry *gse,
524                               const gchar   *text,
525                               gint           row,
526                               gint           column,
527                               gfloat         alignment)
528 {
529   GtkWidget *label;
530 
531   g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gse), NULL);
532   g_return_val_if_fail (text != NULL, NULL);
533 
534   label = gtk_label_new_with_mnemonic (text);
535 
536   if (column == 0)
537     {
538       GList *children;
539       GList *list;
540 
541       children = gtk_container_get_children (GTK_CONTAINER (gse));
542 
543       for (list = children; list; list = g_list_next (list))
544         {
545           GtkWidget *child = list->data;
546           gint       left_attach;
547           gint       top_attach;
548 
549           gtk_container_child_get (GTK_CONTAINER (gse), child,
550                                    "left-attach", &left_attach,
551                                    "top-attach",  &top_attach,
552                                    NULL);
553 
554           if (left_attach == 1 && top_attach == row)
555             {
556               gtk_label_set_mnemonic_widget (GTK_LABEL (label), child);
557               break;
558             }
559         }
560 
561       g_list_free (children);
562     }
563 
564   gtk_label_set_xalign (GTK_LABEL (label), alignment);
565 
566   gtk_table_attach (GTK_TABLE (gse), label, column, column+1, row, row+1,
567                     GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
568   gtk_widget_show (label);
569 
570   return label;
571 }
572 
573 
574 /**
575  * gimp_size_entry_set_resolution:
576  * @gse:        The sizeentry you want to set a resolution for.
577  * @field:      The index of the field you want to set the resolution for.
578  * @resolution: The new resolution (in dpi) for the chosen @field.
579  * @keep_size:  %TRUE if the @field's size in pixels should stay the same.
580  *              %FALSE if the @field's size in units should stay the same.
581  *
582  * Sets the resolution (in dpi) for field # @field of the #GimpSizeEntry.
583  *
584  * The @resolution passed will be clamped to fit in
585  * [#GIMP_MIN_RESOLUTION..#GIMP_MAX_RESOLUTION].
586  *
587  * This function does nothing if the #GimpSizeEntryUpdatePolicy specified in
588  * gimp_size_entry_new() doesn't equal to #GIMP_SIZE_ENTRY_UPDATE_SIZE.
589  **/
590 void
gimp_size_entry_set_resolution(GimpSizeEntry * gse,gint field,gdouble resolution,gboolean keep_size)591 gimp_size_entry_set_resolution (GimpSizeEntry *gse,
592                                 gint           field,
593                                 gdouble        resolution,
594                                 gboolean       keep_size)
595 {
596   GimpSizeEntryField *gsef;
597   gfloat              val;
598 
599   g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
600   g_return_if_fail ((field >= 0) && (field < gse->number_of_fields));
601 
602   resolution = CLAMP (resolution, GIMP_MIN_RESOLUTION, GIMP_MAX_RESOLUTION);
603 
604   gsef = (GimpSizeEntryField*) g_slist_nth_data (gse->fields, field);
605   gsef->resolution = resolution;
606 
607   val = gsef->value;
608 
609   gsef->stop_recursion = 0;
610   gimp_size_entry_set_refval_boundaries (gse, field,
611                                          gsef->min_refval, gsef->max_refval);
612 
613   if (! keep_size)
614     gimp_size_entry_set_value (gse, field, val);
615 }
616 
617 
618 /**
619  * gimp_size_entry_set_size:
620  * @gse:   The sizeentry you want to set a size for.
621  * @field: The index of the field you want to set the size for.
622  * @lower: The reference value which will be treated as 0%.
623  * @upper: The reference value which will be treated as 100%.
624  *
625  * Sets the pixel values for field # @field of the #GimpSizeEntry
626  * which will be treated as 0% and 100%.
627  *
628  * These values will be used if you specified @menu_show_percent as %TRUE
629  * in gimp_size_entry_new() and the user has selected GIMP_UNIT_PERCENT in
630  * the #GimpSizeEntry's #GimpUnitComboBox.
631  *
632  * This function does nothing if the #GimpSizeEntryUpdatePolicy specified in
633  * gimp_size_entry_new() doesn't equal to GIMP_SIZE_ENTRY_UPDATE_SIZE.
634  **/
635 void
gimp_size_entry_set_size(GimpSizeEntry * gse,gint field,gdouble lower,gdouble upper)636 gimp_size_entry_set_size (GimpSizeEntry *gse,
637                           gint           field,
638                           gdouble        lower,
639                           gdouble        upper)
640 {
641   GimpSizeEntryField *gsef;
642 
643   g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
644   g_return_if_fail ((field >= 0) && (field < gse->number_of_fields));
645   g_return_if_fail (lower <= upper);
646 
647   gsef = (GimpSizeEntryField*) g_slist_nth_data (gse->fields, field);
648   gsef->lower = lower;
649   gsef->upper = upper;
650 
651   gimp_size_entry_set_refval (gse, field, gsef->refval);
652 }
653 
654 
655 /**
656  * gimp_size_entry_set_value_boundaries:
657  * @gse:   The sizeentry you want to set value boundaries for.
658  * @field: The index of the field you want to set value boundaries for.
659  * @lower: The new lower boundary of the value of the chosen @field.
660  * @upper: The new upper boundary of the value of the chosen @field.
661  *
662  * Limits the range of possible values which can be entered in field # @field
663  * of the #GimpSizeEntry.
664  *
665  * The current value of the @field will be clamped to fit in the @field's
666  * new boundaries.
667  *
668  * NOTE: In most cases you won't be interested in this function because the
669  *       #GimpSizeEntry's purpose is to shield the programmer from unit
670  *       calculations. Use gimp_size_entry_set_refval_boundaries() instead.
671  *       Whatever you do, don't mix these calls. A size entry should either
672  *       be clamped by the value or the reference value.
673  **/
674 void
gimp_size_entry_set_value_boundaries(GimpSizeEntry * gse,gint field,gdouble lower,gdouble upper)675 gimp_size_entry_set_value_boundaries (GimpSizeEntry *gse,
676                                       gint           field,
677                                       gdouble        lower,
678                                       gdouble        upper)
679 {
680   GimpSizeEntryField *gsef;
681 
682   g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
683   g_return_if_fail ((field >= 0) && (field < gse->number_of_fields));
684   g_return_if_fail (lower <= upper);
685 
686   gsef = (GimpSizeEntryField*) g_slist_nth_data (gse->fields, field);
687   gsef->min_value        = lower;
688   gsef->max_value        = upper;
689 
690   g_object_freeze_notify (G_OBJECT (gsef->value_adjustment));
691 
692   gtk_adjustment_set_lower (gsef->value_adjustment, gsef->min_value);
693   gtk_adjustment_set_upper (gsef->value_adjustment, gsef->max_value);
694 
695   if (gsef->stop_recursion) /* this is a hack (but useful ;-) */
696     {
697       g_object_thaw_notify (G_OBJECT (gsef->value_adjustment));
698       return;
699     }
700 
701   gsef->stop_recursion++;
702   switch (gsef->gse->update_policy)
703     {
704     case GIMP_SIZE_ENTRY_UPDATE_NONE:
705       break;
706 
707     case GIMP_SIZE_ENTRY_UPDATE_SIZE:
708       switch (gse->unit)
709         {
710         case GIMP_UNIT_PIXEL:
711           gimp_size_entry_set_refval_boundaries (gse, field,
712                                                  gsef->min_value,
713                                                  gsef->max_value);
714           break;
715         case GIMP_UNIT_PERCENT:
716           gimp_size_entry_set_refval_boundaries (gse, field,
717                                                  gsef->lower +
718                                                  (gsef->upper - gsef->lower) *
719                                                  gsef->min_value / 100,
720                                                  gsef->lower +
721                                                  (gsef->upper - gsef->lower) *
722                                                  gsef->max_value / 100);
723           break;
724         default:
725           gimp_size_entry_set_refval_boundaries (gse, field,
726                                                  gsef->min_value *
727                                                  gsef->resolution /
728                                                  gimp_unit_get_factor (gse->unit),
729                                                  gsef->max_value *
730                                                  gsef->resolution /
731                                                  gimp_unit_get_factor (gse->unit));
732           break;
733         }
734       break;
735 
736     case GIMP_SIZE_ENTRY_UPDATE_RESOLUTION:
737       gimp_size_entry_set_refval_boundaries (gse, field,
738                                              gsef->min_value *
739                                              gimp_unit_get_factor (gse->unit),
740                                              gsef->max_value *
741                                              gimp_unit_get_factor (gse->unit));
742       break;
743 
744     default:
745       break;
746     }
747   gsef->stop_recursion--;
748 
749   gimp_size_entry_set_value (gse, field, gsef->value);
750 
751   g_object_thaw_notify (G_OBJECT (gsef->value_adjustment));
752 }
753 
754 /**
755  * gimp_size_entry_get_value:
756  * @gse:   The sizeentry you want to know a value of.
757  * @field: The index of the field you want to know the value of.
758  *
759  * Returns the value of field # @field of the #GimpSizeEntry.
760  *
761  * The @value returned is a distance or resolution
762  * in the #GimpUnit the user has selected in the #GimpSizeEntry's
763  * #GimpUnitComboBox.
764  *
765  * NOTE: In most cases you won't be interested in this value because the
766  *       #GimpSizeEntry's purpose is to shield the programmer from unit
767  *       calculations. Use gimp_size_entry_get_refval() instead.
768  *
769  * Returns: The value of the chosen @field.
770  **/
771 gdouble
gimp_size_entry_get_value(GimpSizeEntry * gse,gint field)772 gimp_size_entry_get_value (GimpSizeEntry *gse,
773                            gint           field)
774 {
775   GimpSizeEntryField *gsef;
776 
777   g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gse), 0);
778   g_return_val_if_fail ((field >= 0) && (field < gse->number_of_fields), 0);
779 
780   gsef = (GimpSizeEntryField *) g_slist_nth_data (gse->fields, field);
781   return gsef->value;
782 }
783 
784 static void
gimp_size_entry_update_value(GimpSizeEntryField * gsef,gdouble value)785 gimp_size_entry_update_value (GimpSizeEntryField *gsef,
786                               gdouble             value)
787 {
788   if (gsef->stop_recursion > 1)
789     return;
790 
791   gsef->value = value;
792 
793   switch (gsef->gse->update_policy)
794     {
795     case GIMP_SIZE_ENTRY_UPDATE_NONE:
796       break;
797 
798     case GIMP_SIZE_ENTRY_UPDATE_SIZE:
799       switch (gsef->gse->unit)
800         {
801         case GIMP_UNIT_PIXEL:
802           gsef->refval = value;
803           break;
804         case GIMP_UNIT_PERCENT:
805           gsef->refval =
806             CLAMP (gsef->lower + (gsef->upper - gsef->lower) * value / 100,
807                    gsef->min_refval, gsef->max_refval);
808           break;
809         default:
810           gsef->refval =
811             CLAMP (value * gsef->resolution /
812                    gimp_unit_get_factor (gsef->gse->unit),
813                    gsef->min_refval, gsef->max_refval);
814           break;
815         }
816       if (gsef->gse->show_refval)
817         gtk_adjustment_set_value (gsef->refval_adjustment, gsef->refval);
818       break;
819 
820     case GIMP_SIZE_ENTRY_UPDATE_RESOLUTION:
821       gsef->refval =
822         CLAMP (value * gimp_unit_get_factor (gsef->gse->unit),
823                gsef->min_refval, gsef->max_refval);
824       if (gsef->gse->show_refval)
825         gtk_adjustment_set_value (gsef->refval_adjustment, gsef->refval);
826       break;
827 
828     default:
829       break;
830     }
831 
832   g_signal_emit (gsef->gse, gimp_size_entry_signals[VALUE_CHANGED], 0);
833 }
834 
835 /**
836  * gimp_size_entry_set_value:
837  * @gse:   The sizeentry you want to set a value for.
838  * @field: The index of the field you want to set a value for.
839  * @value: The new value for @field.
840  *
841  * Sets the value for field # @field of the #GimpSizeEntry.
842  *
843  * The @value passed is treated to be a distance or resolution
844  * in the #GimpUnit the user has selected in the #GimpSizeEntry's
845  * #GimpUnitComboBox.
846  *
847  * NOTE: In most cases you won't be interested in this value because the
848  *       #GimpSizeEntry's purpose is to shield the programmer from unit
849  *       calculations. Use gimp_size_entry_set_refval() instead.
850  **/
851 void
gimp_size_entry_set_value(GimpSizeEntry * gse,gint field,gdouble value)852 gimp_size_entry_set_value (GimpSizeEntry *gse,
853                            gint           field,
854                            gdouble        value)
855 {
856   GimpSizeEntryField *gsef;
857 
858   g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
859   g_return_if_fail ((field >= 0) && (field < gse->number_of_fields));
860 
861   gsef = (GimpSizeEntryField *) g_slist_nth_data (gse->fields, field);
862 
863   value = CLAMP (value, gsef->min_value, gsef->max_value);
864 
865   gtk_adjustment_set_value (gsef->value_adjustment, value);
866   gimp_size_entry_update_value (gsef, value);
867 }
868 
869 
870 static void
gimp_size_entry_value_callback(GtkWidget * widget,gpointer data)871 gimp_size_entry_value_callback (GtkWidget *widget,
872                                 gpointer   data)
873 {
874   GimpSizeEntryField *gsef;
875   gdouble             new_value;
876 
877   gsef = (GimpSizeEntryField *) data;
878 
879   new_value = gtk_adjustment_get_value (GTK_ADJUSTMENT (widget));
880 
881   if (gsef->value != new_value)
882     gimp_size_entry_update_value (gsef, new_value);
883 }
884 
885 
886 /**
887  * gimp_size_entry_set_refval_boundaries:
888  * @gse:   The sizeentry you want to set the reference value boundaries for.
889  * @field: The index of the field you want to set the reference value
890  *         boundaries for.
891  * @lower: The new lower boundary of the reference value of the chosen @field.
892  * @upper: The new upper boundary of the reference value of the chosen @field.
893  *
894  * Limits the range of possible reference values which can be entered in
895  * field # @field of the #GimpSizeEntry.
896  *
897  * The current reference value of the @field will be clamped to fit in the
898  * @field's new boundaries.
899  **/
900 void
gimp_size_entry_set_refval_boundaries(GimpSizeEntry * gse,gint field,gdouble lower,gdouble upper)901 gimp_size_entry_set_refval_boundaries (GimpSizeEntry *gse,
902                                        gint           field,
903                                        gdouble        lower,
904                                        gdouble        upper)
905 {
906   GimpSizeEntryField *gsef;
907 
908   g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
909   g_return_if_fail ((field >= 0) && (field < gse->number_of_fields));
910   g_return_if_fail (lower <= upper);
911 
912   gsef = (GimpSizeEntryField *) g_slist_nth_data (gse->fields, field);
913   gsef->min_refval = lower;
914   gsef->max_refval = upper;
915 
916   if (gse->show_refval)
917     {
918       g_object_freeze_notify (G_OBJECT (gsef->refval_adjustment));
919 
920       gtk_adjustment_set_lower (gsef->refval_adjustment, gsef->min_refval);
921       gtk_adjustment_set_upper (gsef->refval_adjustment, gsef->max_refval);
922     }
923 
924   if (gsef->stop_recursion) /* this is a hack (but useful ;-) */
925     {
926       if (gse->show_refval)
927         g_object_thaw_notify (G_OBJECT (gsef->refval_adjustment));
928 
929       return;
930     }
931 
932   gsef->stop_recursion++;
933   switch (gsef->gse->update_policy)
934     {
935     case GIMP_SIZE_ENTRY_UPDATE_NONE:
936       break;
937 
938     case GIMP_SIZE_ENTRY_UPDATE_SIZE:
939       switch (gse->unit)
940         {
941         case GIMP_UNIT_PIXEL:
942           gimp_size_entry_set_value_boundaries (gse, field,
943                                                 gsef->min_refval,
944                                                 gsef->max_refval);
945           break;
946         case GIMP_UNIT_PERCENT:
947           gimp_size_entry_set_value_boundaries (gse, field,
948                                                 100 * (gsef->min_refval -
949                                                        gsef->lower) /
950                                                 (gsef->upper - gsef->lower),
951                                                 100 * (gsef->max_refval -
952                                                        gsef->lower) /
953                                                 (gsef->upper - gsef->lower));
954           break;
955         default:
956           gimp_size_entry_set_value_boundaries (gse, field,
957                                                 gsef->min_refval *
958                                                 gimp_unit_get_factor (gse->unit) /
959                                                 gsef->resolution,
960                                                 gsef->max_refval *
961                                                 gimp_unit_get_factor (gse->unit) /
962                                                 gsef->resolution);
963           break;
964         }
965       break;
966 
967     case GIMP_SIZE_ENTRY_UPDATE_RESOLUTION:
968       gimp_size_entry_set_value_boundaries (gse, field,
969                                             gsef->min_refval /
970                                             gimp_unit_get_factor (gse->unit),
971                                             gsef->max_refval /
972                                             gimp_unit_get_factor (gse->unit));
973       break;
974 
975     default:
976       break;
977     }
978   gsef->stop_recursion--;
979 
980   gimp_size_entry_set_refval (gse, field, gsef->refval);
981 
982   if (gse->show_refval)
983     g_object_thaw_notify (G_OBJECT (gsef->refval_adjustment));
984 }
985 
986 /**
987  * gimp_size_entry_set_refval_digits:
988  * @gse:    The sizeentry you want to set the reference value digits for.
989  * @field:  The index of the field you want to set the reference value for.
990  * @digits: The new number of decimal digits for the #GtkSpinButton which
991  *          displays @field's reference value.
992  *
993  * Sets the decimal digits of field # @field of the #GimpSizeEntry to
994  * @digits.
995  *
996  * If you don't specify this value explicitly, the reference value's number
997  * of digits will equal to 0 for #GIMP_SIZE_ENTRY_UPDATE_SIZE and to 2 for
998  * #GIMP_SIZE_ENTRY_UPDATE_RESOLUTION.
999  **/
1000 void
gimp_size_entry_set_refval_digits(GimpSizeEntry * gse,gint field,gint digits)1001 gimp_size_entry_set_refval_digits (GimpSizeEntry *gse,
1002                                    gint           field,
1003                                    gint           digits)
1004 {
1005   GimpSizeEntryField *gsef;
1006 
1007   g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
1008   g_return_if_fail ((field >= 0) && (field < gse->number_of_fields));
1009   g_return_if_fail ((digits >= 0) && (digits <= 6));
1010 
1011   gsef = (GimpSizeEntryField*) g_slist_nth_data (gse->fields, field);
1012   gsef->refval_digits = digits;
1013 
1014   if (gse->update_policy == GIMP_SIZE_ENTRY_UPDATE_SIZE)
1015     {
1016       if (gse->show_refval)
1017         gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->refval_spinbutton),
1018                                     gsef->refval_digits);
1019       else if (gse->unit == GIMP_UNIT_PIXEL)
1020         gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->value_spinbutton),
1021                                     gsef->refval_digits);
1022     }
1023 }
1024 
1025 /**
1026  * gimp_size_entry_get_refval:
1027  * @gse:   The sizeentry you want to know a reference value of.
1028  * @field: The index of the field you want to know the reference value of.
1029  *
1030  * Returns the reference value for field # @field of the #GimpSizeEntry.
1031  *
1032  * The reference value is either a distance in pixels or a resolution
1033  * in dpi, depending on which #GimpSizeEntryUpdatePolicy you chose in
1034  * gimp_size_entry_new().
1035  *
1036  * Returns: The reference value of the chosen @field.
1037  **/
1038 gdouble
gimp_size_entry_get_refval(GimpSizeEntry * gse,gint field)1039 gimp_size_entry_get_refval (GimpSizeEntry *gse,
1040                             gint           field)
1041 {
1042   GimpSizeEntryField *gsef;
1043 
1044   /*  return 1.0 to avoid division by zero  */
1045   g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gse), 1.0);
1046   g_return_val_if_fail ((field >= 0) && (field < gse->number_of_fields), 1.0);
1047 
1048   gsef = (GimpSizeEntryField*) g_slist_nth_data (gse->fields, field);
1049   return gsef->refval;
1050 }
1051 
1052 static void
gimp_size_entry_update_refval(GimpSizeEntryField * gsef,gdouble refval)1053 gimp_size_entry_update_refval (GimpSizeEntryField *gsef,
1054                                gdouble             refval)
1055 {
1056   if (gsef->stop_recursion > 1)
1057     return;
1058 
1059   gsef->refval = refval;
1060 
1061   switch (gsef->gse->update_policy)
1062     {
1063     case GIMP_SIZE_ENTRY_UPDATE_NONE:
1064       break;
1065 
1066     case GIMP_SIZE_ENTRY_UPDATE_SIZE:
1067       switch (gsef->gse->unit)
1068         {
1069         case GIMP_UNIT_PIXEL:
1070           gsef->value = refval;
1071           break;
1072         case GIMP_UNIT_PERCENT:
1073           gsef->value =
1074             CLAMP (100 * (refval - gsef->lower) / (gsef->upper - gsef->lower),
1075                    gsef->min_value, gsef->max_value);
1076           break;
1077         default:
1078           gsef->value =
1079             CLAMP (refval * gimp_unit_get_factor (gsef->gse->unit) /
1080                    gsef->resolution,
1081                    gsef->min_value, gsef->max_value);
1082           break;
1083         }
1084       gtk_adjustment_set_value (gsef->value_adjustment, gsef->value);
1085       break;
1086 
1087     case GIMP_SIZE_ENTRY_UPDATE_RESOLUTION:
1088       gsef->value =
1089         CLAMP (refval / gimp_unit_get_factor (gsef->gse->unit),
1090                gsef->min_value, gsef->max_value);
1091       gtk_adjustment_set_value (gsef->value_adjustment, gsef->value);
1092       break;
1093 
1094     default:
1095       break;
1096     }
1097 
1098   g_signal_emit (gsef->gse, gimp_size_entry_signals[REFVAL_CHANGED], 0);
1099 }
1100 
1101 /**
1102  * gimp_size_entry_set_refval:
1103  * @gse:    The sizeentry you want to set a reference value for.
1104  * @field:  The index of the field you want to set the reference value for.
1105  * @refval: The new reference value for @field.
1106  *
1107  * Sets the reference value for field # @field of the #GimpSizeEntry.
1108  *
1109  * The @refval passed is either a distance in pixels or a resolution in dpi,
1110  * depending on which #GimpSizeEntryUpdatePolicy you chose in
1111  * gimp_size_entry_new().
1112  **/
1113 void
gimp_size_entry_set_refval(GimpSizeEntry * gse,gint field,gdouble refval)1114 gimp_size_entry_set_refval (GimpSizeEntry *gse,
1115                             gint           field,
1116                             gdouble        refval)
1117 {
1118   GimpSizeEntryField *gsef;
1119 
1120   g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
1121   g_return_if_fail ((field >= 0) && (field < gse->number_of_fields));
1122 
1123   gsef = (GimpSizeEntryField *) g_slist_nth_data (gse->fields, field);
1124 
1125   refval = CLAMP (refval, gsef->min_refval, gsef->max_refval);
1126 
1127   if (gse->show_refval)
1128     gtk_adjustment_set_value (gsef->refval_adjustment, refval);
1129 
1130   gimp_size_entry_update_refval (gsef, refval);
1131 }
1132 
1133 static void
gimp_size_entry_refval_callback(GtkWidget * widget,gpointer data)1134 gimp_size_entry_refval_callback (GtkWidget *widget,
1135                                  gpointer   data)
1136 {
1137   GimpSizeEntryField *gsef;
1138   gdouble             new_refval;
1139 
1140   gsef = (GimpSizeEntryField *) data;
1141 
1142   new_refval = gtk_adjustment_get_value (GTK_ADJUSTMENT (widget));
1143 
1144   if (gsef->refval != new_refval)
1145     gimp_size_entry_update_refval (gsef, new_refval);
1146 }
1147 
1148 
1149 /**
1150  * gimp_size_entry_get_unit:
1151  * @gse: The sizeentry you want to know the unit of.
1152  *
1153  * Returns the #GimpUnit the user has selected in the #GimpSizeEntry's
1154  * #GimpUnitComboBox.
1155  *
1156  * Returns: The sizeentry's unit.
1157  **/
1158 GimpUnit
gimp_size_entry_get_unit(GimpSizeEntry * gse)1159 gimp_size_entry_get_unit (GimpSizeEntry *gse)
1160 {
1161   g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gse), GIMP_UNIT_INCH);
1162 
1163   return gse->unit;
1164 }
1165 
1166 static void
gimp_size_entry_update_unit(GimpSizeEntry * gse,GimpUnit unit)1167 gimp_size_entry_update_unit (GimpSizeEntry *gse,
1168                              GimpUnit       unit)
1169 {
1170   GimpSizeEntryField *gsef;
1171   gint                i;
1172   gint                digits;
1173 
1174   gse->unit = unit;
1175 
1176   digits = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (gse),
1177                                                "gimp-pixel-digits"));
1178 
1179   for (i = 0; i < gse->number_of_fields; i++)
1180     {
1181       gsef = (GimpSizeEntryField *) g_slist_nth_data (gse->fields, i);
1182 
1183       if (gse->update_policy == GIMP_SIZE_ENTRY_UPDATE_SIZE)
1184         {
1185           if (unit == GIMP_UNIT_PIXEL)
1186             gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->value_spinbutton),
1187                                         gsef->refval_digits + digits);
1188           else if (unit == GIMP_UNIT_PERCENT)
1189             gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->value_spinbutton),
1190                                         2 + digits);
1191           else
1192             gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->value_spinbutton),
1193                                         GIMP_SIZE_ENTRY_DIGITS (unit) + digits);
1194         }
1195       else if (gse->update_policy == GIMP_SIZE_ENTRY_UPDATE_RESOLUTION)
1196         {
1197           digits = (gimp_unit_get_digits (GIMP_UNIT_INCH) -
1198                     gimp_unit_get_digits (unit));
1199           gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->value_spinbutton),
1200                                       MAX (3 + digits, 3));
1201         }
1202 
1203       gsef->stop_recursion = 0; /* hack !!! */
1204 
1205       gimp_size_entry_set_refval_boundaries (gse, i,
1206                                              gsef->min_refval,
1207                                              gsef->max_refval);
1208     }
1209 
1210   g_signal_emit (gse, gimp_size_entry_signals[UNIT_CHANGED], 0);
1211 }
1212 
1213 
1214 /**
1215  * gimp_size_entry_set_unit:
1216  * @gse:  The sizeentry you want to change the unit for.
1217  * @unit: The new unit.
1218  *
1219  * Sets the #GimpSizeEntry's unit. The reference value for all fields will
1220  * stay the same but the value in units or pixels per unit will change
1221  * according to which #GimpSizeEntryUpdatePolicy you chose in
1222  * gimp_size_entry_new().
1223  **/
1224 void
gimp_size_entry_set_unit(GimpSizeEntry * gse,GimpUnit unit)1225 gimp_size_entry_set_unit (GimpSizeEntry *gse,
1226                           GimpUnit       unit)
1227 {
1228   g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
1229   g_return_if_fail (gse->menu_show_pixels || (unit != GIMP_UNIT_PIXEL));
1230   g_return_if_fail (gse->menu_show_percent || (unit != GIMP_UNIT_PERCENT));
1231 
1232   gimp_unit_combo_box_set_active (GIMP_UNIT_COMBO_BOX (gse->unitmenu), unit);
1233   gimp_size_entry_update_unit (gse, unit);
1234 }
1235 
1236 static void
gimp_size_entry_unit_callback(GtkWidget * widget,GimpSizeEntry * gse)1237 gimp_size_entry_unit_callback (GtkWidget     *widget,
1238                                GimpSizeEntry *gse)
1239 {
1240   GimpUnit new_unit;
1241 
1242   new_unit = gimp_unit_combo_box_get_active (GIMP_UNIT_COMBO_BOX (widget));
1243 
1244   if (gse->unit != new_unit)
1245     gimp_size_entry_update_unit (gse, new_unit);
1246 }
1247 
1248 /**
1249  * gimp_size_entry_attach_eevl:
1250  * @spin_button: one of the size_entry's spinbuttons.
1251  * @gsef:        a size entry field.
1252  *
1253  * Hooks in the GimpEevl unit expression parser into the
1254  * #GtkSpinButton of the #GimpSizeEntryField.
1255  **/
1256 static void
gimp_size_entry_attach_eevl(GtkSpinButton * spin_button,GimpSizeEntryField * gsef)1257 gimp_size_entry_attach_eevl (GtkSpinButton      *spin_button,
1258                              GimpSizeEntryField *gsef)
1259 {
1260   gtk_spin_button_set_numeric (spin_button, FALSE);
1261   gtk_spin_button_set_update_policy (spin_button, GTK_UPDATE_IF_VALID);
1262 
1263   g_signal_connect_after (spin_button, "input",
1264                           G_CALLBACK (gimp_size_entry_eevl_input_callback),
1265                           gsef);
1266 }
1267 
1268 static gint
gimp_size_entry_eevl_input_callback(GtkSpinButton * spinner,gdouble * return_val,gpointer * data)1269 gimp_size_entry_eevl_input_callback (GtkSpinButton *spinner,
1270                                      gdouble       *return_val,
1271                                      gpointer      *data)
1272 {
1273   GimpSizeEntryField *gsef      = (GimpSizeEntryField *) data;
1274   GimpEevlOptions     options   = GIMP_EEVL_OPTIONS_INIT;
1275   gboolean            success   = FALSE;
1276   const gchar        *error_pos = 0;
1277   GError             *error     = NULL;
1278   GimpEevlQuantity    result;
1279 
1280   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spinner), FALSE);
1281   g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gsef->gse), FALSE);
1282 
1283   options.unit_resolver_proc = gimp_size_entry_eevl_unit_resolver;
1284   options.data               = data;
1285 
1286   /* enable ratio expressions when there are two fields */
1287   if (gsef->gse->number_of_fields == 2)
1288     {
1289       GimpSizeEntryField *other_gsef;
1290       GimpEevlQuantity    default_unit_factor;
1291       gdouble             default_unit_offset;
1292 
1293       options.ratio_expressions = TRUE;
1294 
1295       if (gsef == gsef->gse->fields->data)
1296         {
1297           other_gsef = gsef->gse->fields->next->data;
1298 
1299           options.ratio_invert = FALSE;
1300         }
1301       else
1302         {
1303           other_gsef = gsef->gse->fields->data;
1304 
1305           options.ratio_invert = TRUE;
1306         }
1307 
1308       options.unit_resolver_proc (NULL,
1309                                   &default_unit_factor, &default_unit_offset,
1310                                   options.data);
1311 
1312       options.ratio_quantity.value     = other_gsef->value /
1313                                          default_unit_factor.value;
1314       options.ratio_quantity.dimension = default_unit_factor.dimension;
1315     }
1316 
1317   success = gimp_eevl_evaluate (gtk_entry_get_text (GTK_ENTRY (spinner)),
1318                                 &options,
1319                                 &result,
1320                                 &error_pos,
1321                                 &error);
1322   if (! success)
1323     {
1324       if (error && error_pos)
1325         {
1326           g_printerr ("ERROR: %s at '%s'\n",
1327                       error->message,
1328                       *error_pos ? error_pos : "<End of input>");
1329         }
1330       else
1331         {
1332           g_printerr ("ERROR: Expression evaluation failed without error.\n");
1333         }
1334 
1335       gtk_widget_error_bell (GTK_WIDGET (spinner));
1336       return GTK_INPUT_ERROR;
1337     }
1338   else if (result.dimension != 1 && gsef->gse->unit != GIMP_UNIT_PERCENT)
1339     {
1340       g_printerr ("ERROR: result has wrong dimension (expected 1, got %d)\n", result.dimension);
1341 
1342       gtk_widget_error_bell (GTK_WIDGET (spinner));
1343       return GTK_INPUT_ERROR;
1344     }
1345   else if (result.dimension != 0 && gsef->gse->unit == GIMP_UNIT_PERCENT)
1346     {
1347       g_printerr ("ERROR: result has wrong dimension (expected 0, got %d)\n", result.dimension);
1348 
1349       gtk_widget_error_bell (GTK_WIDGET (spinner));
1350       return GTK_INPUT_ERROR;
1351     }
1352   else
1353     {
1354       /* transform back to UI-unit */
1355       GimpEevlQuantity  ui_unit;
1356       GtkAdjustment    *adj;
1357       gdouble           val;
1358 
1359       switch (gsef->gse->unit)
1360         {
1361         case GIMP_UNIT_PIXEL:
1362           ui_unit.value     = gsef->resolution;
1363           ui_unit.dimension = 1;
1364           break;
1365         case GIMP_UNIT_PERCENT:
1366           ui_unit.value     = 1.0;
1367           ui_unit.dimension = 0;
1368           break;
1369         default:
1370           ui_unit.value     = gimp_unit_get_factor(gsef->gse->unit);
1371           ui_unit.dimension = 1;
1372           break;
1373         }
1374 
1375       *return_val = result.value * ui_unit.value;
1376 
1377       /*  CLAMP() to adjustment bounds, or too large/small values
1378        *  will make the validation machinery revert to the old value.
1379        *  See bug #694477.
1380        */
1381       adj = gtk_spin_button_get_adjustment (spinner);
1382 
1383       val = CLAMP (*return_val,
1384                    gtk_adjustment_get_lower (adj),
1385                    gtk_adjustment_get_upper (adj));
1386 
1387       if (val != *return_val)
1388         {
1389           gtk_widget_error_bell (GTK_WIDGET (spinner));
1390           *return_val = val;
1391         }
1392 
1393       return TRUE;
1394     }
1395 }
1396 
1397 static gboolean
gimp_size_entry_eevl_unit_resolver(const gchar * identifier,GimpEevlQuantity * factor,gdouble * offset,gpointer data)1398 gimp_size_entry_eevl_unit_resolver (const gchar      *identifier,
1399                                     GimpEevlQuantity *factor,
1400                                     gdouble          *offset,
1401                                     gpointer          data)
1402 {
1403   GimpSizeEntryField *gsef                 = (GimpSizeEntryField *) data;
1404   gboolean            resolve_default_unit = (identifier == NULL);
1405   GimpUnit            unit;
1406 
1407   g_return_val_if_fail (gsef, FALSE);
1408   g_return_val_if_fail (factor != NULL, FALSE);
1409   g_return_val_if_fail (offset != NULL, FALSE);
1410   g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gsef->gse), FALSE);
1411 
1412   *offset = 0.0;
1413 
1414   for (unit = 0;
1415        unit <= gimp_unit_get_number_of_units ();
1416        unit++)
1417     {
1418       /* Hack to handle percent within the loop */
1419       if (unit == gimp_unit_get_number_of_units ())
1420         unit = GIMP_UNIT_PERCENT;
1421 
1422       if ((resolve_default_unit && unit == gsef->gse->unit) ||
1423           (identifier &&
1424            (strcmp (gimp_unit_get_symbol (unit),       identifier) == 0 ||
1425             strcmp (gimp_unit_get_abbreviation (unit), identifier) == 0)))
1426         {
1427           switch (unit)
1428             {
1429             case GIMP_UNIT_PERCENT:
1430               if (gsef->gse->unit == GIMP_UNIT_PERCENT)
1431                 {
1432                   factor->value = 1;
1433                   factor->dimension = 0;
1434                 }
1435               else
1436                 {
1437                   /* gsef->upper contains the '100%'-value */
1438                   factor->value = 100*gsef->resolution/(gsef->upper - gsef->lower);
1439                   /* gsef->lower contains the '0%'-value */
1440                   *offset       = gsef->lower/gsef->resolution;
1441                   factor->dimension = 1;
1442                 }
1443               /* return here, don't perform percentage conversion */
1444               return TRUE;
1445             case GIMP_UNIT_PIXEL:
1446               factor->value     = gsef->resolution;
1447               break;
1448             default:
1449               factor->value     = gimp_unit_get_factor (unit);
1450               break;
1451             }
1452 
1453           if (gsef->gse->unit == GIMP_UNIT_PERCENT)
1454             {
1455               /* map non-percentages onto percent */
1456               factor->value = gsef->upper/(100*gsef->resolution);
1457               factor->dimension = 0;
1458             }
1459           else
1460             {
1461               factor->dimension = 1;
1462             }
1463 
1464           /* We are done */
1465           return TRUE;
1466         }
1467     }
1468 
1469   return FALSE;
1470 }
1471 
1472 /**
1473  * gimp_size_entry_show_unit_menu:
1474  * @gse: a #GimpSizeEntry
1475  * @show: Boolean
1476  *
1477  * Controls whether a unit menu is shown in the size entry.  If
1478  * @show is #TRUE, the menu is shown; otherwise it is hidden.
1479  *
1480  * Since: 2.4
1481  **/
1482 void
gimp_size_entry_show_unit_menu(GimpSizeEntry * gse,gboolean show)1483 gimp_size_entry_show_unit_menu (GimpSizeEntry *gse,
1484                                 gboolean       show)
1485 {
1486   g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
1487 
1488   gtk_widget_set_visible (gse->unitmenu, show);
1489 }
1490 
1491 
1492 /**
1493  * gimp_size_entry_set_pixel_digits:
1494  * @gse: a #GimpSizeEntry
1495  * @digits: the number of digits to display for a pixel size
1496  *
1497  * This function allows you set up a #GimpSizeEntry so that sub-pixel
1498  * sizes can be entered.
1499  **/
1500 void
gimp_size_entry_set_pixel_digits(GimpSizeEntry * gse,gint digits)1501 gimp_size_entry_set_pixel_digits (GimpSizeEntry *gse,
1502                                   gint           digits)
1503 {
1504   GimpUnitComboBox *combo;
1505 
1506   g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
1507 
1508   combo = GIMP_UNIT_COMBO_BOX (gse->unitmenu);
1509 
1510   g_object_set_data (G_OBJECT (gse), "gimp-pixel-digits",
1511                      GINT_TO_POINTER (digits));
1512   gimp_size_entry_update_unit (gse, gimp_unit_combo_box_get_active (combo));
1513 }
1514 
1515 
1516 /**
1517  * gimp_size_entry_grab_focus:
1518  * @gse: The sizeentry you want to grab the keyboard focus.
1519  *
1520  * This function is rather ugly and just a workaround for the fact that
1521  * it's impossible to implement gtk_widget_grab_focus() for a #GtkTable.
1522  **/
1523 void
gimp_size_entry_grab_focus(GimpSizeEntry * gse)1524 gimp_size_entry_grab_focus (GimpSizeEntry *gse)
1525 {
1526   GimpSizeEntryField *gsef;
1527 
1528   g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
1529 
1530   gsef = gse->fields->data;
1531   if (gsef)
1532     gtk_widget_grab_focus (gse->show_refval ?
1533                            gsef->refval_spinbutton : gsef->value_spinbutton);
1534 }
1535 
1536 /**
1537  * gimp_size_entry_set_activates_default:
1538  * @gse:     A #GimpSizeEntry
1539  * @setting: %TRUE to activate window's default widget on Enter keypress
1540  *
1541  * Iterates over all entries in the #GimpSizeEntry and calls
1542  * gtk_entry_set_activates_default() on them.
1543  *
1544  * Since: 2.4
1545  **/
1546 void
gimp_size_entry_set_activates_default(GimpSizeEntry * gse,gboolean setting)1547 gimp_size_entry_set_activates_default (GimpSizeEntry *gse,
1548                                        gboolean       setting)
1549 {
1550   GSList *list;
1551 
1552   g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
1553 
1554   for (list = gse->fields; list; list = g_slist_next (list))
1555     {
1556       GimpSizeEntryField *gsef = list->data;
1557 
1558       if (gsef->value_spinbutton)
1559         gtk_entry_set_activates_default (GTK_ENTRY (gsef->value_spinbutton),
1560                                          setting);
1561 
1562       if (gsef->refval_spinbutton)
1563         gtk_entry_set_activates_default (GTK_ENTRY (gsef->refval_spinbutton),
1564                                          setting);
1565     }
1566 }
1567 
1568 /**
1569  * gimp_size_entry_get_help_widget:
1570  * @gse: a #GimpSizeEntry
1571  * @field: the index of the widget you want to get a pointer to
1572  *
1573  * You shouldn't fiddle with the internals of a #GimpSizeEntry but
1574  * if you want to set tooltips using gimp_help_set_help_data() you
1575  * can use this function to get a pointer to the spinbuttons.
1576  *
1577  * Return value: a #GtkWidget pointer that you can attach a tooltip to.
1578  **/
1579 GtkWidget *
gimp_size_entry_get_help_widget(GimpSizeEntry * gse,gint field)1580 gimp_size_entry_get_help_widget (GimpSizeEntry *gse,
1581                                  gint           field)
1582 {
1583   GimpSizeEntryField *gsef;
1584 
1585   g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gse), NULL);
1586   g_return_val_if_fail ((field >= 0) && (field < gse->number_of_fields), NULL);
1587 
1588   gsef = g_slist_nth_data (gse->fields, field);
1589   if (!gsef)
1590     return NULL;
1591 
1592   return (gsef->refval_spinbutton ?
1593           gsef->refval_spinbutton : gsef->value_spinbutton);
1594 }
1595