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