Remove the trace of a TODO file.
[gliv.git] / src / callbacks.c
blob2eae2a683bf7e62eab73beda10dbce5612941204
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 * See the COPYING file for license information.
18 * Guillaume Chazarain <guichaz@yahoo.fr>
21 /****************************************************************
22 * Callbacks to control the gl_widget through its parent window *
23 ****************************************************************/
25 #include <gdk/gdkkeysyms.h> /* GDK_* */
27 #include "gliv.h"
28 #include "callbacks.h"
29 #include "math_floats.h" /* atan2f(), powf() */
30 #include "options.h"
31 #include "rendering.h"
32 #include "params.h"
33 #include "main.h"
34 #include "zoom_frame.h"
35 #include "move_pointer.h"
36 #include "matrix.h"
37 #include "gl_widget.h"
38 #include "next_image.h"
40 extern rt_struct *rt;
41 extern options_struct *options;
42 extern GtkWidget *gl_widget;
44 /* Left button saved position in the root window. */
45 static gfloat old1_root_x, old1_root_y;
47 /* Where the left button press occured in the window. */
48 static gfloat press1_win_x, press1_win_y;
50 /* Right button saved position in the window. */
51 static gint old3_win_x, old3_win_y;
53 /* If the middle button is released. */
54 static gboolean mouse_wheel_zoom = TRUE;
56 /* Number of times the pointer warped in each direction. */
57 static gint nb_scr_w = 0, nb_scr_h = 0;
59 /* Absolute position, taking into account the screen warpings. */
60 #define ROOT_X(e) ((e)->x_root + nb_scr_w * rt->scr_width)
61 #define ROOT_Y(e) ((e)->y_root + nb_scr_h * rt->scr_height)
63 static gboolean button_press_event(GtkWidget * unused, GdkEventButton * event)
65 switch (event->button) {
66 case 1:
67 /* Reset mouse warping. */
68 nb_scr_w = 0;
69 nb_scr_h = 0;
71 old1_root_x = ROOT_X(event);
72 old1_root_y = ROOT_Y(event);
74 press1_win_x = event->x;
75 press1_win_y = event->y;
76 break;
78 case 2:
79 mouse_wheel_zoom = FALSE;
80 break;
82 case 3:
83 /* We now have one of the zoom rectangle vertex. */
84 old3_win_x = (gint) event->x;
85 old3_win_y = (gint) event->y;
86 break;
89 return TRUE;
92 static gboolean button_release_event(GtkWidget * unused, GdkEventButton * event)
94 switch (event->button) {
95 case 1:
96 /* Reset mouse warping. */
97 nb_scr_w = 0;
98 nb_scr_h = 0;
100 refresh(APPEND_HISTORY);
101 break;
103 case 2:
104 mouse_wheel_zoom = TRUE;
105 break;
107 case 3:
108 zoom_frame();
111 return TRUE;
114 /* Checks whether the pointer is on a border then moves it and returns TRUE. */
115 static gboolean warp_pointer(gfloat x, gfloat y)
117 gboolean warped;
118 gint new_x, new_y;
120 if (x <= 0.0) {
121 nb_scr_w--;
122 new_x = rt->scr_width - 1;
123 warped = TRUE;
125 } else if (x >= rt->scr_width - 1.0) {
126 nb_scr_w++;
127 new_x = 0;
128 warped = TRUE;
130 } else {
131 new_x = (gint) x;
132 warped = FALSE;
135 if (y <= 0.0) {
136 nb_scr_h--;
137 new_y = rt->scr_height - 1;
138 warped = TRUE;
140 } else if (y >= rt->scr_height - 1.0) {
141 nb_scr_h++;
142 new_y = 0;
143 warped = TRUE;
145 } else
146 new_y = (gint) y;
148 if (warped)
149 move_pointer(new_x, new_y);
151 return warped;
155 * Remember the pointer position to know the sliding distance, the angle,
156 * or the zoom when the first button is pressed.
158 static void update_coordinates1(GdkEventMotion * event)
160 old1_root_x = ROOT_X(event);
161 old1_root_y = ROOT_Y(event);
164 static gint process_move(GdkEventMotion * event)
166 gfloat root_x, root_y;
168 root_x = ROOT_X(event);
169 root_y = ROOT_Y(event);
171 matrix_move(root_x - old1_root_x, root_y - old1_root_y);
173 return REFRESH_BURST;
176 static gint process_rotation(GdkEventMotion * event)
178 gfloat center_x, center_y, x0, y0, x1, y1, angle;
179 gint win_x, win_y;
181 gdk_window_get_root_origin(gl_widget->window, &win_x, &win_y);
183 /* Window center in root coordinates. */
184 center_x = win_x + rt->wid_size->width / 2.0;
185 center_y = win_y + rt->wid_size->height / 2.0;
187 /* First rotation vector. */
188 x0 = old1_root_x - center_x;
189 y0 = old1_root_y - center_y;
191 /* Second rotation vector. */
192 x1 = ROOT_X(event) - center_x;
193 y1 = ROOT_Y(event) - center_y;
195 angle = atan2f(y0, x0) + atan2f(x1, y1) - PI / 2.0;
197 matrix_rotate(angle);
199 return REFRESH_BURST | REFRESH_STATUS;
202 static gint process_zoom(GdkEventMotion * event)
204 gfloat center_x, center_y, delta, ratio;
206 delta = ROOT_Y(event) - old1_root_y;
207 ratio = powf(ZOOM_FACTOR, delta / 10.0);
209 if (options->zoom_pointer) {
210 center_x = press1_win_x;
211 center_y = press1_win_y;
212 } else {
213 center_x = rt->wid_size->width / 2.0;
214 center_y = rt->wid_size->height / 2.0;
217 matrix_zoom(ratio, center_x, center_y);
219 return REFRESH_BURST | REFRESH_STATUS;
222 static gboolean motion_notify_event(GtkWidget * unused, GdkEventMotion * event)
224 /* move_pointer() generates a motion_notify_event, useless here. */
225 static gboolean skip = FALSE;
226 gint what = 0;
228 if (skip) {
229 /* We come just after a move_pointer(). */
230 skip = FALSE;
231 return TRUE;
234 if (rt->cursor_hidden)
235 toggle_cursor(TRUE);
236 else
237 schedule_hide_cursor();
239 if (event->state & GDK_BUTTON1_MASK) {
241 if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) {
242 if (event->state & GDK_CONTROL_MASK)
243 what = process_rotation(event);
245 if (event->state & GDK_SHIFT_MASK)
246 what |= process_zoom(event);
247 } else
248 what = process_move(event);
250 refresh(what);
252 update_coordinates1(event);
253 skip = warp_pointer(event->x_root, event->y_root);
255 } else if (event->state & GDK_BUTTON3_MASK) {
257 /* We draw the zoom rectangle. */
258 set_zoom_frame(old3_win_x, old3_win_y,
259 (gint) event->x - old3_win_x,
260 (gint) event->y - old3_win_y);
262 draw_zoom_frame(TRUE);
265 return TRUE;
268 /* Called when pressing an arrow key. */
269 static void move_or_rotate(guint state, gfloat x, gfloat y, gfloat angle)
271 guint what = REFRESH_IMAGE | APPEND_HISTORY;
273 if (state & GDK_CONTROL_MASK) {
274 matrix_rotate(angle);
275 what |= REFRESH_STATUS;
276 } else
277 matrix_move(x, y);
279 refresh(what);
282 /* Mask to keep track of which arrow keys are pressed. */
283 #define ARROW_UP (1 << 0)
284 #define ARROW_LEFT (1 << 1)
285 #define ARROW_RIGHT (1 << 2)
286 #define ARROW_DOWN (1 << 3)
288 /* Called twice to know how far we move in the two directions. */
289 static gfloat get_arrow_move(guint state, guint flag_pos, guint flag_neg)
291 guint flags;
293 flags = state & (flag_pos | flag_neg);
295 if (flags == (flag_pos | flag_neg) || flags == 0)
296 /* Both keys are pressed, or none => don't move. */
297 return 0.0;
299 if (state & flag_pos)
300 /* Left or up. */
301 return MOVE_OFFSET;
303 /* Right or down. */
304 return -MOVE_OFFSET;
307 /* An arrow key or something else was either pressed or released. */
308 static gboolean process_arrow_key(GdkEventKey * event, gboolean press)
310 static guint arrow_state = 0;
311 guint arrow;
312 gfloat x, y, angle;
314 switch (event->keyval) {
315 case GDK_Up:
316 case GDK_KP_Up:
317 arrow = ARROW_UP;
318 angle = BIG_ROTATION;
319 break;
321 case GDK_Left:
322 case GDK_KP_Left:
323 arrow = ARROW_LEFT;
324 angle = SMALL_ROTATION;
325 break;
327 case GDK_Right:
328 case GDK_KP_Right:
329 arrow = ARROW_RIGHT;
330 angle = -SMALL_ROTATION;
331 break;
333 case GDK_Down:
334 case GDK_KP_Down:
335 arrow = ARROW_DOWN;
336 angle = -BIG_ROTATION;
337 break;
339 default:
340 /* Key not found, try a keyboard accelerator. */
341 return FALSE;
344 if (press)
345 arrow_state |= arrow;
346 else
347 arrow_state &= ~arrow;
349 if (press) {
350 x = get_arrow_move(arrow_state, ARROW_LEFT, ARROW_RIGHT);
351 y = get_arrow_move(arrow_state, ARROW_UP, ARROW_DOWN);
353 move_or_rotate(event->state, x, y, angle);
356 return TRUE;
359 static gboolean key_press_event(GtkWidget * unused, GdkEventKey * event)
361 /* Most keys are handled via keyboard accelerators. */
363 switch (event->keyval) {
364 case GDK_Escape:
365 gui_quit();
366 break;
368 case GDK_KP_Space:
369 case GDK_space:
370 load_direction(1);
371 break;
373 case GDK_BackSpace:
374 load_direction(-1);
375 break;
377 case GDK_minus:
378 case GDK_KP_Subtract:
379 zoom_in(1.0 / ZOOM_FACTOR);
380 break;
382 case GDK_plus:
383 case GDK_KP_Add:
384 case GDK_equal:
385 zoom_in(ZOOM_FACTOR);
386 break;
388 default:
389 /* Key not found, try an arrow key and then a keyboard accelerator. */
390 return process_arrow_key(event, TRUE);
393 return TRUE;
396 static gboolean key_release_event(GtkWidget * unused, GdkEventKey * event)
398 return process_arrow_key(event, FALSE);
401 static gboolean scroll_event(GtkWidget * unused, GdkEventScroll * event)
403 if (mouse_wheel_zoom) {
405 if (event->direction == GDK_SCROLL_UP)
406 zoom_in(1.0 / ZOOM_FACTOR);
408 else if (event->direction == GDK_SCROLL_DOWN)
409 zoom_in(ZOOM_FACTOR);
411 } else {
412 /* mouse_wheel_zoom == FALSE */
414 if (event->direction == GDK_SCROLL_UP)
415 load_direction(-1);
417 else if (event->direction == GDK_SCROLL_DOWN)
418 load_direction(1);
421 return TRUE;
424 void install_callbacks(gpointer object)
426 g_signal_connect(object, "button-press-event",
427 G_CALLBACK(button_press_event), NULL);
429 g_signal_connect(object, "button-release-event",
430 G_CALLBACK(button_release_event), NULL);
432 g_signal_connect(object, "motion-notify-event",
433 G_CALLBACK(motion_notify_event), NULL);
435 g_signal_connect(object, "key-press-event",
436 G_CALLBACK(key_press_event), NULL);
438 g_signal_connect(object, "key-release-event",
439 G_CALLBACK(key_release_event), NULL);
441 g_signal_connect(object, "delete-event", G_CALLBACK(gui_quit), NULL);
443 g_signal_connect(object, "scroll-event", G_CALLBACK(scroll_event), NULL);
447 * We repeatedly call process_events() when waiting for a thread or a
448 * process, when rebuilding the images menus or to wait for a refresh.
450 void process_events(void)
452 while (gtk_events_pending() != 0)
453 gtk_main_iteration_do(FALSE);