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_* */
28 #include "callbacks.h"
29 #include "math_floats.h" /* atan2f(), powf() */
31 #include "rendering.h"
34 #include "zoom_frame.h"
35 #include "move_pointer.h"
38 #include "next_image.h"
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
) {
67 /* Reset mouse warping. */
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
;
79 mouse_wheel_zoom
= FALSE
;
83 /* We now have one of the zoom rectangle vertex. */
84 old3_win_x
= (gint
) event
->x
;
85 old3_win_y
= (gint
) event
->y
;
92 static gboolean
button_release_event(GtkWidget
* unused
, GdkEventButton
* event
)
94 switch (event
->button
) {
96 /* Reset mouse warping. */
100 refresh(APPEND_HISTORY
);
104 mouse_wheel_zoom
= 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
)
122 new_x
= rt
->scr_width
- 1;
125 } else if (x
>= rt
->scr_width
- 1.0) {
137 new_y
= rt
->scr_height
- 1;
140 } else if (y
>= rt
->scr_height
- 1.0) {
149 move_pointer(new_x
, new_y
);
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
;
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
;
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
;
229 /* We come just after a move_pointer(). */
234 if (rt
->cursor_hidden
)
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
);
248 what
= process_move(event
);
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
);
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
;
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
)
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. */
299 if (state
& flag_pos
)
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;
314 switch (event
->keyval
) {
318 angle
= BIG_ROTATION
;
324 angle
= SMALL_ROTATION
;
330 angle
= -SMALL_ROTATION
;
336 angle
= -BIG_ROTATION
;
340 /* Key not found, try a keyboard accelerator. */
345 arrow_state
|= arrow
;
347 arrow_state
&= ~arrow
;
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
);
359 static gboolean
key_press_event(GtkWidget
* unused
, GdkEventKey
* event
)
361 /* Most keys are handled via keyboard accelerators. */
362 switch (event
->keyval
) {
377 case GDK_KP_Subtract
:
378 zoom_in(1.0 / ZOOM_FACTOR
);
384 zoom_in(ZOOM_FACTOR
);
388 /* Key not found, try an arrow key, */
389 if (process_arrow_key(event
, TRUE
) == FALSE
)
390 /* or an accelerator without the Ctrl modifier. */
391 event
->state
&= ~GDK_CONTROL_MASK
;
397 static gboolean
key_release_event(GtkWidget
* unused
, GdkEventKey
* event
)
399 return process_arrow_key(event
, FALSE
);
402 static gboolean
scroll_event(GtkWidget
* unused
, GdkEventScroll
* event
)
404 if (mouse_wheel_zoom
) {
406 if (event
->direction
== GDK_SCROLL_UP
)
407 zoom_in(1.0 / ZOOM_FACTOR
);
409 else if (event
->direction
== GDK_SCROLL_DOWN
)
410 zoom_in(ZOOM_FACTOR
);
413 /* mouse_wheel_zoom == FALSE */
415 if (event
->direction
== GDK_SCROLL_UP
)
418 else if (event
->direction
== GDK_SCROLL_DOWN
)
425 void install_callbacks(gpointer object
)
427 g_signal_connect(object
, "button-press-event",
428 G_CALLBACK(button_press_event
), NULL
);
430 g_signal_connect(object
, "button-release-event",
431 G_CALLBACK(button_release_event
), NULL
);
433 g_signal_connect(object
, "motion-notify-event",
434 G_CALLBACK(motion_notify_event
), NULL
);
436 g_signal_connect(object
, "key-press-event",
437 G_CALLBACK(key_press_event
), NULL
);
439 g_signal_connect(object
, "key-release-event",
440 G_CALLBACK(key_release_event
), NULL
);
442 g_signal_connect(object
, "delete-event", G_CALLBACK(gui_quit
), NULL
);
444 g_signal_connect(object
, "scroll-event", G_CALLBACK(scroll_event
), NULL
);
446 g_signal_connect_after(object
, "button-press-event",
447 G_CALLBACK(set_correct_cursor
), NULL
);
449 g_signal_connect_after(object
, "button-release-event",
450 G_CALLBACK(set_correct_cursor
), NULL
);
452 g_signal_connect_after(object
, "key-press-event",
453 G_CALLBACK(set_correct_cursor
), NULL
);
455 g_signal_connect_after(object
, "key-release-event",
456 G_CALLBACK(set_correct_cursor
), NULL
);
458 g_signal_connect_after(object
, "focus-in-event",
459 G_CALLBACK(set_correct_cursor
), NULL
);
463 * We call process_events() when rebuilding the images menus and to wait for a
466 void process_events(void)
468 while (gtk_events_pending() != 0)
469 gtk_main_iteration_do(FALSE
);