Fix some more build warnings
[gliv.git] / src / callbacks.c
blob4d66f6cdf73bfc3665643da9fecf5e8eb36036a4
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@gmail.com>
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 "cursors.h"
38 #include "next_image.h"
39 #include "gliv-image.h"
41 extern rt_struct *rt;
42 extern options_struct *options;
43 extern GlivImage *current_image;
44 extern GtkWidget *gl_widget;
46 /* Left button saved position in the root window. */
47 static gfloat old1_root_x, old1_root_y;
49 /* Where the left button press occurred in the window. */
50 static gfloat press1_win_x, press1_win_y;
52 /* Where the middle button press occurred in the window. */
53 static gfloat press2_win_x, press2_win_y;
55 /* Right button saved position in the window. */
56 static gint old3_win_x, old3_win_y;
58 /* If the middle button is released. */
59 static gboolean mouse_wheel_zoom = TRUE;
61 static gboolean button_press_event(GtkWidget * unused, GdkEventButton * event)
63 if (current_image == NULL)
64 return FALSE;
66 switch (event->button) {
67 case 1:
68 old1_root_x = event->x_root;
69 old1_root_y = event->y_root;
71 press1_win_x = event->x;
72 press1_win_y = event->y;
73 break;
75 case 2:
76 mouse_wheel_zoom = FALSE;
77 press2_win_x = event->x;
78 press2_win_y = event->y;
79 break;
81 case 3:
82 /* We now have one of the zoom rectangle vertex. */
83 old3_win_x = (gint) event->x;
84 old3_win_y = (gint) event->y;
85 break;
88 return FALSE;
91 #define CHANGE_IMAGE_SCREEN_RATIO 20
93 static void detect_image_change(GdkEventButton * event)
95 int dx = event->x - press2_win_x;
96 int dy = event->y - press2_win_y;
97 int dir = 0;
99 if (!(event->state & GDK_CONTROL_MASK))
100 return;
102 if (dx > rt->scr_width / CHANGE_IMAGE_SCREEN_RATIO ||
103 dy > rt->scr_height / CHANGE_IMAGE_SCREEN_RATIO)
104 dir = 1;
105 else if (-dx > rt->scr_width / CHANGE_IMAGE_SCREEN_RATIO ||
106 -dy > rt->scr_height / CHANGE_IMAGE_SCREEN_RATIO)
107 dir = -1;
109 if (dir)
110 load_direction(dir);
113 static gboolean button_release_event(GtkWidget * unused, GdkEventButton * event)
115 if (current_image == NULL)
116 return FALSE;
118 switch (event->button) {
119 case 1:
120 refresh(APPEND_HISTORY);
121 break;
123 case 2:
124 mouse_wheel_zoom = TRUE;
125 detect_image_change(event);
126 break;
128 case 3:
129 zoom_frame();
132 return FALSE;
135 /* Checks whether the pointer is on a border then moves it and returns TRUE. */
136 static gboolean warp_pointer(gfloat x, gfloat y)
138 gboolean warped;
139 gint new_x, new_y;
141 if (x <= 0.0) {
142 new_x = rt->scr_width - 1;
143 warped = TRUE;
145 } else if (x >= rt->scr_width - 1.0) {
146 new_x = 0;
147 warped = TRUE;
149 } else {
150 new_x = (gint) x;
151 warped = FALSE;
154 if (y <= 0.0) {
155 new_y = rt->scr_height - 1;
156 warped = TRUE;
158 } else if (y >= rt->scr_height - 1.0) {
159 new_y = 0;
160 warped = TRUE;
162 } else
163 new_y = (gint) y;
165 if (warped)
166 move_pointer(new_x, new_y);
168 return warped;
172 * Remember the pointer position to know the sliding distance, the angle,
173 * or the zoom when the first button is pressed.
175 static void update_coordinates1(GdkEventMotion * event)
177 old1_root_x = event->x_root;
178 old1_root_y = event->y_root;
181 static gint process_move(GdkEventMotion * event)
183 gfloat root_x, root_y;
185 root_x = event->x_root;
186 root_y = event->y_root;
188 matrix_move(root_x - old1_root_x, root_y - old1_root_y);
190 return REFRESH_BURST;
193 static gint process_rotation(GdkEventMotion * event)
195 gfloat center_x, center_y, x0, y0, x1, y1, angle;
196 gint win_x, win_y;
198 gdk_window_get_root_origin(gl_widget->window, &win_x, &win_y);
200 /* Window center in root coordinates. */
201 center_x = win_x + rt->wid_size->width / 2.0;
202 center_y = win_y + rt->wid_size->height / 2.0;
204 /* First rotation vector. */
205 x0 = old1_root_x - center_x;
206 y0 = old1_root_y - center_y;
208 /* Second rotation vector. */
209 x1 = event->x_root - center_x;
210 y1 = event->y_root - center_y;
212 angle = atan2f(y0, x0) + atan2f(x1, y1) - PI / 2.0;
214 matrix_rotate(angle);
216 return REFRESH_BURST | REFRESH_STATUS;
219 static gint process_zoom(GdkEventMotion * event)
221 gfloat center_x, center_y, delta, ratio;
223 delta = event->y_root - old1_root_y;
224 ratio = powf(ZOOM_FACTOR, delta / 10.0);
226 if (options->zoom_pointer) {
227 center_x = press1_win_x;
228 center_y = press1_win_y;
229 } else {
230 center_x = rt->wid_size->width / 2.0;
231 center_y = rt->wid_size->height / 2.0;
234 matrix_zoom(ratio, center_x, center_y);
236 return REFRESH_BURST | REFRESH_STATUS;
239 static gboolean motion_notify_event(GtkWidget * unused, GdkEventMotion * event)
241 /* move_pointer() generates a motion_notify_event, useless here. */
242 static gboolean skip = FALSE;
243 gint what = 0;
245 if (skip) {
246 /* We come just after a move_pointer(). */
247 skip = FALSE;
248 update_coordinates1(event);
249 return TRUE;
252 if (rt->cursor_hidden)
253 show_cursor();
254 else
255 schedule_hide_cursor();
257 if (current_image == NULL)
258 return FALSE;
260 if (event->state & GDK_BUTTON1_MASK) {
262 if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) {
263 if (event->state & GDK_CONTROL_MASK)
264 what = process_rotation(event);
266 if (event->state & GDK_SHIFT_MASK)
267 what |= process_zoom(event);
268 } else
269 what = process_move(event);
271 refresh(what);
273 update_coordinates1(event);
274 skip = warp_pointer(event->x_root, event->y_root);
276 } else if (event->state & GDK_BUTTON3_MASK) {
278 /* We draw the zoom rectangle. */
279 set_zoom_frame(old3_win_x, old3_win_y,
280 (gint) event->x - old3_win_x,
281 (gint) event->y - old3_win_y);
283 draw_zoom_frame();
286 return TRUE;
289 /* Called when pressing an arrow key. */
290 static void move_or_rotate(guint state, gfloat x, gfloat y, gfloat angle)
292 guint what = REFRESH_IMAGE | APPEND_HISTORY;
294 if (state & GDK_CONTROL_MASK) {
295 matrix_rotate(angle);
296 what |= REFRESH_STATUS;
297 } else
298 matrix_move(x, y);
300 refresh(what);
303 /* Mask to keep track of which arrow keys are pressed. */
304 #define ARROW_UP (1 << 0)
305 #define ARROW_LEFT (1 << 1)
306 #define ARROW_RIGHT (1 << 2)
307 #define ARROW_DOWN (1 << 3)
309 /* Called twice to know how far we move in the two directions. */
310 static gfloat get_arrow_move(guint state, guint flag_pos, guint flag_neg)
312 guint flags;
314 flags = state & (flag_pos | flag_neg);
316 if (flags == (flag_pos | flag_neg) || flags == 0)
317 /* Both keys are pressed, or none => don't move. */
318 return 0.0;
320 if (state & flag_pos)
321 /* Left or up. */
322 return MOVE_OFFSET;
324 /* Right or down. */
325 return -MOVE_OFFSET;
328 /* An arrow key or something else was either pressed or released. */
329 static gboolean process_arrow_key(GdkEventKey * event, gboolean press)
331 static guint arrow_state = 0;
332 guint arrow;
333 gfloat x, y, angle;
335 if (current_image == NULL)
336 return FALSE;
338 switch (event->keyval) {
339 case GDK_Up:
340 case GDK_KP_Up:
341 arrow = ARROW_UP;
342 angle = BIG_ROTATION;
343 break;
345 case GDK_Left:
346 case GDK_KP_Left:
347 arrow = ARROW_LEFT;
348 angle = SMALL_ROTATION;
349 break;
351 case GDK_Right:
352 case GDK_KP_Right:
353 arrow = ARROW_RIGHT;
354 angle = -SMALL_ROTATION;
355 break;
357 case GDK_Down:
358 case GDK_KP_Down:
359 arrow = ARROW_DOWN;
360 angle = -BIG_ROTATION;
361 break;
363 default:
364 /* Key not found, try a keyboard accelerator. */
365 return FALSE;
368 if (press)
369 arrow_state |= arrow;
370 else
371 arrow_state &= ~arrow;
373 if (press) {
374 x = get_arrow_move(arrow_state, ARROW_LEFT, ARROW_RIGHT);
375 y = get_arrow_move(arrow_state, ARROW_UP, ARROW_DOWN);
377 move_or_rotate(event->state, x, y, angle);
380 return TRUE;
383 static gboolean key_press_event(GtkWidget * unused, GdkEventKey * event)
385 /* Most keys are handled via keyboard accelerators. */
386 switch (event->keyval) {
387 case GDK_Escape:
388 gui_quit();
389 break;
391 case GDK_KP_Space:
392 case GDK_space:
393 case GDK_Page_Down:
394 case GDK_KP_Page_Down:
395 load_direction(1);
396 break;
398 case GDK_BackSpace:
399 case GDK_Page_Up:
400 case GDK_KP_Page_Up:
401 load_direction(-1);
402 break;
404 case GDK_KP_Subtract:
405 zoom_in(1.0 / ZOOM_FACTOR);
406 break;
408 case GDK_KP_Add:
409 case GDK_equal:
410 zoom_in(ZOOM_FACTOR);
411 break;
413 default:
414 /* Key not found, try an arrow key, */
415 return process_arrow_key(event, TRUE);
418 return TRUE;
421 static gboolean key_release_event(GtkWidget * unused, GdkEventKey * event)
423 return process_arrow_key(event, FALSE);
426 static gboolean scroll_event(GtkWidget * unused, GdkEventScroll * event)
428 if (current_image == NULL)
429 return FALSE;
431 if (mouse_wheel_zoom) {
433 if (event->direction == GDK_SCROLL_UP)
434 zoom_in(1.0 / ZOOM_FACTOR);
436 else if (event->direction == GDK_SCROLL_DOWN)
437 zoom_in(ZOOM_FACTOR);
439 } else {
440 /* mouse_wheel_zoom == FALSE */
442 if (event->direction == GDK_SCROLL_UP)
443 load_direction(-1);
445 else if (event->direction == GDK_SCROLL_DOWN)
446 load_direction(1);
449 return TRUE;
452 void install_callbacks(gpointer object)
454 g_signal_connect(object, "button-press-event",
455 G_CALLBACK(button_press_event), NULL);
457 g_signal_connect(object, "button-release-event",
458 G_CALLBACK(button_release_event), NULL);
460 g_signal_connect(object, "motion-notify-event",
461 G_CALLBACK(motion_notify_event), NULL);
463 g_signal_connect(object, "key-press-event",
464 G_CALLBACK(key_press_event), NULL);
466 g_signal_connect(object, "key-release-event",
467 G_CALLBACK(key_release_event), NULL);
469 g_signal_connect(object, "delete-event", G_CALLBACK(gui_quit), NULL);
471 g_signal_connect(object, "scroll-event", G_CALLBACK(scroll_event), NULL);
473 g_signal_connect_after(object, "button-press-event",
474 G_CALLBACK(set_correct_cursor), NULL);
476 g_signal_connect_after(object, "button-release-event",
477 G_CALLBACK(set_correct_cursor), NULL);
479 g_signal_connect_after(object, "key-press-event",
480 G_CALLBACK(set_correct_cursor), NULL);
482 g_signal_connect_after(object, "key-release-event",
483 G_CALLBACK(set_correct_cursor), NULL);
485 g_signal_connect_after(object, "focus-in-event",
486 G_CALLBACK(set_correct_cursor), NULL);
489 static gboolean finish(gpointer data)
491 gboolean *b = data;
492 *b = TRUE;
493 return FALSE;
497 * We call process_events() when rebuilding the images menus and to wait for a
498 * refresh.
500 void process_events(void)
502 GTimeVal beginning, now;
503 gint delay = 0;
504 gboolean stop = FALSE;
506 if (!gtk_events_pending())
507 return;
509 g_idle_add(finish, &stop);
510 g_get_current_time(&beginning);
512 while (!stop && delay < G_USEC_PER_SEC / 10 && gtk_events_pending()) {
513 gtk_main_iteration_do(FALSE);
514 g_get_current_time(&now);
515 delay = (now.tv_sec - beginning.tv_sec) * G_USEC_PER_SEC +
516 now.tv_usec - beginning.tv_usec;
519 if (!stop)
520 g_idle_remove_by_data(&stop);