1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include <gegl.h>
21 #include <gtk/gtk.h>
22 
23 #include "libgimpmath/gimpmath.h"
24 #include "libgimpwidgets/gimpwidgets.h"
25 
26 #include "display-types.h"
27 
28 #include "core/gimp.h"
29 #include "core/gimpviewable.h"
30 
31 #include "widgets/gimphelp-ids.h"
32 #include "widgets/gimpviewabledialog.h"
33 
34 #include "gimpdisplay.h"
35 #include "gimpdisplayshell.h"
36 #include "gimpdisplayshell-scale.h"
37 #include "gimpdisplayshell-scale-dialog.h"
38 
39 #include "gimp-intl.h"
40 
41 
42 #define SCALE_EPSILON      0.0001
43 #define SCALE_EQUALS(a,b) (fabs ((a) - (b)) < SCALE_EPSILON)
44 
45 
46 typedef struct
47 {
48   GimpDisplayShell *shell;
49   GimpZoomModel    *model;
50   GtkAdjustment    *scale_adj;
51   GtkAdjustment    *num_adj;
52   GtkAdjustment    *denom_adj;
53 } ScaleDialogData;
54 
55 
56 /*  local function prototypes  */
57 
58 static void  gimp_display_shell_scale_dialog_response (GtkWidget        *widget,
59                                                        gint              response_id,
60                                                        ScaleDialogData  *dialog);
61 static void  gimp_display_shell_scale_dialog_free     (ScaleDialogData  *dialog);
62 
63 static void  update_zoom_values                       (GtkAdjustment    *adj,
64                                                        ScaleDialogData  *dialog);
65 
66 
67 
68 /*  public functions  */
69 
70 /**
71  * gimp_display_shell_scale_dialog:
72  * @shell: the #GimpDisplayShell
73  *
74  * Constructs and displays a dialog allowing the user to enter a
75  * custom display scale.
76  **/
77 void
gimp_display_shell_scale_dialog(GimpDisplayShell * shell)78 gimp_display_shell_scale_dialog (GimpDisplayShell *shell)
79 {
80   ScaleDialogData *data;
81   GimpImage       *image;
82   GtkWidget       *toplevel;
83   GtkWidget       *hbox;
84   GtkWidget       *table;
85   GtkWidget       *spin;
86   GtkWidget       *label;
87   gint             num, denom, row;
88 
89   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
90 
91   if (shell->scale_dialog)
92     {
93       gtk_window_present (GTK_WINDOW (shell->scale_dialog));
94       return;
95     }
96 
97   if (SCALE_EQUALS (shell->other_scale, 0.0))
98     {
99       /* other_scale not yet initialized */
100       shell->other_scale = gimp_zoom_model_get_factor (shell->zoom);
101     }
102 
103   image = gimp_display_get_image (shell->display);
104 
105   data = g_slice_new (ScaleDialogData);
106 
107   data->shell = shell;
108   data->model = g_object_new (GIMP_TYPE_ZOOM_MODEL,
109                               "value", fabs (shell->other_scale),
110                               NULL);
111 
112   shell->scale_dialog =
113     gimp_viewable_dialog_new (GIMP_VIEWABLE (image),
114                               gimp_get_user_context (shell->display->gimp),
115                               _("Zoom Ratio"), "display_scale",
116                               "zoom-original",
117                               _("Select Zoom Ratio"),
118                               GTK_WIDGET (shell),
119                               gimp_standard_help_func,
120                               GIMP_HELP_VIEW_ZOOM_OTHER,
121 
122                               _("_Cancel"), GTK_RESPONSE_CANCEL,
123                               _("_OK"),     GTK_RESPONSE_OK,
124 
125                               NULL);
126 
127   gtk_dialog_set_alternative_button_order (GTK_DIALOG (shell->scale_dialog),
128                                            GTK_RESPONSE_OK,
129                                            GTK_RESPONSE_CANCEL,
130                                            -1);
131 
132   g_object_weak_ref (G_OBJECT (shell->scale_dialog),
133                      (GWeakNotify) gimp_display_shell_scale_dialog_free, data);
134   g_object_weak_ref (G_OBJECT (shell->scale_dialog),
135                      (GWeakNotify) g_object_unref, data->model);
136 
137   g_object_add_weak_pointer (G_OBJECT (shell->scale_dialog),
138                              (gpointer) &shell->scale_dialog);
139 
140   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (shell));
141 
142   gtk_window_set_transient_for (GTK_WINDOW (shell->scale_dialog),
143                                 GTK_WINDOW (toplevel));
144   gtk_window_set_destroy_with_parent (GTK_WINDOW (shell->scale_dialog), TRUE);
145 
146   g_signal_connect (shell->scale_dialog, "response",
147                     G_CALLBACK (gimp_display_shell_scale_dialog_response),
148                     data);
149 
150   table = gtk_table_new (2, 2, FALSE);
151   gtk_container_set_border_width (GTK_CONTAINER (table), 12);
152   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
153   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
154   gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (shell->scale_dialog))),
155                       table, TRUE, TRUE, 0);
156   gtk_widget_show (table);
157 
158   row = 0;
159 
160   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
161   gimp_table_attach_aligned (GTK_TABLE (table), 0, row++,
162                              _("Zoom ratio:"), 0.0, 0.5,
163                              hbox, 1, FALSE);
164 
165   gimp_zoom_model_get_fraction (data->model, &num, &denom);
166 
167   data->num_adj = (GtkAdjustment *)
168     gtk_adjustment_new (num, 1, 256, 1, 8, 0);
169   spin = gimp_spin_button_new (data->num_adj, 1.0, 0);
170   gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spin), TRUE);
171   gtk_entry_set_activates_default (GTK_ENTRY (spin), TRUE);
172   gtk_box_pack_start (GTK_BOX (hbox), spin, TRUE, TRUE, 0);
173   gtk_widget_show (spin);
174 
175   label = gtk_label_new (":");
176   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
177   gtk_widget_show (label);
178 
179   data->denom_adj = (GtkAdjustment *)
180     gtk_adjustment_new (denom, 1, 256, 1, 8, 0);
181   spin = gimp_spin_button_new (data->denom_adj, 1.0, 0);
182   gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spin), TRUE);
183   gtk_entry_set_activates_default (GTK_ENTRY (spin), TRUE);
184   gtk_box_pack_start (GTK_BOX (hbox), spin, TRUE, TRUE, 0);
185   gtk_widget_show (spin);
186 
187   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
188   gimp_table_attach_aligned (GTK_TABLE (table), 0, row++,
189                              _("Zoom:"), 0.0, 0.5,
190                              hbox, 1, FALSE);
191 
192   data->scale_adj = (GtkAdjustment *)
193     gtk_adjustment_new (fabs (shell->other_scale) * 100,
194                         100.0 / 256.0, 25600.0,
195                         10, 50, 0);
196   spin = gimp_spin_button_new (data->scale_adj, 1.0, 2);
197   gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spin), TRUE);
198   gtk_entry_set_activates_default (GTK_ENTRY (spin), TRUE);
199   gtk_box_pack_start (GTK_BOX (hbox), spin, TRUE, TRUE, 0);
200   gtk_widget_show (spin);
201 
202   label = gtk_label_new ("%");
203   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
204   gtk_widget_show (label);
205 
206   g_signal_connect (data->scale_adj, "value-changed",
207                     G_CALLBACK (update_zoom_values), data);
208   g_signal_connect (data->num_adj, "value-changed",
209                     G_CALLBACK (update_zoom_values), data);
210   g_signal_connect (data->denom_adj, "value-changed",
211                     G_CALLBACK (update_zoom_values), data);
212 
213   gtk_widget_show (shell->scale_dialog);
214 }
215 
216 static void
gimp_display_shell_scale_dialog_response(GtkWidget * widget,gint response_id,ScaleDialogData * dialog)217 gimp_display_shell_scale_dialog_response (GtkWidget       *widget,
218                                           gint             response_id,
219                                           ScaleDialogData *dialog)
220 {
221   if (response_id == GTK_RESPONSE_OK)
222     {
223       gdouble scale;
224 
225       scale = gtk_adjustment_get_value (dialog->scale_adj);
226 
227       gimp_display_shell_scale (dialog->shell,
228                                 GIMP_ZOOM_TO,
229                                 scale / 100.0,
230                                 GIMP_ZOOM_FOCUS_BEST_GUESS);
231     }
232   else
233     {
234       /*  need to emit "scaled" to get the menu updated  */
235       gimp_display_shell_scaled (dialog->shell);
236     }
237 
238   dialog->shell->other_scale = - fabs (dialog->shell->other_scale);
239 
240   gtk_widget_destroy (dialog->shell->scale_dialog);
241 }
242 
243 static void
gimp_display_shell_scale_dialog_free(ScaleDialogData * dialog)244 gimp_display_shell_scale_dialog_free (ScaleDialogData *dialog)
245 {
246   g_slice_free (ScaleDialogData, dialog);
247 }
248 
249 static void
update_zoom_values(GtkAdjustment * adj,ScaleDialogData * dialog)250 update_zoom_values (GtkAdjustment   *adj,
251                     ScaleDialogData *dialog)
252 {
253   gint    num, denom;
254   gdouble scale;
255 
256   g_signal_handlers_block_by_func (dialog->scale_adj,
257                                    update_zoom_values,
258                                    dialog);
259   g_signal_handlers_block_by_func (dialog->num_adj,
260                                    update_zoom_values,
261                                    dialog);
262   g_signal_handlers_block_by_func (dialog->denom_adj,
263                                    update_zoom_values,
264                                    dialog);
265 
266   if (adj == dialog->scale_adj)
267     {
268       scale = gtk_adjustment_get_value (dialog->scale_adj);
269 
270       gimp_zoom_model_zoom (dialog->model, GIMP_ZOOM_TO, scale / 100.0);
271       gimp_zoom_model_get_fraction (dialog->model, &num, &denom);
272 
273       gtk_adjustment_set_value (dialog->num_adj, num);
274       gtk_adjustment_set_value (dialog->denom_adj, denom);
275     }
276   else /* fraction adjustments */
277     {
278       scale = (gtk_adjustment_get_value (dialog->num_adj) /
279                gtk_adjustment_get_value (dialog->denom_adj));
280 
281       gtk_adjustment_set_value (dialog->scale_adj, scale * 100);
282     }
283 
284   g_signal_handlers_unblock_by_func (dialog->scale_adj,
285                                      update_zoom_values,
286                                      dialog);
287   g_signal_handlers_unblock_by_func (dialog->num_adj,
288                                      update_zoom_values,
289                                      dialog);
290   g_signal_handlers_unblock_by_func (dialog->denom_adj,
291                                      update_zoom_values,
292                                      dialog);
293 }
294