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 "display-types.h"
24 
25 #include "widgets/gimpdeviceinfo.h"
26 #include "widgets/gimpdeviceinfo-coords.h"
27 
28 #include "gimpdisplay.h"
29 #include "gimpdisplayshell.h"
30 #include "gimpdisplayshell-autoscroll.h"
31 #include "gimpdisplayshell-scroll.h"
32 #include "gimpdisplayshell-transform.h"
33 
34 #include "tools/tools-types.h"
35 #include "tools/tool_manager.h"
36 #include "tools/gimptool.h"
37 #include "tools/gimptoolcontrol.h"
38 
39 
40 #define AUTOSCROLL_DT  20
41 #define AUTOSCROLL_DX  0.1
42 
43 
44 typedef struct
45 {
46   GdkEventMotion  *mevent;
47   GimpDeviceInfo  *device;
48   guint32          time;
49   GdkModifierType  state;
50   guint            timeout_id;
51 } ScrollInfo;
52 
53 
54 /*  local function prototypes  */
55 
56 static gboolean gimp_display_shell_autoscroll_timeout (gpointer data);
57 
58 
59 /*  public functions  */
60 
61 void
gimp_display_shell_autoscroll_start(GimpDisplayShell * shell,GdkModifierType state,GdkEventMotion * mevent)62 gimp_display_shell_autoscroll_start (GimpDisplayShell *shell,
63                                      GdkModifierType   state,
64                                      GdkEventMotion   *mevent)
65 {
66   ScrollInfo *info;
67 
68   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
69 
70   if (shell->scroll_info)
71     return;
72 
73   info = g_slice_new0 (ScrollInfo);
74 
75   info->mevent     = mevent;
76   info->device     = gimp_device_info_get_by_device (mevent->device);
77   info->time       = gdk_event_get_time ((GdkEvent *) mevent);
78   info->state      = state;
79   info->timeout_id = g_timeout_add (AUTOSCROLL_DT,
80                                     gimp_display_shell_autoscroll_timeout,
81                                     shell);
82 
83   shell->scroll_info = info;
84 }
85 
86 void
gimp_display_shell_autoscroll_stop(GimpDisplayShell * shell)87 gimp_display_shell_autoscroll_stop (GimpDisplayShell *shell)
88 {
89   ScrollInfo *info;
90 
91   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
92 
93   if (! shell->scroll_info)
94     return;
95 
96   info = shell->scroll_info;
97 
98   if (info->timeout_id)
99     {
100       g_source_remove (info->timeout_id);
101       info->timeout_id = 0;
102     }
103 
104   g_slice_free (ScrollInfo, info);
105   shell->scroll_info = NULL;
106 }
107 
108 
109 /*  private functions  */
110 
111 static gboolean
gimp_display_shell_autoscroll_timeout(gpointer data)112 gimp_display_shell_autoscroll_timeout (gpointer data)
113 {
114   GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (data);
115   ScrollInfo       *info  = shell->scroll_info;
116   GimpCoords        device_coords;
117   GimpCoords        image_coords;
118   gint              dx = 0;
119   gint              dy = 0;
120 
121   gimp_device_info_get_device_coords (info->device,
122                                       gtk_widget_get_window (shell->canvas),
123                                       &device_coords);
124 
125   if (device_coords.x < 0)
126     dx = device_coords.x;
127   else if (device_coords.x > shell->disp_width)
128     dx = device_coords.x - shell->disp_width;
129 
130   if (device_coords.y < 0)
131     dy = device_coords.y;
132   else if (device_coords.y > shell->disp_height)
133     dy = device_coords.y - shell->disp_height;
134 
135   if (dx || dy)
136     {
137       GimpDisplay *display         = shell->display;
138       GimpTool    *active_tool     = tool_manager_get_active (display->gimp);
139       gint         scroll_amount_x = AUTOSCROLL_DX * dx;
140       gint         scroll_amount_y = AUTOSCROLL_DX * dy;
141 
142       info->time += AUTOSCROLL_DT;
143 
144       gimp_display_shell_scroll_unoverscrollify (shell,
145                                                  scroll_amount_x,
146                                                  scroll_amount_y,
147                                                  &scroll_amount_x,
148                                                  &scroll_amount_y);
149 
150       gimp_display_shell_scroll (shell,
151                                  scroll_amount_x,
152                                  scroll_amount_y);
153 
154       gimp_display_shell_untransform_coords (shell,
155                                              &device_coords,
156                                              &image_coords);
157 
158       if (gimp_tool_control_get_snap_to (active_tool->control))
159         {
160           gint x, y, width, height;
161 
162           gimp_tool_control_get_snap_offsets (active_tool->control,
163                                               &x, &y, &width, &height);
164 
165           gimp_display_shell_snap_coords (shell,
166                                           &image_coords,
167                                           x, y, width, height);
168         }
169 
170       tool_manager_motion_active (display->gimp,
171                                   &image_coords,
172                                   info->time, info->state,
173                                   display);
174 
175       return TRUE;
176     }
177   else
178     {
179       g_slice_free (ScrollInfo, info);
180       shell->scroll_info = NULL;
181 
182       return FALSE;
183     }
184 }
185