Bump gEDA version
[geda-gaf.git] / gschem / src / x_event.c
blobdbbc531734151c3319f5beb1337e231c22139eab
1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2020 gEDA Contributors (see ChangeLog for details)
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 #include <config.h>
22 #include <stdio.h>
23 #include <math.h>
24 #ifdef HAVE_STDLIB_H
25 #include <stdlib.h>
26 #endif
28 #include "gschem.h"
29 #include <gdk/gdkkeysyms.h>
32 /* used for the stroke stuff */
33 static int DOING_STROKE = FALSE;
35 /*! \brief Redraws the view when widget is exposed.
37 * \param [in] view The GschemPageView.
38 * \param [in] event The event structure.
39 * \param [in] w_current The GschemToplevel.
40 * \returns FALSE to propagate the event further.
43 gint
44 x_event_expose(GschemPageView *view, GdkEventExpose *event, GschemToplevel *w_current)
46 gschem_page_view_redraw (view, event, w_current);
48 return(0);
53 /*! \todo Finish function documentation!!!
54 * \brief
55 * \par Function Description
58 gint
59 x_event_raise_dialog_boxes (GschemPageView *view, GdkEventExpose *event, GschemToplevel *w_current)
61 g_return_val_if_fail (w_current != NULL, 0);
63 /* raise the dialog boxes if this feature is enabled */
64 if (w_current->raise_dialog_boxes) {
65 x_dialog_raise_all(w_current);
68 return 0;
74 /*! \todo Finish function documentation!!!
75 * \brief
76 * \par Function Description
79 gint
80 x_event_button_pressed(GschemPageView *page_view, GdkEventButton *event, GschemToplevel *w_current)
82 PAGE *page = gschem_page_view_get_page (page_view);
83 int w_x, w_y;
84 int unsnapped_wx, unsnapped_wy;
86 g_return_val_if_fail ((w_current != NULL), 0);
88 if (page == NULL) {
89 return TRUE; /* terminate event */
92 if (!gtk_widget_has_focus (GTK_WIDGET (page_view))) {
93 gtk_widget_grab_focus (GTK_WIDGET (page_view));
96 scm_dynwind_begin (0);
97 g_dynwind_window (w_current);
99 #if DEBUG
100 printf("pressed button %d! \n", event->button);
101 printf("event state: %d \n", event->state);
102 printf("w_current state: %d \n", w_current->event_state);
103 printf("Selection is:\n");
104 o_selection_print_all(&(page->selection_list));
105 printf("\n");
106 #endif
108 gschem_page_view_SCREENtoWORLD (page_view, (int) event->x, (int) event->y,
109 &unsnapped_wx, &unsnapped_wy);
110 w_x = snap_grid (w_current, unsnapped_wx);
111 w_y = snap_grid (w_current, unsnapped_wy);
113 if (event->type == GDK_2BUTTON_PRESS &&
114 w_current->event_state == SELECT) {
115 /* Don't re-select an object (lp-912978) */
116 /* o_find_object(w_current, w_x, w_y, TRUE); */
118 /* GDK_BUTTON_EVENT is emitted before GDK_2BUTTON_EVENT, which
119 * leads to setting of the inside_action flag. If o_edit()
120 * brings up a modal window (e.g., the edit attribute dialog),
121 * it intercepts the release button event and thus doesn't
122 * allow resetting of the inside_action flag so we do it
123 * manually here before processing the double-click event. */
124 i_action_stop (w_current);
125 o_edit (w_current, geda_list_get_glist (page->selection_list), TRUE);
126 scm_dynwind_end ();
127 return(0);
130 w_current->SHIFTKEY = (event->state & GDK_SHIFT_MASK ) ? 1 : 0;
131 w_current->CONTROLKEY = (event->state & GDK_CONTROL_MASK) ? 1 : 0;
132 w_current->ALTKEY = (event->state & GDK_MOD1_MASK) ? 1 : 0;
134 /* Huge switch statement to evaluate state transitions. Jump to
135 * end_button_pressed label to escape the state evaluation rather than
136 * returning from the function directly. */
138 if (event->button == 1) {
139 if (w_current->inside_action) {
140 /* End action */
141 if (page->place_list != NULL) {
142 switch(w_current->event_state) {
143 case (COMPMODE) : o_place_end(w_current, w_x, w_y,
144 w_current->continue_component_place, FALSE,
145 "%add-objects-hook", _("Add Component")); break;
146 case (TEXTMODE) : o_place_end(w_current, w_x, w_y, FALSE, FALSE,
147 "%add-objects-hook", _("Add Text")); break;
148 case (PASTEMODE) : o_place_end(w_current, w_x, w_y, FALSE, TRUE,
149 "%paste-objects-hook", _("Paste")); break;
150 default: break;
152 } else {
153 switch(w_current->event_state) {
154 case (ARCMODE) : o_arc_end1(w_current, w_x, w_y); break;
155 case (BOXMODE) : o_box_end(w_current, w_x, w_y); break;
156 case (BUSMODE) : o_bus_end(w_current, w_x, w_y); break;
157 case (CIRCLEMODE) : o_circle_end(w_current, w_x, w_y); break;
158 case (LINEMODE) : o_line_end(w_current, w_x, w_y); break;
159 case (NETMODE) : o_net_end(w_current, w_x, w_y); break;
160 case (PATHMODE) : o_path_continue (w_current, w_x, w_y); break;
161 case (PICTUREMODE): o_picture_end(w_current, w_x, w_y); break;
162 case (PINMODE) : o_pin_end (w_current, w_x, w_y); break;
163 case (OGNRSTMODE) : o_ognrst_end (w_current, w_x, w_y); break;
164 default: break;
167 } else {
168 /* Start action */
169 switch(w_current->event_state) {
170 case (ARCMODE) : o_arc_start(w_current, w_x, w_y); break;
171 case (BOXMODE) : o_box_start(w_current, w_x, w_y); break;
172 case (BUSMODE) : o_bus_start(w_current, w_x, w_y); break;
173 case (CIRCLEMODE) : o_circle_start(w_current, w_x, w_y); break;
174 case (LINEMODE) : o_line_start(w_current, w_x, w_y); break;
175 case (NETMODE) : o_net_start(w_current, w_x, w_y); break;
176 case (PATHMODE) : o_path_start (w_current, w_x, w_y); break;
177 case (PICTUREMODE): o_picture_start(w_current, w_x, w_y); break;
178 case (PINMODE) : o_pin_start (w_current, w_x, w_y); break;
179 case (ZOOMBOX) : a_zoom_box_start(w_current, unsnapped_wx, unsnapped_wy); break;
180 case (SELECT) : o_select_start(w_current, w_x, w_y); break;
182 case (COPYMODE) :
183 case (MCOPYMODE) : o_copy_start(w_current, w_x, w_y); break;
184 case (MOVEMODE) : o_move_start(w_current, w_x, w_y); break;
185 default: break;
189 switch(w_current->event_state) {
190 case(ROTATEMODE): o_rotate_world_update(w_current, w_x, w_y, 90,
191 geda_list_get_glist(page->selection_list)); break;
192 case(MIRRORMODE): o_mirror_world_update(w_current, w_x, w_y,
193 geda_list_get_glist(page->selection_list)); break;
195 case(PAN):
196 gschem_page_view_pan (page_view, w_x, w_y);
197 if (w_current->undo_panzoom)
198 o_undo_savestate (w_current, page, UNDO_VIEWPORT_ONLY, _("Pan"));
199 i_set_state(w_current, SELECT);
200 break;
202 } else if (event->button == 2) {
204 /* try this out and see how it behaves */
205 if (w_current->inside_action) {
206 if (w_current->event_state == OGNRSTMODE &&
207 w_current->middle_button == MID_MOUSEPAN_ENABLED)
208 gschem_page_view_pan_start (page_view, (int) event->x, (int) event->y);
209 else if (
210 !(w_current->event_state == COMPMODE||
211 w_current->event_state == TEXTMODE||
212 w_current->event_state == MOVEMODE||
213 w_current->event_state == COPYMODE ||
214 w_current->event_state == MCOPYMODE ||
215 w_current->event_state == PASTEMODE )) {
216 i_cancel (w_current);
218 goto end_button_pressed;
221 switch(w_current->middle_button) {
223 case(ACTION):
225 /* don't want to search if shift */
226 /* key is pressed */
227 if (!w_current->SHIFTKEY) {
228 o_find_object(w_current, unsnapped_wx, unsnapped_wy, TRUE);
231 /* make sure the list is not empty */
232 if (!o_select_selected(w_current)) {
233 /* this means the above find did not
234 * find anything */
235 i_action_stop (w_current);
236 i_set_state(w_current, SELECT);
237 goto end_button_pressed;
240 /* determine here if copy or move */
241 if (w_current->ALTKEY) {
242 i_set_state(w_current, COPYMODE);
243 o_copy_start(w_current, w_x, w_y);
244 } else {
245 o_move_start(w_current, w_x, w_y);
247 break;
249 case(REPEAT):
250 if (w_current->last_action != NULL) {
251 scm_dynwind_begin (0);
252 g_dynwind_window (w_current);
253 g_scm_eval_protected (scm_list_2 (
254 scm_variable_ref (
255 scm_c_public_variable (
256 "gschem action",
257 "eval-action-at-point!")),
258 w_current->last_action->smob),
259 SCM_UNDEFINED);
260 scm_dynwind_end ();
262 break;
263 case(STROKE):
264 DOING_STROKE=TRUE;
265 break;
267 case(MID_MOUSEPAN_ENABLED):
268 gschem_page_view_pan_start (page_view, (int) event->x, (int) event->y);
269 break;
272 } else if (event->button == 3) {
273 if (!w_current->inside_action) {
274 if (w_current->third_button == POPUP_ENABLED) {
275 /* (third-button "popup") */
276 i_update_menus(w_current); /* update menus before popup */
277 if (w_current->popup_menu != NULL)
278 gtk_menu_popup (GTK_MENU (w_current->popup_menu),
279 NULL, NULL, NULL, NULL,
280 event->button, event->time);
281 } else {
282 /* (third-button "mousepan") */
283 gschem_page_view_pan_start (page_view, (int) event->x, (int) event->y);
285 } else {
286 if ((w_current->third_button == MOUSEPAN_ENABLED) &&
287 (!w_current->third_button_cancel)) {
288 gschem_page_view_pan_start (page_view, (int) event->x, (int) event->y);
289 } else { /* this is the default cancel */
291 /* reset all draw and place actions */
293 switch (w_current->event_state) {
295 case (ARCMODE) : o_arc_invalidate_rubber (w_current); break;
296 case (BOXMODE) : o_box_invalidate_rubber (w_current); break;
297 case (BUSMODE) : o_bus_invalidate_rubber (w_current); break;
298 case (CIRCLEMODE) : o_circle_invalidate_rubber (w_current); break;
299 case (LINEMODE) : o_line_invalidate_rubber (w_current); break;
300 case (NETMODE) : o_net_reset (w_current); break;
301 case (PATHMODE) : o_path_end_path (w_current); break;
302 case (PICTUREMODE): o_picture_invalidate_rubber (w_current); break;
303 case (PINMODE) : o_pin_invalidate_rubber (w_current); break;
305 default:
306 i_cancel (w_current);
307 break;
313 end_button_pressed:
314 scm_dynwind_end ();
316 return(0);
319 /*! \todo Finish function documentation!!!
320 * \brief
321 * \par Function Description
324 gint
325 x_event_button_released (GschemPageView *page_view, GdkEventButton *event, GschemToplevel *w_current)
327 PAGE *page = gschem_page_view_get_page (page_view);
328 int unsnapped_wx, unsnapped_wy;
329 int w_x, w_y;
331 g_return_val_if_fail ((page_view != NULL), 0);
332 g_return_val_if_fail ((w_current != NULL), 0);
334 if (page == NULL) {
335 return TRUE; /* terminate event */
338 #if DEBUG
339 printf("released! %d \n", w_current->event_state);
340 #endif
342 w_current->SHIFTKEY = (event->state & GDK_SHIFT_MASK ) ? 1 : 0;
343 w_current->CONTROLKEY = (event->state & GDK_CONTROL_MASK) ? 1 : 0;
344 w_current->ALTKEY = (event->state & GDK_MOD1_MASK) ? 1 : 0;
346 gschem_page_view_SCREENtoWORLD (page_view, (int) event->x, (int) event->y,
347 &unsnapped_wx, &unsnapped_wy);
348 w_x = snap_grid (w_current, unsnapped_wx);
349 w_y = snap_grid (w_current, unsnapped_wy);
351 /* Huge switch statement to evaluate state transitions. Jump to
352 * end_button_released label to escape the state evaluation rather
353 * than returning from the function directly. */
354 scm_dynwind_begin (0);
355 g_dynwind_window (w_current);
357 if (event->button == 1) {
359 if (w_current->inside_action) {
360 if (page->place_list != NULL) {
361 switch(w_current->event_state) {
362 case (COPYMODE) :
363 case (MCOPYMODE) : o_copy_end(w_current); break;
364 case (MOVEMODE) : o_move_end(w_current); break;
365 default: break;
367 } else {
368 switch(w_current->event_state) {
369 case (GRIPS) : o_grips_end(w_current); break;
370 case (PATHMODE) : o_path_end (w_current, w_x, w_y); break;
371 case (SBOX) : o_select_box_end(w_current, unsnapped_wx, unsnapped_wy); break;
372 case (SELECT) : o_select_end(w_current, unsnapped_wx, unsnapped_wy); break;
373 case (ZOOMBOX) : a_zoom_box_end(w_current, unsnapped_wx, unsnapped_wy); break;
374 default: break;
378 } else if (event->button == 2) {
380 if (w_current->inside_action) {
381 if (w_current->event_state == COMPMODE||
382 w_current->event_state == TEXTMODE||
383 w_current->event_state == MOVEMODE||
384 w_current->event_state == COPYMODE ||
385 w_current->event_state == MCOPYMODE ||
386 w_current->event_state == PASTEMODE ) {
388 if (w_current->event_state == MOVEMODE) {
389 o_move_invalidate_rubber (w_current, FALSE);
390 } else {
391 o_place_invalidate_rubber (w_current, FALSE);
393 w_current->rubber_visible = 0;
395 o_place_rotate(w_current);
397 if (w_current->event_state == COMPMODE) {
398 o_complex_place_changed_run_hook (w_current);
401 if (w_current->event_state == MOVEMODE) {
402 o_move_invalidate_rubber (w_current, TRUE);
403 } else {
404 o_place_invalidate_rubber (w_current, TRUE);
406 w_current->rubber_visible = 1;
407 goto end_button_released;
411 switch(w_current->middle_button) {
412 case(ACTION):
413 if (w_current->inside_action && (page->place_list != NULL)) {
414 switch(w_current->event_state) {
415 case (COPYMODE): o_copy_end(w_current); break;
416 case (MOVEMODE): o_move_end(w_current); break;
419 break;
421 case(STROKE):
422 DOING_STROKE = FALSE;
423 x_stroke_translate_and_execute (w_current);
424 break;
426 case(MID_MOUSEPAN_ENABLED):
427 if (gschem_page_view_pan_end (page_view) && w_current->undo_panzoom) {
428 o_undo_savestate_old (w_current, UNDO_VIEWPORT_ONLY, _("Pan"));
430 break;
433 } else if (event->button == 3) {
434 /* just for ending a mouse pan */
435 if (gschem_page_view_pan_end (page_view) && w_current->undo_panzoom) {
436 o_undo_savestate_old (w_current, UNDO_VIEWPORT_ONLY, _("Pan"));
439 end_button_released:
440 scm_dynwind_end ();
442 return(0);
445 /*! \todo Finish function documentation!!!
446 * \brief
447 * \par Function Description
450 gint
451 x_event_motion (GschemPageView *page_view, GdkEventMotion *event, GschemToplevel *w_current)
453 PAGE *page = gschem_page_view_get_page (page_view);
454 int w_x, w_y;
455 int unsnapped_wx, unsnapped_wy;
456 int skip_event=0;
457 GdkEvent *test_event;
459 g_return_val_if_fail ((w_current != NULL), 0);
461 if (page == NULL) {
462 return TRUE; /* terminate event */
465 w_current->SHIFTKEY = (event->state & GDK_SHIFT_MASK ) ? 1 : 0;
466 w_current->CONTROLKEY = (event->state & GDK_CONTROL_MASK) ? 1 : 0;
467 w_current->ALTKEY = (event->state & GDK_MOD1_MASK) ? 1 : 0;
469 #if DEBUG
470 /* printf("MOTION!\n");*/
471 #endif
473 if (DOING_STROKE == TRUE) {
474 x_stroke_record (w_current, event->x, event->y);
475 return(0);
478 /* skip the moving event if there are other moving events in the
479 gdk event queue (Werner)
480 Only skip the event if is the same event and no buttons or modifier
481 keys changed*/
482 if ((test_event = gdk_event_get()) != NULL) {
483 if (test_event->type == GDK_MOTION_NOTIFY
484 && ((GdkEventMotion *) test_event)->state == event->state) {
485 skip_event= 1;
487 gdk_event_put(test_event); /* put it back in front of the queue */
488 gdk_event_free(test_event);
489 if (skip_event == 1)
490 return 0;
493 gschem_page_view_SCREENtoWORLD (page_view, (int) event->x, (int) event->y,
494 &unsnapped_wx, &unsnapped_wy);
495 w_x = snap_grid (w_current, unsnapped_wx);
496 w_y = snap_grid (w_current, unsnapped_wy);
498 gschem_bottom_widget_set_coordinates (
499 GSCHEM_BOTTOM_WIDGET (w_current->bottom_widget), w_x, w_y);
501 gschem_page_view_pan_motion (page_view, w_current->mousepan_gain, (int) event->x, (int) event->y);
503 /* Huge switch statement to evaluate state transitions. Jump to
504 * end_motion label to escape the state evaluation rather
505 * than returning from the function directly. */
506 scm_dynwind_begin (0);
507 g_dynwind_window (w_current);
509 if (w_current->inside_action) {
510 if (page->place_list != NULL) {
511 switch(w_current->event_state) {
512 case (COPYMODE) :
513 case (MCOPYMODE) :
514 case (COMPMODE) :
515 case (PASTEMODE) :
516 case (TEXTMODE) : o_place_motion (w_current, w_x, w_y); break;
517 case (MOVEMODE) : o_move_motion (w_current, w_x, w_y); break;
518 default: break;
520 } else {
521 switch(w_current->event_state) {
522 case (ARCMODE) : o_arc_motion (w_current, w_x, w_y, ARC_RADIUS); break;
523 case (BOXMODE) : o_box_motion (w_current, w_x, w_y); break;
524 case (BUSMODE) : o_bus_motion (w_current, w_x, w_y); break;
525 case (CIRCLEMODE) : o_circle_motion (w_current, w_x, w_y); break;
526 case (LINEMODE) : o_line_motion (w_current, w_x, w_y); break;
527 case (NETMODE) : o_net_motion (w_current, w_x, w_y); break;
528 case (PATHMODE) : o_path_motion (w_current, w_x, w_y); break;
529 case (PICTUREMODE): o_picture_motion (w_current, w_x, w_y); break;
530 case (PINMODE) : o_pin_motion (w_current, w_x, w_y); break;
531 case (GRIPS) : o_grips_motion(w_current, w_x, w_y); break;
532 case (SBOX) : o_select_box_motion (w_current, unsnapped_wx, unsnapped_wy); break;
533 case (ZOOMBOX) : a_zoom_box_motion (w_current, unsnapped_wx, unsnapped_wy); break;
534 case (SELECT) : o_select_motion (w_current, w_x, w_y); break;
535 case (OGNRSTMODE) : o_ognrst_motion (w_current, w_x, w_y); break;
536 default: break;
539 } else {
540 switch(w_current->event_state) {
541 case(NETMODE) : o_net_start_magnetic(w_current, w_x, w_y); break;
542 default: break;
546 scm_dynwind_end ();
548 return(0);
551 /*! \brief Updates the display when drawing area is configured.
552 * \par Function Description
553 * This is the callback function connected to the configure event of
554 * the GschemPageView of the main window.
556 * It re-pans each of its pages to keep their contents centered in the
557 * GschemPageView.
559 * When the window is maximised, the zoom of every page is changed to
560 * best fit the previously displayed area of the page in the new
561 * area. Otherwise the current zoom level is left unchanged.
563 * \param [in] widget The GschemPageView which received the signal.
564 * \param [in] event The event structure of signal configure-event.
565 * \param [in] unused
566 * \returns FALSE to propagate the event further.
568 gboolean
569 x_event_configure (GschemPageView *page_view,
570 GdkEventConfigure *event,
571 gpointer unused)
573 GtkAllocation current_allocation;
574 GList *iter;
575 PAGE *p_current = gschem_page_view_get_page (page_view);
577 if (p_current == NULL) {
578 /* don't want to call this if the current page isn't setup yet */
579 return FALSE;
582 g_return_val_if_fail (p_current->toplevel != NULL, FALSE);
584 gtk_widget_get_allocation (GTK_WIDGET(page_view), &current_allocation);
586 if ((current_allocation.width == page_view->previous_allocation.width) &&
587 (current_allocation.height == page_view->previous_allocation.height)) {
588 /* the size of the drawing area has not changed -- nothing to do here */
589 return FALSE;
592 page_view->previous_allocation = current_allocation;
594 /* re-pan each page of the TOPLEVEL */
595 for ( iter = geda_list_get_glist (p_current->toplevel->pages);
596 iter != NULL;
597 iter = g_list_next (iter) ) {
599 gschem_page_view_set_page (page_view, (PAGE *)iter->data);
601 if (page_view->configured) {
602 gschem_page_view_pan_mouse (page_view, 0, 0);
603 } else {
604 gschem_page_view_zoom_extents (page_view, NULL);
608 page_view->configured = TRUE;
610 gschem_page_view_set_page (page_view, p_current);
612 return FALSE;
615 /*! \todo Finish function documentation!!!
616 * \brief
617 * \par Function Description
620 gint x_event_enter(GtkWidget *widget, GdkEventCrossing *event,
621 GschemToplevel *w_current)
623 g_return_val_if_fail ((w_current != NULL), 0);
624 /* do nothing or now */
625 return(0);
628 /*! \brief Callback to handle key events in the drawing area.
629 * \par Function Description
631 * GTK+ callback function (registered in x_window_setup_draw_events() ) which
632 * handles key press and release events from the GTK+ system.
634 * \param [in] widget the widget that generated the event
635 * \param [in] event the event itself
636 * \param w_current the toplevel environment
637 * \returns TRUE if the event has been handled.
639 gboolean
640 x_event_key (GschemPageView *page_view, GdkEventKey *event, GschemToplevel *w_current)
642 gboolean retval = FALSE;
643 int pressed;
644 gboolean special = FALSE;
646 g_return_val_if_fail (page_view != NULL, FALSE);
648 #if DEBUG
649 printf("x_event_key_pressed: Pressed key %i.\n", event->keyval);
650 #endif
652 /* update the state of the modifiers */
653 w_current->ALTKEY = (event->state & GDK_MOD1_MASK) ? 1 : 0;
654 w_current->SHIFTKEY = (event->state & GDK_SHIFT_MASK) ? 1 : 0;
655 w_current->CONTROLKEY = (event->state & GDK_CONTROL_MASK) ? 1 : 0;
657 pressed = (event->type == GDK_KEY_PRESS) ? 1 : 0;
659 switch (event->keyval) {
660 case GDK_Alt_L:
661 case GDK_Alt_R:
662 w_current->ALTKEY = pressed;
663 break;
665 case GDK_Shift_L:
666 case GDK_Shift_R:
667 w_current->SHIFTKEY = pressed;
668 special = TRUE;
669 break;
671 case GDK_Control_L:
672 case GDK_Control_R:
673 w_current->CONTROLKEY = pressed;
674 special = TRUE;
675 break;
678 scm_dynwind_begin (0);
679 g_dynwind_window (w_current);
681 /* Special case to update the object being drawn or placed after
682 * scrolling when Shift or Control were pressed */
683 if (special) {
684 x_event_faked_motion (page_view, event);
687 if (pressed)
688 retval = g_keys_execute (w_current, event) ? TRUE : FALSE;
690 scm_dynwind_end ();
692 return retval;
696 /*! \todo Finish function documentation!!!
697 * \brief
698 * \par Function Description
700 * \param [in] widget The GschemPageView with the scroll event.
701 * \param [in] event
702 * \param [in] w_current
704 gint x_event_scroll (GtkWidget *widget, GdkEventScroll *event,
705 GschemToplevel *w_current)
707 GtkAdjustment *adj;
708 gboolean pan_xaxis = FALSE;
709 gboolean pan_yaxis = FALSE;
710 gboolean zoom = FALSE;
711 int pan_direction = 1;
712 int zoom_direction = ZOOM_IN;
713 GschemPageView *view = NULL;
714 PAGE *page = NULL;
716 g_return_val_if_fail ((w_current != NULL), 0);
718 view = GSCHEM_PAGE_VIEW (widget);
719 g_return_val_if_fail ((view != NULL), 0);
721 page = gschem_page_view_get_page (view);
723 if (page == NULL) {
724 return FALSE; /* we cannot zoom page if it doesn't exist :) */
727 /* update the state of the modifiers */
728 w_current->SHIFTKEY = (event->state & GDK_SHIFT_MASK ) ? 1 : 0;
729 w_current->CONTROLKEY = (event->state & GDK_CONTROL_MASK) ? 1 : 0;
730 w_current->ALTKEY = (event->state & GDK_MOD1_MASK) ? 1 : 0;
732 if (w_current->scroll_wheel == SCROLL_WHEEL_CLASSIC) {
733 /* Classic gschem behaviour */
734 zoom = !w_current->CONTROLKEY && !w_current->SHIFTKEY;
735 pan_yaxis = !w_current->CONTROLKEY && w_current->SHIFTKEY;
736 pan_xaxis = w_current->CONTROLKEY && !w_current->SHIFTKEY;
737 } else {
738 /* GTK style behaviour */
739 zoom = w_current->CONTROLKEY && !w_current->SHIFTKEY;
740 pan_yaxis = !w_current->CONTROLKEY && !w_current->SHIFTKEY;
741 pan_xaxis = !w_current->CONTROLKEY && w_current->SHIFTKEY;
744 /* If the user has a left/right scroll wheel, always scroll the y-axis */
745 if (event->direction == GDK_SCROLL_LEFT ||
746 event->direction == GDK_SCROLL_RIGHT) {
747 zoom = FALSE;
748 pan_yaxis = FALSE;
749 pan_xaxis = TRUE;
752 /* You must have scrollbars enabled if you want to use the scroll wheel to pan */
753 if (!w_current->scrollbars_flag) {
754 pan_xaxis = FALSE;
755 pan_yaxis = FALSE;
758 switch (event->direction) {
759 case GDK_SCROLL_UP:
760 case GDK_SCROLL_LEFT:
761 pan_direction = -1;
762 zoom_direction = ZOOM_IN;
763 break;
764 case GDK_SCROLL_DOWN:
765 case GDK_SCROLL_RIGHT:
766 pan_direction = 1;
767 zoom_direction = ZOOM_OUT;
768 break;
771 if (zoom) {
772 /*! \todo Change "HOTKEY" TO new "MOUSE" specifier? */
773 a_zoom(w_current, GSCHEM_PAGE_VIEW (widget), zoom_direction, HOTKEY);
776 if (pan_xaxis) {
777 adj = gschem_page_view_get_hadjustment (GSCHEM_PAGE_VIEW (widget));
778 g_return_val_if_fail (adj != NULL, TRUE);
779 gtk_adjustment_set_value(adj, min(adj->value + pan_direction *
780 (adj->page_increment /
781 w_current->scrollpan_steps),
782 adj->upper - adj->page_size));
785 if (pan_yaxis) {
786 adj = gschem_page_view_get_vadjustment (GSCHEM_PAGE_VIEW (widget));
787 g_return_val_if_fail (adj != NULL, TRUE);
788 gtk_adjustment_set_value(adj, min(adj->value + pan_direction *
789 (adj->page_increment /
790 w_current->scrollpan_steps),
791 adj->upper - adj->page_size));
794 if (w_current->undo_panzoom && (zoom || pan_xaxis || pan_yaxis)) {
795 o_undo_savestate_old (w_current, UNDO_VIEWPORT_ONLY,
796 zoom ? _("Zoom") : _("Pan"));
799 x_event_faked_motion (view, NULL);
800 /* Stop further processing of this signal */
801 return TRUE;
805 /*! \brief get the pointer position of a given GschemToplevel
806 * \par Function Description
807 * This function gets the pointer position of the drawing area of the
808 * current workspace <b>GschemToplevel</b>. The flag <b>snapped</b> specifies
809 * whether the pointer position should be snapped to the current grid.
811 * \param [in] w_current The GschemToplevel object.
812 * \param [in] snapped An option flag to specify the wished coords
813 * \param [out] wx snapped/unsnapped world x coordinate
814 * \param [out] wy snapped/unsnapped world y coordinate
816 * \return Returns TRUE if the pointer position is inside the drawing area.
819 gboolean
820 x_event_get_pointer_position (GschemToplevel *w_current, gboolean snapped, gint *wx, gint *wy)
822 int width;
823 int height;
824 int sx;
825 int sy;
826 int x;
827 int y;
829 GschemPageView *page_view = gschem_toplevel_get_current_page_view (w_current);
830 g_return_val_if_fail (page_view != NULL, FALSE);
832 g_return_val_if_fail (GTK_WIDGET (page_view)->window != NULL, FALSE);
834 /* \todo The following line is depricated in GDK 2.24 */
835 gdk_drawable_get_size (GTK_WIDGET (page_view)->window, &width, &height);
837 gtk_widget_get_pointer(GTK_WIDGET (page_view), &sx, &sy);
839 /* check if we are inside the drawing area */
840 if ((sx < 0) || (sx >= width) || (sy < 0) || (sy >= height)) {
841 return FALSE;
844 gschem_page_view_SCREENtoWORLD (page_view, sx, sy, &x, &y);
846 if (snapped) {
847 x = snap_grid (w_current, x);
848 y = snap_grid (w_current, y);
851 *wx = x;
852 *wy = y;
854 return TRUE;
857 /*! \brief Emits a faked motion event to update objects being drawn or placed
858 * \par Function Description
859 * This function emits an additional "motion-notify-event" to
860 * update objects being drawn or placed while zooming, scrolling, or
861 * panning.
863 * If its event parameter is not NULL, the current state of Shift
864 * and Control is preserved to correctly deal with special cases.
866 * \param [in] view The GschemPageView object which received the signal.
867 * \param [in] event The event structure of the signal or NULL.
868 * \returns FALSE to propagate the event further.
870 gboolean
871 x_event_faked_motion (GschemPageView *view, GdkEventKey *event) {
872 gint x, y;
873 gboolean ret;
874 GdkEventMotion *newevent;
876 gtk_widget_get_pointer (GTK_WIDGET (view), &x, &y);
877 newevent = (GdkEventMotion*)gdk_event_new(GDK_MOTION_NOTIFY);
878 newevent->x = x;
879 newevent->y = y;
881 if (event != NULL ) {
882 switch (event->keyval) {
883 case GDK_Control_L:
884 case GDK_Control_R:
885 if (event->type == GDK_KEY_PRESS) {
886 newevent->state |= GDK_CONTROL_MASK;
887 } else {
888 newevent->state &= ~GDK_CONTROL_MASK;
890 break;
892 case GDK_Shift_L:
893 case GDK_Shift_R:
894 if (event->type == GDK_KEY_PRESS) {
895 newevent->state |= GDK_SHIFT_MASK;
896 } else {
897 newevent->state &= ~GDK_SHIFT_MASK;
899 break;
903 g_signal_emit_by_name (view, "motion-notify-event", newevent, &ret);
905 gdk_event_free((GdkEvent*)newevent);
907 return FALSE;