1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * gimpsizebox.c
5  * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  */
20 
21 #include "config.h"
22 
23 #include <stdio.h>
24 
25 #include <gegl.h>
26 #include <gtk/gtk.h>
27 
28 #include "libgimpbase/gimpbase.h"
29 #include "libgimpmath/gimpmath.h"
30 #include "libgimpconfig/gimpconfig.h"
31 #include "libgimpwidgets/gimpwidgets.h"
32 
33 #include "widgets-types.h"
34 
35 #include "gimpsizebox.h"
36 
37 #include "gimp-intl.h"
38 
39 
40 #define SB_WIDTH 8
41 
42 enum
43 {
44   PROP_0,
45   PROP_WIDTH,
46   PROP_HEIGHT,
47   PROP_UNIT,
48   PROP_XRESOLUTION,
49   PROP_YRESOLUTION,
50   PROP_RESOLUTION_UNIT,
51   PROP_KEEP_ASPECT,
52   PROP_EDIT_RESOLUTION
53 };
54 
55 
56 #define GIMP_SIZE_BOX_GET_PRIVATE(obj) ((GimpSizeBoxPrivate *) gimp_size_box_get_instance_private ((GimpSizeBox *) (obj)))
57 
58 typedef struct _GimpSizeBoxPrivate GimpSizeBoxPrivate;
59 
60 struct _GimpSizeBoxPrivate
61 {
62   GimpSizeEntry   *size_entry;
63   GimpChainButton *size_chain;
64   GtkWidget       *pixel_label;
65   GtkWidget       *res_label;
66 };
67 
68 
69 static void   gimp_size_box_constructed       (GObject         *object);
70 static void   gimp_size_box_dispose           (GObject         *object);
71 static void   gimp_size_box_set_property      (GObject         *object,
72                                                guint            property_id,
73                                                const GValue    *value,
74                                                GParamSpec      *pspec);
75 static void   gimp_size_box_get_property      (GObject         *object,
76                                                guint            property_id,
77                                                GValue          *value,
78                                                GParamSpec      *pspec);
79 
80 static void   gimp_size_box_update_size       (GimpSizeBox     *box);
81 static void   gimp_size_box_update_resolution (GimpSizeBox     *box);
82 static void   gimp_size_box_chain_toggled     (GimpChainButton *button,
83                                                GimpSizeBox     *box);
84 
85 
G_DEFINE_TYPE_WITH_PRIVATE(GimpSizeBox,gimp_size_box,GTK_TYPE_BOX)86 G_DEFINE_TYPE_WITH_PRIVATE (GimpSizeBox, gimp_size_box, GTK_TYPE_BOX)
87 
88 #define parent_class gimp_size_box_parent_class
89 
90 
91 static void
92 gimp_size_box_class_init (GimpSizeBoxClass *klass)
93 {
94   GObjectClass *object_class = G_OBJECT_CLASS (klass);
95 
96   object_class->constructed  = gimp_size_box_constructed;
97   object_class->dispose      = gimp_size_box_dispose;
98   object_class->set_property = gimp_size_box_set_property;
99   object_class->get_property = gimp_size_box_get_property;
100 
101   g_object_class_install_property (object_class, PROP_WIDTH,
102                                    g_param_spec_int ("width", NULL, NULL,
103                                                      GIMP_MIN_IMAGE_SIZE,
104                                                      GIMP_MAX_IMAGE_SIZE,
105                                                      256,
106                                                      GIMP_PARAM_READWRITE |
107                                                      G_PARAM_CONSTRUCT));
108   g_object_class_install_property (object_class, PROP_HEIGHT,
109                                    g_param_spec_int ("height", NULL, NULL,
110                                                      GIMP_MIN_IMAGE_SIZE,
111                                                      GIMP_MAX_IMAGE_SIZE,
112                                                      256,
113                                                      GIMP_PARAM_READWRITE |
114                                                      G_PARAM_CONSTRUCT));
115   g_object_class_install_property (object_class, PROP_UNIT,
116                                    gimp_param_spec_unit ("unit", NULL, NULL,
117                                                          TRUE, TRUE,
118                                                          GIMP_UNIT_PIXEL,
119                                                          GIMP_PARAM_READWRITE |
120                                                          G_PARAM_CONSTRUCT));
121   g_object_class_install_property (object_class, PROP_XRESOLUTION,
122                                    g_param_spec_double ("xresolution",
123                                                         NULL, NULL,
124                                                         GIMP_MIN_RESOLUTION,
125                                                         GIMP_MAX_RESOLUTION,
126                                                         72.0,
127                                                         GIMP_PARAM_READWRITE |
128                                                         G_PARAM_CONSTRUCT));
129   g_object_class_install_property (object_class, PROP_YRESOLUTION,
130                                    g_param_spec_double ("yresolution",
131                                                         NULL, NULL,
132                                                         GIMP_MIN_RESOLUTION,
133                                                         GIMP_MAX_RESOLUTION,
134                                                         72.0,
135                                                         GIMP_PARAM_READWRITE |
136                                                         G_PARAM_CONSTRUCT));
137   g_object_class_install_property (object_class, PROP_RESOLUTION_UNIT,
138                                    gimp_param_spec_unit ("resolution-unit",
139                                                          NULL, NULL,
140                                                          FALSE, FALSE,
141                                                          GIMP_UNIT_INCH,
142                                                          GIMP_PARAM_READWRITE |
143                                                          G_PARAM_CONSTRUCT));
144   g_object_class_install_property (object_class, PROP_KEEP_ASPECT,
145                                    g_param_spec_boolean ("keep-aspect",
146                                                          NULL, NULL,
147                                                          TRUE,
148                                                          GIMP_PARAM_READWRITE));
149   g_object_class_install_property (object_class, PROP_EDIT_RESOLUTION,
150                                    g_param_spec_boolean ("edit-resolution",
151                                                          NULL, NULL,
152                                                          FALSE,
153                                                          GIMP_PARAM_READWRITE |
154                                                          G_PARAM_CONSTRUCT_ONLY));
155 }
156 
157 static void
gimp_size_box_init(GimpSizeBox * box)158 gimp_size_box_init (GimpSizeBox *box)
159 {
160   gtk_orientable_set_orientation (GTK_ORIENTABLE (box),
161                                   GTK_ORIENTATION_VERTICAL);
162 
163   gtk_box_set_spacing (GTK_BOX (box), 6);
164 
165   box->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
166 }
167 
168 static void
gimp_size_box_constructed(GObject * object)169 gimp_size_box_constructed (GObject *object)
170 {
171   GimpSizeBox        *box  = GIMP_SIZE_BOX (object);
172   GimpSizeBoxPrivate *priv = GIMP_SIZE_BOX_GET_PRIVATE (box);
173   GtkWidget          *vbox;
174   GtkWidget          *entry;
175   GtkWidget          *hbox;
176   GtkWidget          *label;
177   GList              *children;
178   GList              *list;
179 
180   G_OBJECT_CLASS (parent_class)->constructed (object);
181 
182   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
183   gtk_box_pack_start (GTK_BOX (box), hbox, FALSE, FALSE, 0);
184   gtk_widget_show (hbox);
185 
186   entry = gimp_coordinates_new (box->unit, "%p",
187                                 TRUE, TRUE, SB_WIDTH,
188                                 GIMP_SIZE_ENTRY_UPDATE_SIZE,
189                                 TRUE, TRUE,
190                                 _("_Width:"),
191                                 box->width, box->xresolution,
192                                 GIMP_MIN_IMAGE_SIZE, GIMP_MAX_IMAGE_SIZE,
193                                 0, box->width,
194                                 _("H_eight:"),
195                                 box->height, box->yresolution,
196                                 GIMP_MIN_IMAGE_SIZE, GIMP_MAX_IMAGE_SIZE,
197                                 0, box->height);
198 
199   priv->size_entry = GIMP_SIZE_ENTRY (entry);
200   priv->size_chain = GIMP_COORDINATES_CHAINBUTTON (GIMP_SIZE_ENTRY (entry));
201 
202   /*
203    * let gimp_prop_coordinates_callback know how to interpret the chainbutton
204    */
205   g_object_set_data (G_OBJECT (priv->size_chain),
206                      "constrains-ratio", GINT_TO_POINTER (TRUE));
207 
208   gimp_prop_coordinates_connect (G_OBJECT (box),
209                                  "width", "height",
210                                  "unit",
211                                  entry, NULL,
212                                  box->xresolution,
213                                  box->yresolution);
214 
215   g_signal_connect (priv->size_chain, "toggled",
216                     G_CALLBACK (gimp_size_box_chain_toggled),
217                     box);
218 
219   gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
220   gtk_widget_show (entry);
221 
222   children = gtk_container_get_children (GTK_CONTAINER (entry));
223   for (list = children; list; list = g_list_next (list))
224     if (GTK_IS_LABEL (list->data))
225       gtk_size_group_add_widget (box->size_group, list->data);
226   g_list_free (children);
227 
228   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
229   gtk_table_attach_defaults (GTK_TABLE (entry), vbox, 1, 3, 2, 3);
230   gtk_widget_show (vbox);
231 
232   label = gtk_label_new (NULL);
233   gimp_label_set_attributes (GTK_LABEL (label),
234                              PANGO_ATTR_SCALE,  PANGO_SCALE_SMALL,
235                              -1);
236   gtk_label_set_xalign (GTK_LABEL (label), 0.0);
237   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
238   gtk_widget_show (label);
239 
240   priv->pixel_label = label;
241 
242   if (box->edit_resolution)
243     {
244       gboolean chain_active;
245 
246       hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
247       gtk_box_pack_start (GTK_BOX (box), hbox, FALSE, FALSE, 0);
248       gtk_widget_show (hbox);
249 
250       chain_active = ABS (box->xresolution -
251                           box->yresolution) < GIMP_MIN_RESOLUTION;
252 
253       entry = gimp_coordinates_new (box->resolution_unit, _("pixels/%a"),
254                                     FALSE, FALSE, SB_WIDTH,
255                                     GIMP_SIZE_ENTRY_UPDATE_RESOLUTION,
256                                     chain_active, FALSE,
257                                     _("_X resolution:"),
258                                     box->xresolution, 1.0,
259                                     1, 1, 1, 10,
260                                     _("_Y resolution:"),
261                                     box->yresolution, 1.0,
262                                     1, 1, 1, 10);
263 
264 
265       gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
266       gtk_widget_show (entry);
267 
268       children = gtk_container_get_children (GTK_CONTAINER (entry));
269       for (list = children; list; list = g_list_next (list))
270         if (GTK_IS_LABEL (list->data))
271           gtk_size_group_add_widget (box->size_group, list->data);
272       g_list_free (children);
273 
274       gimp_prop_coordinates_connect (G_OBJECT (box),
275                                      "xresolution", "yresolution",
276                                      "resolution-unit",
277                                      entry, NULL,
278                                      1.0, 1.0);
279     }
280   else
281     {
282       label = gtk_label_new (NULL);
283       gimp_label_set_attributes (GTK_LABEL (label),
284                                  PANGO_ATTR_SCALE,  PANGO_SCALE_SMALL,
285                                  -1);
286       gtk_label_set_xalign (GTK_LABEL (label), 0.0);
287       gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
288       gtk_widget_show (label);
289 
290       priv->res_label = label;
291     }
292 
293   gimp_size_box_update_size (box);
294   gimp_size_box_update_resolution (box);
295 }
296 
297 static void
gimp_size_box_dispose(GObject * object)298 gimp_size_box_dispose (GObject *object)
299 {
300   GimpSizeBox *box = GIMP_SIZE_BOX (object);
301 
302   if (box->size_group)
303     {
304       g_object_unref (box->size_group);
305       box->size_group = NULL;
306     }
307 
308   G_OBJECT_CLASS (parent_class)->dispose (object);
309 }
310 
311 static void
gimp_size_box_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)312 gimp_size_box_set_property (GObject      *object,
313                             guint         property_id,
314                             const GValue *value,
315                             GParamSpec   *pspec)
316 {
317   GimpSizeBox        *box  = GIMP_SIZE_BOX (object);
318   GimpSizeBoxPrivate *priv = GIMP_SIZE_BOX_GET_PRIVATE (box);
319 
320   switch (property_id)
321     {
322     case PROP_WIDTH:
323       box->width = g_value_get_int (value);
324       gimp_size_box_update_size (box);
325       break;
326 
327     case PROP_HEIGHT:
328       box->height = g_value_get_int (value);
329       gimp_size_box_update_size (box);
330       break;
331 
332     case PROP_UNIT:
333       box->unit = g_value_get_int (value);
334       break;
335 
336     case PROP_XRESOLUTION:
337       box->xresolution = g_value_get_double (value);
338       if (priv->size_entry)
339         gimp_size_entry_set_resolution (priv->size_entry, 0,
340                                         box->xresolution, TRUE);
341       gimp_size_box_update_resolution (box);
342       break;
343 
344     case PROP_YRESOLUTION:
345       box->yresolution = g_value_get_double (value);
346       if (priv->size_entry)
347         gimp_size_entry_set_resolution (priv->size_entry, 1,
348                                         box->yresolution, TRUE);
349       gimp_size_box_update_resolution (box);
350       break;
351 
352     case PROP_RESOLUTION_UNIT:
353       box->resolution_unit = g_value_get_int (value);
354       break;
355 
356     case PROP_KEEP_ASPECT:
357       if (priv->size_chain)
358         gimp_chain_button_set_active (priv->size_chain,
359                                       g_value_get_boolean (value));
360       break;
361 
362     case PROP_EDIT_RESOLUTION:
363       box->edit_resolution = g_value_get_boolean (value);
364       break;
365 
366     default:
367       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
368       break;
369     }
370 }
371 
372 static void
gimp_size_box_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)373 gimp_size_box_get_property (GObject    *object,
374                             guint       property_id,
375                             GValue     *value,
376                             GParamSpec *pspec)
377 {
378   GimpSizeBox        *box  = GIMP_SIZE_BOX (object);
379   GimpSizeBoxPrivate *priv = GIMP_SIZE_BOX_GET_PRIVATE (box);
380 
381   switch (property_id)
382     {
383     case PROP_WIDTH:
384       g_value_set_int (value, box->width);
385       break;
386 
387     case PROP_HEIGHT:
388       g_value_set_int (value, box->height);
389       break;
390 
391     case PROP_UNIT:
392       g_value_set_int (value, box->unit);
393       break;
394 
395     case PROP_XRESOLUTION:
396       g_value_set_double (value, box->xresolution);
397       break;
398 
399     case PROP_YRESOLUTION:
400       g_value_set_double (value, box->yresolution);
401       break;
402 
403     case PROP_RESOLUTION_UNIT:
404       g_value_set_int (value, box->resolution_unit);
405       break;
406 
407     case PROP_KEEP_ASPECT:
408       g_value_set_boolean (value,
409                            gimp_chain_button_get_active (priv->size_chain));
410       break;
411 
412     case PROP_EDIT_RESOLUTION:
413       g_value_set_boolean (value, box->edit_resolution);
414       break;
415 
416     default:
417       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
418       break;
419     }
420 }
421 
422 static void
gimp_size_box_update_size(GimpSizeBox * box)423 gimp_size_box_update_size (GimpSizeBox *box)
424 {
425   GimpSizeBoxPrivate *priv = GIMP_SIZE_BOX_GET_PRIVATE (box);
426 
427   if (priv->pixel_label)
428     {
429       gchar *text = g_strdup_printf (ngettext ("%d × %d pixel",
430                                                "%d × %d pixels", box->height),
431                                      box->width, box->height);
432       gtk_label_set_text (GTK_LABEL (priv->pixel_label), text);
433       g_free (text);
434     }
435 }
436 
437 static void
gimp_size_box_update_resolution(GimpSizeBox * box)438 gimp_size_box_update_resolution (GimpSizeBox *box)
439 {
440   GimpSizeBoxPrivate *priv = GIMP_SIZE_BOX_GET_PRIVATE (box);
441 
442   if (priv->size_entry)
443     {
444       gimp_size_entry_set_refval (priv->size_entry, 0, box->width);
445       gimp_size_entry_set_refval (priv->size_entry, 1, box->height);
446     }
447 
448   if (priv->res_label)
449     {
450       gchar *text;
451       gint   xres = ROUND (box->xresolution);
452       gint   yres = ROUND (box->yresolution);
453 
454       if (xres != yres)
455         text = g_strdup_printf (_("%d × %d ppi"), xres, yres);
456       else
457         text = g_strdup_printf (_("%d ppi"), yres);
458 
459       gtk_label_set_text (GTK_LABEL (priv->res_label), text);
460       g_free (text);
461     }
462 }
463 
464 static void
gimp_size_box_chain_toggled(GimpChainButton * button,GimpSizeBox * box)465 gimp_size_box_chain_toggled (GimpChainButton *button,
466                              GimpSizeBox     *box)
467 {
468   g_object_set (box,
469                 "keep-aspect", gimp_chain_button_get_active (button),
470                 NULL);
471 }
472