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 <string.h>
21 
22 #include <gegl.h>
23 #include <gtk/gtk.h>
24 
25 #include "libgimpmath/gimpmath.h"
26 #include "libgimpwidgets/gimpwidgets.h"
27 
28 #include "tools-types.h"
29 
30 #include "core/gimpchannel.h"
31 #include "core/gimpdrawable.h"
32 #include "core/gimpdrawablefilter.h"
33 #include "core/gimpimage.h"
34 #include "core/gimplayer.h"
35 #include "core/gimplayermask.h"
36 
37 #include "widgets/gimphelp-ids.h"
38 
39 #include "display/gimpdisplay.h"
40 #include "display/gimptoolgui.h"
41 
42 #include "gimpoffsettool.h"
43 #include "gimpfilteroptions.h"
44 #include "gimptoolcontrol.h"
45 
46 #include "gimp-intl.h"
47 
48 
49 static gboolean   gimp_offset_tool_initialize            (GimpTool              *tool,
50                                                           GimpDisplay           *display,
51                                                           GError               **error);
52 static void       gimp_offset_tool_control               (GimpTool              *tool,
53                                                           GimpToolAction         action,
54                                                           GimpDisplay           *display);
55 static void       gimp_offset_tool_button_press          (GimpTool              *tool,
56                                                           const GimpCoords      *coords,
57                                                           guint32                time,
58                                                           GdkModifierType        state,
59                                                           GimpButtonPressType    press_type,
60                                                           GimpDisplay           *display);
61 static void       gimp_offset_tool_button_release        (GimpTool              *tool,
62                                                           const GimpCoords      *coords,
63                                                           guint32                time,
64                                                           GdkModifierType        state,
65                                                           GimpButtonReleaseType  release_type,
66                                                           GimpDisplay           *display);
67 static void       gimp_offset_tool_motion                (GimpTool              *tool,
68                                                           const GimpCoords      *coords,
69                                                           guint32                time,
70                                                           GdkModifierType        state,
71                                                           GimpDisplay           *display);
72 static void       gimp_offset_tool_oper_update           (GimpTool              *tool,
73                                                           const GimpCoords      *coords,
74                                                           GdkModifierType        state,
75                                                           gboolean               proximity,
76                                                           GimpDisplay           *display);
77 static void       gimp_offset_tool_cursor_update         (GimpTool              *tool,
78                                                           const GimpCoords      *coords,
79                                                           GdkModifierType        state,
80                                                           GimpDisplay           *display);
81 
82 static gchar    * gimp_offset_tool_get_operation         (GimpFilterTool        *filter_tool,
83                                                           gchar                **description);
84 static void       gimp_offset_tool_dialog                (GimpFilterTool        *filter_tool);
85 static void       gimp_offset_tool_config_notify         (GimpFilterTool        *filter_tool,
86                                                           GimpConfig            *config,
87                                                           const GParamSpec      *pspec);
88 static void       gimp_offset_tool_region_changed        (GimpFilterTool        *filter_tool);
89 
90 static void       gimp_offset_tool_offset_changed        (GimpSizeEntry         *se,
91                                                           GimpOffsetTool        *offset_tool);
92 
93 static void       gimp_offset_tool_half_xy_clicked       (GtkButton             *button,
94                                                           GimpOffsetTool        *offset_tool);
95 static void       gimp_offset_tool_half_x_clicked        (GtkButton             *button,
96                                                           GimpOffsetTool        *offset_tool);
97 static void       gimp_offset_tool_half_y_clicked        (GtkButton             *button,
98                                                           GimpOffsetTool        *offset_tool);
99 
100 static void       gimp_offset_tool_edge_behavior_toggled (GtkToggleButton       *toggle,
101                                                           GimpOffsetTool        *offset_tool);
102 
103 static void       gimp_offset_tool_background_changed    (GimpContext           *context,
104                                                           const GimpRGB         *color,
105                                                           GimpOffsetTool        *offset_tool);
106 
107 static gint       gimp_offset_tool_get_width             (GimpOffsetTool        *offset_tool);
108 static gint       gimp_offset_tool_get_height            (GimpOffsetTool        *offset_tool);
109 
110 static void       gimp_offset_tool_update                (GimpOffsetTool        *offset_tool);
111 
112 static void       gimp_offset_tool_halt                  (GimpOffsetTool        *offset_tool);
113 
114 
G_DEFINE_TYPE(GimpOffsetTool,gimp_offset_tool,GIMP_TYPE_FILTER_TOOL)115 G_DEFINE_TYPE (GimpOffsetTool, gimp_offset_tool,
116                GIMP_TYPE_FILTER_TOOL)
117 
118 #define parent_class gimp_offset_tool_parent_class
119 
120 
121 void
122 gimp_offset_tool_register (GimpToolRegisterCallback callback,
123                            gpointer                 data)
124 {
125   (* callback) (GIMP_TYPE_OFFSET_TOOL,
126                 GIMP_TYPE_FILTER_OPTIONS, NULL,
127                 GIMP_CONTEXT_PROP_MASK_BACKGROUND,
128                 "gimp-offset-tool",
129                 _("Offset"),
130                 _("Shift the pixels, optionally wrapping them at the borders"),
131                 N_("_Offset..."), NULL,
132                 NULL, GIMP_HELP_TOOL_OFFSET,
133                 GIMP_ICON_TOOL_OFFSET,
134                 data);
135 }
136 
137 static void
gimp_offset_tool_class_init(GimpOffsetToolClass * klass)138 gimp_offset_tool_class_init (GimpOffsetToolClass *klass)
139 {
140   GimpToolClass       *tool_class        = GIMP_TOOL_CLASS (klass);
141   GimpFilterToolClass *filter_tool_class = GIMP_FILTER_TOOL_CLASS (klass);
142 
143   tool_class->initialize            = gimp_offset_tool_initialize;
144   tool_class->control               = gimp_offset_tool_control;
145   tool_class->button_press          = gimp_offset_tool_button_press;
146   tool_class->button_release        = gimp_offset_tool_button_release;
147   tool_class->motion                = gimp_offset_tool_motion;
148   tool_class->oper_update           = gimp_offset_tool_oper_update;
149   tool_class->cursor_update         = gimp_offset_tool_cursor_update;
150 
151   filter_tool_class->get_operation  = gimp_offset_tool_get_operation;
152   filter_tool_class->dialog         = gimp_offset_tool_dialog;
153   filter_tool_class->config_notify  = gimp_offset_tool_config_notify;
154   filter_tool_class->region_changed = gimp_offset_tool_region_changed;
155 }
156 
157 static void
gimp_offset_tool_init(GimpOffsetTool * offset_tool)158 gimp_offset_tool_init (GimpOffsetTool *offset_tool)
159 {
160   GimpTool *tool = GIMP_TOOL (offset_tool);
161 
162   gimp_tool_control_set_scroll_lock (tool->control, TRUE);
163   gimp_tool_control_set_precision   (tool->control,
164                                      GIMP_CURSOR_PRECISION_PIXEL_CENTER);
165 }
166 
167 static gboolean
gimp_offset_tool_initialize(GimpTool * tool,GimpDisplay * display,GError ** error)168 gimp_offset_tool_initialize (GimpTool     *tool,
169                              GimpDisplay  *display,
170                              GError      **error)
171 {
172   GimpFilterTool *filter_tool = GIMP_FILTER_TOOL (tool);
173   GimpOffsetTool *offset_tool = GIMP_OFFSET_TOOL (tool);
174   GimpContext    *context     = GIMP_CONTEXT (GIMP_TOOL_GET_OPTIONS (tool));
175   GimpImage      *image;
176   gdouble         xres;
177   gdouble         yres;
178 
179   if (! GIMP_TOOL_CLASS (parent_class)->initialize (tool, display, error))
180     return FALSE;
181 
182   image = gimp_item_get_image (GIMP_ITEM (tool->drawable));
183 
184   gimp_image_get_resolution (image, &xres, &yres);
185 
186   g_signal_handlers_block_by_func (offset_tool->offset_se,
187                                    gimp_offset_tool_offset_changed,
188                                    offset_tool);
189 
190   gimp_size_entry_set_resolution (
191     GIMP_SIZE_ENTRY (offset_tool->offset_se), 0,
192     xres, FALSE);
193   gimp_size_entry_set_resolution (
194     GIMP_SIZE_ENTRY (offset_tool->offset_se), 1,
195     yres, FALSE);
196 
197   if (GIMP_IS_LAYER (tool->drawable))
198     gimp_tool_gui_set_description (filter_tool->gui, _("Offset Layer"));
199   else if (GIMP_IS_LAYER_MASK (tool->drawable))
200     gimp_tool_gui_set_description (filter_tool->gui, _("Offset Layer Mask"));
201   else if (GIMP_IS_CHANNEL (tool->drawable))
202     gimp_tool_gui_set_description (filter_tool->gui, _("Offset Channel"));
203   else
204     g_warning ("%s: unexpected drawable type", G_STRFUNC);
205 
206   gtk_widget_set_sensitive (offset_tool->transparent_radio,
207                             gimp_drawable_has_alpha (tool->drawable));
208 
209   g_signal_handlers_unblock_by_func (offset_tool->offset_se,
210                                      gimp_offset_tool_offset_changed,
211                                      offset_tool);
212 
213   gegl_node_set (
214     filter_tool->operation,
215     "context", context,
216     NULL);
217 
218   g_signal_connect (context, "background-changed",
219                     G_CALLBACK (gimp_offset_tool_background_changed),
220                     offset_tool);
221 
222   gimp_offset_tool_update (offset_tool);
223 
224   return TRUE;
225 }
226 
227 static void
gimp_offset_tool_control(GimpTool * tool,GimpToolAction action,GimpDisplay * display)228 gimp_offset_tool_control (GimpTool       *tool,
229                           GimpToolAction  action,
230                           GimpDisplay    *display)
231 {
232   GimpOffsetTool *offset_tool = GIMP_OFFSET_TOOL (tool);
233 
234   switch (action)
235     {
236     case GIMP_TOOL_ACTION_PAUSE:
237     case GIMP_TOOL_ACTION_RESUME:
238       break;
239 
240     case GIMP_TOOL_ACTION_HALT:
241       gimp_offset_tool_halt (offset_tool);
242       break;
243 
244     case GIMP_TOOL_ACTION_COMMIT:
245       break;
246     }
247 
248   GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
249 }
250 
251 static gchar *
gimp_offset_tool_get_operation(GimpFilterTool * filter_tool,gchar ** description)252 gimp_offset_tool_get_operation (GimpFilterTool  *filter_tool,
253                                 gchar          **description)
254 {
255   return g_strdup ("gimp:offset");
256 }
257 
258 static void
gimp_offset_tool_button_press(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpButtonPressType press_type,GimpDisplay * display)259 gimp_offset_tool_button_press (GimpTool            *tool,
260                                const GimpCoords    *coords,
261                                guint32              time,
262                                GdkModifierType      state,
263                                GimpButtonPressType  press_type,
264                                GimpDisplay         *display)
265 {
266   GimpOffsetTool *offset_tool = GIMP_OFFSET_TOOL (tool);
267 
268   offset_tool->dragging = ! gimp_filter_tool_on_guide (GIMP_FILTER_TOOL (tool),
269                                                        coords, display);
270 
271   if (! offset_tool->dragging)
272     {
273       GIMP_TOOL_CLASS (parent_class)->button_press (tool, coords, time, state,
274                                                     press_type, display);
275     }
276   else
277     {
278       offset_tool->x = coords->x;
279       offset_tool->y = coords->y;
280 
281       g_object_get (GIMP_FILTER_TOOL (tool)->config,
282                     "x", &offset_tool->offset_x,
283                     "y", &offset_tool->offset_y,
284                     NULL);
285 
286       tool->display = display;
287 
288       gimp_tool_control_activate (tool->control);
289 
290       gimp_tool_pop_status (tool, display);
291 
292       gimp_tool_push_status_coords (tool, display,
293                                     GIMP_CURSOR_PRECISION_PIXEL_CENTER,
294                                     _("Offset: "),
295                                     0,
296                                     ", ",
297                                     0,
298                                     NULL);
299     }
300 }
301 
302 static void
gimp_offset_tool_button_release(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpButtonReleaseType release_type,GimpDisplay * display)303 gimp_offset_tool_button_release (GimpTool              *tool,
304                                  const GimpCoords      *coords,
305                                  guint32                time,
306                                  GdkModifierType        state,
307                                  GimpButtonReleaseType  release_type,
308                                  GimpDisplay           *display)
309 {
310   GimpOffsetTool *offset_tool = GIMP_OFFSET_TOOL (tool);
311 
312   if (! offset_tool->dragging)
313     {
314       GIMP_TOOL_CLASS (parent_class)->button_release (tool, coords, time, state,
315                                                       release_type, display);
316     }
317   else
318     {
319       gimp_tool_control_halt (tool->control);
320 
321       offset_tool->dragging = FALSE;
322 
323       if (release_type == GIMP_BUTTON_RELEASE_CANCEL)
324         {
325           g_object_set (GIMP_FILTER_TOOL (tool)->config,
326                         "x", offset_tool->offset_x,
327                         "y", offset_tool->offset_y,
328                         NULL);
329         }
330     }
331 }
332 
333 static void
gimp_offset_tool_motion(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpDisplay * display)334 gimp_offset_tool_motion (GimpTool         *tool,
335                          const GimpCoords *coords,
336                          guint32           time,
337                          GdkModifierType   state,
338                          GimpDisplay      *display)
339 {
340   GimpFilterTool *filter_tool = GIMP_FILTER_TOOL (tool);
341   GimpOffsetTool *offset_tool = GIMP_OFFSET_TOOL (tool);
342 
343   if (! offset_tool->dragging)
344     {
345       GIMP_TOOL_CLASS (parent_class)->motion (tool, coords, time, state,
346                                               display);
347     }
348   else
349     {
350       GimpOffsetType type;
351       gint           offset_x;
352       gint           offset_y;
353       gint           x;
354       gint           y;
355       gint           width;
356       gint           height;
357 
358       g_object_get (filter_tool->config,
359                     "type", &type,
360                     NULL);
361 
362       offset_x = RINT (coords->x - offset_tool->x);
363       offset_y = RINT (coords->y - offset_tool->y);
364 
365       x = offset_tool->offset_x + offset_x;
366       y = offset_tool->offset_y + offset_y;
367 
368       width  = gimp_offset_tool_get_width  (offset_tool);
369       height = gimp_offset_tool_get_height (offset_tool);
370 
371       if (type == GIMP_OFFSET_WRAP_AROUND)
372         {
373           x %= MAX (width,  1);
374           y %= MAX (height, 1);
375         }
376       else
377         {
378           x = CLAMP (x, -width,  +width);
379           y = CLAMP (y, -height, +height);
380         }
381 
382       g_object_set (filter_tool->config,
383                     "x", x,
384                     "y", y,
385                     NULL);
386 
387       gimp_tool_pop_status (tool, display);
388 
389       gimp_tool_push_status_coords (tool, display,
390                                     GIMP_CURSOR_PRECISION_PIXEL_CENTER,
391                                     _("Offset: "),
392                                     offset_x,
393                                     ", ",
394                                     offset_y,
395                                     NULL);
396     }
397 }
398 
399 static void
gimp_offset_tool_oper_update(GimpTool * tool,const GimpCoords * coords,GdkModifierType state,gboolean proximity,GimpDisplay * display)400 gimp_offset_tool_oper_update (GimpTool         *tool,
401                               const GimpCoords *coords,
402                               GdkModifierType   state,
403                               gboolean          proximity,
404                               GimpDisplay      *display)
405 {
406   if (! tool->drawable ||
407       gimp_filter_tool_on_guide (GIMP_FILTER_TOOL (tool),
408                                  coords, display))
409     {
410       GIMP_TOOL_CLASS (parent_class)->oper_update (tool, coords, state,
411                                                    proximity, display);
412     }
413   else
414     {
415       gimp_tool_pop_status (tool, display);
416 
417       gimp_tool_push_status (tool, display, "%s",
418                              _("Click-Drag to offset drawable"));
419     }
420 }
421 
422 static void
gimp_offset_tool_cursor_update(GimpTool * tool,const GimpCoords * coords,GdkModifierType state,GimpDisplay * display)423 gimp_offset_tool_cursor_update (GimpTool         *tool,
424                                 const GimpCoords *coords,
425                                 GdkModifierType   state,
426                                 GimpDisplay      *display)
427 {
428   if (! tool->drawable ||
429       gimp_filter_tool_on_guide (GIMP_FILTER_TOOL (tool),
430                                  coords, display))
431     {
432       GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state,
433                                                      display);
434     }
435   else
436     {
437       gimp_tool_set_cursor (tool, display,
438                             GIMP_CURSOR_MOUSE,
439                             GIMP_TOOL_CURSOR_MOVE,
440                             GIMP_CURSOR_MODIFIER_NONE);
441     }
442 }
443 
444 static void
gimp_offset_tool_dialog(GimpFilterTool * filter_tool)445 gimp_offset_tool_dialog (GimpFilterTool *filter_tool)
446 {
447   GimpOffsetTool *offset_tool = GIMP_OFFSET_TOOL (filter_tool);
448   GtkWidget      *main_vbox;
449   GtkWidget      *vbox;
450   GtkWidget      *hbox;
451   GtkWidget      *button;
452   GtkWidget      *spinbutton;
453   GtkWidget      *frame;
454   GtkAdjustment  *adjustment;
455 
456   main_vbox = gimp_filter_tool_dialog_get_vbox (filter_tool);
457 
458   /*  The offset frame  */
459   frame = gimp_frame_new (_("Offset"));
460   gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
461   gtk_widget_show (frame);
462 
463   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
464   gtk_container_add (GTK_CONTAINER (frame), vbox);
465   gtk_widget_show (vbox);
466 
467   adjustment = (GtkAdjustment *)
468     gtk_adjustment_new (1, 1, 1, 1, 10, 0);
469   spinbutton = gimp_spin_button_new (adjustment, 1.0, 2);
470   gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
471   gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 10);
472 
473   offset_tool->offset_se = gimp_size_entry_new (1, GIMP_UNIT_PIXEL, "%a",
474                                                 TRUE, TRUE, FALSE, 10,
475                                                 GIMP_SIZE_ENTRY_UPDATE_SIZE);
476 
477   gtk_table_set_col_spacing (GTK_TABLE (offset_tool->offset_se), 0, 4);
478   gtk_table_set_col_spacing (GTK_TABLE (offset_tool->offset_se), 1, 4);
479   gtk_table_set_row_spacing (GTK_TABLE (offset_tool->offset_se), 0, 2);
480 
481   gimp_size_entry_add_field (GIMP_SIZE_ENTRY (offset_tool->offset_se),
482                              GTK_SPIN_BUTTON (spinbutton), NULL);
483   gtk_table_attach_defaults (GTK_TABLE (offset_tool->offset_se), spinbutton,
484                              1, 2, 0, 1);
485   gtk_widget_show (spinbutton);
486 
487   gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (offset_tool->offset_se),
488                                 _("_X:"), 0, 0, 0.0);
489   gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (offset_tool->offset_se),
490                                 _("_Y:"), 1, 0, 0.0);
491 
492   gtk_box_pack_start (GTK_BOX (vbox), offset_tool->offset_se, FALSE, FALSE, 0);
493   gtk_widget_show (offset_tool->offset_se);
494 
495   gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (offset_tool->offset_se),
496                             GIMP_UNIT_PIXEL);
497 
498   g_signal_connect (offset_tool->offset_se, "refval-changed",
499                     G_CALLBACK (gimp_offset_tool_offset_changed),
500                     offset_tool);
501   g_signal_connect (offset_tool->offset_se, "value-changed",
502                     G_CALLBACK (gimp_offset_tool_offset_changed),
503                     offset_tool);
504 
505   button = gtk_button_new_with_mnemonic (_("By width/_2, height/2"));
506   gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
507   gtk_widget_show (button);
508 
509   g_signal_connect (button, "clicked",
510                     G_CALLBACK (gimp_offset_tool_half_xy_clicked),
511                     offset_tool);
512 
513   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
514   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
515   gtk_widget_show (hbox);
516 
517   button = gtk_button_new_with_mnemonic (_("By _width/2"));
518   gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
519   gtk_widget_show (button);
520 
521   g_signal_connect (button, "clicked",
522                     G_CALLBACK (gimp_offset_tool_half_x_clicked),
523                     offset_tool);
524 
525   button = gtk_button_new_with_mnemonic (_("By _height/2"));
526   gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
527   gtk_widget_show (button);
528 
529   g_signal_connect (button, "clicked",
530                     G_CALLBACK (gimp_offset_tool_half_y_clicked),
531                     offset_tool);
532 
533   /*  The edge behavior frame  */
534   frame = gimp_int_radio_group_new (TRUE, _("Edge Behavior"),
535 
536                                     G_CALLBACK (gimp_offset_tool_edge_behavior_toggled),
537                                     offset_tool,
538 
539                                     GIMP_OFFSET_WRAP_AROUND,
540 
541                                     _("W_rap around"),
542                                     GIMP_OFFSET_WRAP_AROUND, NULL,
543 
544                                     _("Fill with _background color"),
545                                     GIMP_OFFSET_BACKGROUND, NULL,
546 
547                                     _("Make _transparent"),
548                                     GIMP_OFFSET_TRANSPARENT,
549                                     &offset_tool->transparent_radio,
550                                     NULL);
551 
552   gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
553   gtk_widget_show (frame);
554 }
555 
556 static void
gimp_offset_tool_config_notify(GimpFilterTool * filter_tool,GimpConfig * config,const GParamSpec * pspec)557 gimp_offset_tool_config_notify (GimpFilterTool   *filter_tool,
558                                 GimpConfig       *config,
559                                 const GParamSpec *pspec)
560 {
561   gimp_offset_tool_update (GIMP_OFFSET_TOOL (filter_tool));
562 
563   GIMP_FILTER_TOOL_CLASS (parent_class)->config_notify (filter_tool,
564                                                         config, pspec);
565 }
566 
567 static void
gimp_offset_tool_region_changed(GimpFilterTool * filter_tool)568 gimp_offset_tool_region_changed (GimpFilterTool *filter_tool)
569 {
570   gimp_offset_tool_update (GIMP_OFFSET_TOOL (filter_tool));
571 }
572 
573 static void
gimp_offset_tool_offset_changed(GimpSizeEntry * se,GimpOffsetTool * offset_tool)574 gimp_offset_tool_offset_changed (GimpSizeEntry  *se,
575                                  GimpOffsetTool *offset_tool)
576 {
577   g_object_set (GIMP_FILTER_TOOL (offset_tool)->config,
578                 "x", (gint) gimp_size_entry_get_refval (se, 0),
579                 "y", (gint) gimp_size_entry_get_refval (se, 1),
580                 NULL);
581 }
582 
583 static void
gimp_offset_tool_half_xy_clicked(GtkButton * button,GimpOffsetTool * offset_tool)584 gimp_offset_tool_half_xy_clicked (GtkButton      *button,
585                                   GimpOffsetTool *offset_tool)
586 {
587   g_object_set (GIMP_FILTER_TOOL (offset_tool)->config,
588                 "x", gimp_offset_tool_get_width  (offset_tool) / 2,
589                 "y", gimp_offset_tool_get_height (offset_tool) / 2,
590                 NULL);
591 }
592 
593 static void
gimp_offset_tool_half_x_clicked(GtkButton * button,GimpOffsetTool * offset_tool)594 gimp_offset_tool_half_x_clicked (GtkButton      *button,
595                                  GimpOffsetTool *offset_tool)
596 {
597   g_object_set (GIMP_FILTER_TOOL (offset_tool)->config,
598                 "x", gimp_offset_tool_get_width (offset_tool) / 2,
599                 NULL);
600 }
601 
602 static void
gimp_offset_tool_half_y_clicked(GtkButton * button,GimpOffsetTool * offset_tool)603 gimp_offset_tool_half_y_clicked (GtkButton      *button,
604                                  GimpOffsetTool *offset_tool)
605 {
606   g_object_set (GIMP_FILTER_TOOL (offset_tool)->config,
607                 "y", gimp_offset_tool_get_height (offset_tool) / 2,
608                 NULL);
609 }
610 
611 static void
gimp_offset_tool_edge_behavior_toggled(GtkToggleButton * toggle,GimpOffsetTool * offset_tool)612 gimp_offset_tool_edge_behavior_toggled (GtkToggleButton *toggle,
613                                         GimpOffsetTool  *offset_tool)
614 {
615   if (gtk_toggle_button_get_active (toggle))
616     {
617       GimpOffsetType type;
618 
619       type = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (toggle),
620                                                  "gimp-item-data"));
621 
622       g_object_set (GIMP_FILTER_TOOL (offset_tool)->config,
623                     "type", type,
624                     NULL);
625     }
626 }
627 
628 static void
gimp_offset_tool_background_changed(GimpContext * context,const GimpRGB * color,GimpOffsetTool * offset_tool)629 gimp_offset_tool_background_changed (GimpContext    *context,
630                                      const GimpRGB  *color,
631                                      GimpOffsetTool *offset_tool)
632 {
633   GimpFilterTool *filter_tool = GIMP_FILTER_TOOL (offset_tool);
634   GimpOffsetType  type;
635 
636   g_object_get (filter_tool->config,
637                 "type", &type,
638                 NULL);
639 
640   if (type == GIMP_OFFSET_BACKGROUND)
641     {
642       gegl_node_set (filter_tool->operation,
643                      "context", context,
644                      NULL);
645 
646       gimp_drawable_filter_apply (filter_tool->filter, NULL);
647     }
648 }
649 
650 static gint
gimp_offset_tool_get_width(GimpOffsetTool * offset_tool)651 gimp_offset_tool_get_width (GimpOffsetTool *offset_tool)
652 {
653   GeglRectangle drawable_area;
654   gint          drawable_offset_x;
655   gint          drawable_offset_y;
656 
657   if (gimp_filter_tool_get_drawable_area (GIMP_FILTER_TOOL (offset_tool),
658                                           &drawable_offset_x,
659                                           &drawable_offset_y,
660                                           &drawable_area) &&
661       ! gegl_rectangle_is_empty (&drawable_area))
662     {
663       return drawable_area.width;
664     }
665 
666   return 0;
667 }
668 
669 static gint
gimp_offset_tool_get_height(GimpOffsetTool * offset_tool)670 gimp_offset_tool_get_height (GimpOffsetTool *offset_tool)
671 {
672   GeglRectangle drawable_area;
673   gint          drawable_offset_x;
674   gint          drawable_offset_y;
675 
676   if (gimp_filter_tool_get_drawable_area (GIMP_FILTER_TOOL (offset_tool),
677                                           &drawable_offset_x,
678                                           &drawable_offset_y,
679                                           &drawable_area) &&
680       ! gegl_rectangle_is_empty (&drawable_area))
681     {
682       return drawable_area.height;
683     }
684 
685   return 0;
686 }
687 
688 static void
gimp_offset_tool_update(GimpOffsetTool * offset_tool)689 gimp_offset_tool_update (GimpOffsetTool *offset_tool)
690 {
691   GimpTool       *tool        = GIMP_TOOL (offset_tool);
692   GimpFilterTool *filter_tool = GIMP_FILTER_TOOL (offset_tool);
693   GimpOffsetType  orig_type;
694   gint            orig_x;
695   gint            orig_y;
696   GimpOffsetType  type;
697   gint            x;
698   gint            y;
699   gint            width;
700   gint            height;
701 
702   g_object_get (filter_tool->config,
703                 "type", &orig_type,
704                 "x",    &orig_x,
705                 "y",    &orig_y,
706                 NULL);
707 
708   width  = gimp_offset_tool_get_width  (offset_tool);
709   height = gimp_offset_tool_get_height (offset_tool);
710 
711   x = CLAMP (orig_x, -width,  +width);
712   y = CLAMP (orig_y, -height, +height);
713 
714   type = orig_type;
715 
716   if (tool->drawable                             &&
717       ! gimp_drawable_has_alpha (tool->drawable) &&
718       type == GIMP_OFFSET_TRANSPARENT)
719     {
720       type = GIMP_OFFSET_BACKGROUND;
721     }
722 
723   if (x    != orig_x ||
724       y    != orig_y ||
725       type != orig_type)
726     {
727       g_object_set (filter_tool->config,
728                     "type", type,
729                     "x",    x,
730                     "y",    y,
731                     NULL);
732     }
733 
734   if (offset_tool->offset_se)
735     {
736       gint width  = gimp_offset_tool_get_width  (offset_tool);
737       gint height = gimp_offset_tool_get_height (offset_tool);
738 
739       g_signal_handlers_block_by_func (offset_tool->offset_se,
740                                        gimp_offset_tool_offset_changed,
741                                        offset_tool);
742 
743       gimp_size_entry_set_refval_boundaries (
744         GIMP_SIZE_ENTRY (offset_tool->offset_se), 0,
745         -width, +width);
746       gimp_size_entry_set_refval_boundaries (
747         GIMP_SIZE_ENTRY (offset_tool->offset_se), 1,
748         -height, +height);
749 
750       gimp_size_entry_set_size (
751         GIMP_SIZE_ENTRY (offset_tool->offset_se), 0,
752         0, width);
753       gimp_size_entry_set_size (
754         GIMP_SIZE_ENTRY (offset_tool->offset_se), 1,
755         0, height);
756 
757       gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (offset_tool->offset_se), 0,
758                                   x);
759       gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (offset_tool->offset_se), 1,
760                                   y);
761 
762       g_signal_handlers_unblock_by_func (offset_tool->offset_se,
763                                          gimp_offset_tool_offset_changed,
764                                          offset_tool);
765     }
766 
767   if (offset_tool->transparent_radio)
768     {
769       gimp_int_radio_group_set_active (
770         GTK_RADIO_BUTTON (offset_tool->transparent_radio),
771         type);
772     }
773 }
774 
775 static void
gimp_offset_tool_halt(GimpOffsetTool * offset_tool)776 gimp_offset_tool_halt (GimpOffsetTool *offset_tool)
777 {
778   GimpContext *context = GIMP_CONTEXT (GIMP_TOOL_GET_OPTIONS (offset_tool));
779 
780   offset_tool->offset_se         = NULL;
781   offset_tool->transparent_radio = NULL;
782 
783   g_signal_handlers_disconnect_by_func (
784     context,
785     gimp_offset_tool_background_changed,
786     offset_tool);
787 }
788