1 /* GIMP LiquidRescale Plug-in
2  * Copyright (C) 2007-2010 Carlo Baldassi (the "Author") <carlobaldassi@gmail.com>.
3  * All Rights Reserved.
4  *
5  * The code in this file is taken from gimpwidgets.c
6  * Copyright (C) 2000 Michael Natterer <mitch@gimp.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the Licence, or
11  * (at your option) any later version.
12 
13  * This program 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
16  * GNU General Public License for more details.
17 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, see <http://www.gnu.org.licences/>.
20  */
21 
22 #include <libgimp/gimp.h>
23 #include <libgimp/gimpui.h>
24 
25 #include "altsizeentry.h"
26 #include "altcoordinates.h"
27 
28 typedef struct
29 {
30   GimpChainButton *chainbutton;
31   gboolean         chain_constrains_ratio;
32   gdouble          orig_x;
33   gdouble          orig_y;
34   gdouble          last_x;
35   gdouble          last_y;
36 } AltCoordinatesData;
37 
38 
39 static void alt_coordinates_callback (GtkWidget *widget, AltCoordinatesData *data);
40 static void alt_coordinates_data_free (AltCoordinatesData *data);
41 static void alt_coordinates_chainbutton_toggled (GimpChainButton *button, AltSizeEntry   *entry);
42 
43 
44 static void
alt_coordinates_callback(GtkWidget * widget,AltCoordinatesData * data)45 alt_coordinates_callback (GtkWidget           *widget,
46                            AltCoordinatesData *data)
47 {
48   gdouble new_x;
49   gdouble new_y;
50 
51   new_x = alt_size_entry_get_refval (ALT_SIZE_ENTRY (widget), 0);
52   new_y = alt_size_entry_get_refval (ALT_SIZE_ENTRY (widget), 1);
53 
54   if (gimp_chain_button_get_active (data->chainbutton))
55     {
56       if (data->chain_constrains_ratio)
57         {
58           if ((data->orig_x != 0) && (data->orig_y != 0))
59             {
60               if (ROUND (new_x) != ROUND (data->last_x))
61                 {
62                   data->last_x = new_x;
63                   new_y = (new_x * data->orig_y) / data->orig_x;
64 
65                   alt_size_entry_set_refval (ALT_SIZE_ENTRY (widget), 1,
66                                               new_y);
67                   data->last_y
68                     = alt_size_entry_get_refval (ALT_SIZE_ENTRY (widget), 1);
69                 }
70               else if (ROUND (new_y) != ROUND (data->last_y))
71                 {
72                   data->last_y = new_y;
73                   new_x = (new_y * data->orig_x) / data->orig_y;
74 
75                   alt_size_entry_set_refval (ALT_SIZE_ENTRY (widget), 0,
76                                               new_x);
77                   data->last_x
78                     = alt_size_entry_get_refval (ALT_SIZE_ENTRY (widget), 0);
79                 }
80             }
81         }
82       else
83         {
84           if (new_x != data->last_x)
85             {
86               new_y = new_x;
87 
88               alt_size_entry_set_refval (ALT_SIZE_ENTRY (widget), 1, new_x);
89               data->last_y = data->last_x
90                 = alt_size_entry_get_refval (ALT_SIZE_ENTRY (widget), 1);
91             }
92           else if (new_y != data->last_y)
93             {
94               new_x = new_y;
95 
96               alt_size_entry_set_refval (ALT_SIZE_ENTRY (widget), 0, new_y);
97               data->last_x = data->last_y
98                 = alt_size_entry_get_refval (ALT_SIZE_ENTRY (widget), 0);
99             }
100         }
101     }
102   else
103     {
104       if (new_x != data->last_x)
105         data->last_x = new_x;
106       if (new_y != data->last_y)
107         data->last_y = new_y;
108     }
109 }
110 
111 static void
alt_coordinates_data_free(AltCoordinatesData * data)112 alt_coordinates_data_free (AltCoordinatesData *data)
113 {
114   g_slice_free (AltCoordinatesData, data);
115 }
116 
117 static void
alt_coordinates_chainbutton_toggled(GimpChainButton * button,AltSizeEntry * entry)118 alt_coordinates_chainbutton_toggled (GimpChainButton *button,
119                                       AltSizeEntry   *entry)
120 {
121   if (gimp_chain_button_get_active (button))
122     {
123       AltCoordinatesData *data;
124 
125       data = g_object_get_data (G_OBJECT (entry), "alt-coordinates-data");
126 
127       data->orig_x = alt_size_entry_get_refval (entry, 0);
128       data->orig_y = alt_size_entry_get_refval (entry, 1);
129     }
130 }
131 
132 /**
133  * gimp_coordinates_new:
134  * @unit:                   The initial unit of the #GimpUnitMenu.
135  * @unit_format:            A printf-like unit-format string as is used with
136  *                          gimp_unit_menu_new().
137  * @menu_show_pixels:       %TRUE if the #GimpUnitMenu should contain an item
138  *                          for GIMP_UNIT_PIXEL.
139  * @menu_show_percent:      %TRUE if the #GimpUnitMenu should contain an item
140  *                          for GIMP_UNIT_PERCENT.
141  * @spinbutton_width:       The horizontal size of the #AltSizeEntry's
142  *                           #GtkSpinButton's.
143  * @update_policy:          The update policy for the #AltSizeEntry.
144  * @chainbutton_active:     %TRUE if the attached #GimpChainButton should be
145  *                          active.
146  * @chain_constrains_ratio: %TRUE if the chainbutton should constrain the
147  *                          fields' aspect ratio. If %FALSE, the values will
148  *                          be constrained.
149  * @xlabel:                 The label for the X coordinate.
150  * @x:                      The initial value of the X coordinate.
151  * @xres:                   The horizontal resolution in DPI.
152  * @lower_boundary_x:       The lower boundary of the X coordinate.
153  * @upper_boundary_x:       The upper boundary of the X coordinate.
154  * @xsize_0:                The X value which will be treated as 0%.
155  * @xsize_100:              The X value which will be treated as 100%.
156  * @ylabel:                 The label for the Y coordinate.
157  * @y:                      The initial value of the Y coordinate.
158  * @yres:                   The vertical resolution in DPI.
159  * @lower_boundary_y:       The lower boundary of the Y coordinate.
160  * @upper_boundary_y:       The upper boundary of the Y coordinate.
161  * @ysize_0:                The Y value which will be treated as 0%.
162  * @ysize_100:              The Y value which will be treated as 100%.
163  *
164  * Convenience function that creates a #AltSizeEntry with two fields for x/y
165  * coordinates/sizes with a #GimpChainButton attached to constrain either the
166  * two fields' values or the ratio between them.
167  *
168  * Returns: The new #AltSizeEntry.
169  **/
170 GtkWidget *
alt_coordinates_new(GimpUnit unit,const gchar * unit_format,gboolean menu_show_pixels,gboolean menu_show_percent,gint spinbutton_width,AltSizeEntryUpdatePolicy update_policy,gboolean chainbutton_active,gboolean chain_constrains_ratio,const gchar * xlabel,gdouble x,gdouble xres,gdouble lower_boundary_x,gdouble upper_boundary_x,gdouble xsize_0,gdouble xsize_100,const gchar * ylabel,gdouble y,gdouble yres,gdouble lower_boundary_y,gdouble upper_boundary_y,gdouble ysize_0,gdouble ysize_100)171 alt_coordinates_new (GimpUnit         unit,
172                       const gchar     *unit_format,
173                       gboolean         menu_show_pixels,
174                       gboolean         menu_show_percent,
175                       gint             spinbutton_width,
176                       AltSizeEntryUpdatePolicy  update_policy,
177 
178                       gboolean         chainbutton_active,
179                       gboolean         chain_constrains_ratio,
180 
181                       const gchar     *xlabel,
182                       gdouble          x,
183                       gdouble          xres,
184                       gdouble          lower_boundary_x,
185                       gdouble          upper_boundary_x,
186                       gdouble          xsize_0,   /* % */
187                       gdouble          xsize_100, /* % */
188 
189                       const gchar     *ylabel,
190                       gdouble          y,
191                       gdouble          yres,
192                       gdouble          lower_boundary_y,
193                       gdouble          upper_boundary_y,
194                       gdouble          ysize_0,   /* % */
195                       gdouble          ysize_100  /* % */)
196 {
197   AltCoordinatesData *data;
198   GtkObject           *adjustment;
199   GtkWidget           *spinbutton;
200   GtkWidget           *sizeentry;
201   GtkWidget           *chainbutton;
202 
203   spinbutton = gimp_spin_button_new (&adjustment, 1, 0, 1, 1, 10, 0, 1, 2);
204 
205   if (spinbutton_width > 0)
206     {
207       if (spinbutton_width < 17)
208         gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), spinbutton_width);
209       else
210         gtk_widget_set_size_request (spinbutton, spinbutton_width, -1);
211     }
212 
213   sizeentry = alt_size_entry_new (1, unit, unit_format,
214                                    menu_show_pixels,
215                                    menu_show_percent,
216                                    FALSE,
217                                    spinbutton_width,
218                                    update_policy);
219   gtk_table_set_col_spacing (GTK_TABLE (sizeentry), 0, 4);
220   gtk_table_set_col_spacing (GTK_TABLE (sizeentry), 2, 4);
221   alt_size_entry_add_field (ALT_SIZE_ENTRY (sizeentry),
222                              GTK_SPIN_BUTTON (spinbutton), NULL);
223   gtk_table_attach_defaults (GTK_TABLE (sizeentry), spinbutton, 1, 2, 0, 1);
224   gtk_widget_show (spinbutton);
225 
226   alt_size_entry_set_unit (ALT_SIZE_ENTRY (sizeentry),
227                             (update_policy == ALT_SIZE_ENTRY_UPDATE_RESOLUTION) ||
228                             (menu_show_pixels == FALSE) ?
229                             GIMP_UNIT_INCH : GIMP_UNIT_PIXEL);
230 
231   alt_size_entry_set_resolution (ALT_SIZE_ENTRY (sizeentry), 0, xres, TRUE);
232   alt_size_entry_set_resolution (ALT_SIZE_ENTRY (sizeentry), 1, yres, TRUE);
233   alt_size_entry_set_refval_boundaries (ALT_SIZE_ENTRY (sizeentry), 0,
234                                          lower_boundary_x, upper_boundary_x);
235   alt_size_entry_set_refval_boundaries (ALT_SIZE_ENTRY (sizeentry), 1,
236                                          lower_boundary_y, upper_boundary_y);
237 
238   if (menu_show_percent)
239     {
240       alt_size_entry_set_size (ALT_SIZE_ENTRY (sizeentry), 0,
241                                 xsize_0, xsize_100);
242       alt_size_entry_set_size (ALT_SIZE_ENTRY (sizeentry), 1,
243                                 ysize_0, ysize_100);
244     }
245 
246   alt_size_entry_set_refval (ALT_SIZE_ENTRY (sizeentry), 0, x);
247   alt_size_entry_set_refval (ALT_SIZE_ENTRY (sizeentry), 1, y);
248 
249   alt_size_entry_attach_label (ALT_SIZE_ENTRY (sizeentry),
250                                 xlabel, 0, 0, 0.0);
251   alt_size_entry_attach_label (ALT_SIZE_ENTRY (sizeentry),
252                                 ylabel, 1, 0, 0.0);
253 
254   chainbutton = gimp_chain_button_new (GIMP_CHAIN_RIGHT);
255 
256   if (chainbutton_active)
257     gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chainbutton), TRUE);
258 
259   gtk_table_attach (GTK_TABLE (sizeentry), chainbutton, 2, 3, 0, 2,
260                     GTK_SHRINK | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
261   gtk_widget_show (chainbutton);
262 
263   data = g_slice_new (AltCoordinatesData);
264 
265   data->chainbutton            = GIMP_CHAIN_BUTTON (chainbutton);
266   data->chain_constrains_ratio = chain_constrains_ratio;
267   data->orig_x                 = x;
268   data->orig_y                 = y;
269   data->last_x                 = x;
270   data->last_y                 = y;
271 
272   g_object_set_data_full (G_OBJECT (sizeentry), "alt-coordinates-data",
273                           data,
274                           (GDestroyNotify) alt_coordinates_data_free);
275 
276   g_signal_connect (sizeentry, "value-changed",
277                     G_CALLBACK (alt_coordinates_callback),
278                     data);
279 
280   g_object_set_data (G_OBJECT (sizeentry), "chainbutton", chainbutton);
281 
282   g_signal_connect (chainbutton, "toggled",
283                     G_CALLBACK (alt_coordinates_chainbutton_toggled),
284                     sizeentry);
285 
286   return sizeentry;
287 }
288 
289