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
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.
44 x_event_expose(GschemPageView
*view
, GdkEventExpose
*event
, GschemToplevel
*w_current
)
46 gschem_page_view_redraw (view
, event
, w_current
);
53 /*! \todo Finish function documentation!!!
55 * \par Function Description
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
);
74 /*! \todo Finish function documentation!!!
76 * \par Function Description
80 x_event_button_pressed(GschemPageView
*page_view
, GdkEventButton
*event
, GschemToplevel
*w_current
)
82 PAGE
*page
= gschem_page_view_get_page (page_view
);
84 int unsnapped_wx
, unsnapped_wy
;
86 g_return_val_if_fail ((w_current
!= NULL
), 0);
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
);
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
));
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
);
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
) {
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;
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;
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;
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;
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;
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
);
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
);
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
) {
225 /* don't want to search if shift */
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
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
);
245 o_move_start(w_current
, w_x
, w_y
);
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 (
255 scm_c_public_variable (
257 "eval-action-at-point!")),
258 w_current
->last_action
->smob
),
267 case(MID_MOUSEPAN_ENABLED
):
268 gschem_page_view_pan_start (page_view
, (int) event
->x
, (int) event
->y
);
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
);
282 /* (third-button "mousepan") */
283 gschem_page_view_pan_start (page_view
, (int) event
->x
, (int) event
->y
);
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;
306 i_cancel (w_current
);
319 /*! \todo Finish function documentation!!!
321 * \par Function Description
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
;
331 g_return_val_if_fail ((page_view
!= NULL
), 0);
332 g_return_val_if_fail ((w_current
!= NULL
), 0);
335 return TRUE
; /* terminate event */
339 printf("released! %d \n", w_current
->event_state
);
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
) {
363 case (MCOPYMODE
) : o_copy_end(w_current
); break;
364 case (MOVEMODE
) : o_move_end(w_current
); break;
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;
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
);
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
);
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
) {
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;
422 DOING_STROKE
= FALSE
;
423 x_stroke_translate_and_execute (w_current
);
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"));
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"));
445 /*! \todo Finish function documentation!!!
447 * \par Function Description
451 x_event_motion (GschemPageView
*page_view
, GdkEventMotion
*event
, GschemToplevel
*w_current
)
453 PAGE
*page
= gschem_page_view_get_page (page_view
);
455 int unsnapped_wx
, unsnapped_wy
;
457 GdkEvent
*test_event
;
459 g_return_val_if_fail ((w_current
!= NULL
), 0);
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;
470 /* printf("MOTION!\n");*/
473 if (DOING_STROKE
== TRUE
) {
474 x_stroke_record (w_current
, event
->x
, event
->y
);
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
482 if ((test_event
= gdk_event_get()) != NULL
) {
483 if (test_event
->type
== GDK_MOTION_NOTIFY
484 && ((GdkEventMotion
*) test_event
)->state
== event
->state
) {
487 gdk_event_put(test_event
); /* put it back in front of the queue */
488 gdk_event_free(test_event
);
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
) {
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;
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;
540 switch(w_current
->event_state
) {
541 case(NETMODE
) : o_net_start_magnetic(w_current
, w_x
, w_y
); break;
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
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.
566 * \returns FALSE to propagate the event further.
569 x_event_configure (GschemPageView
*page_view
,
570 GdkEventConfigure
*event
,
573 GtkAllocation current_allocation
;
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 */
582 g_return_val_if_fail (p_current
->toplevel
!= NULL
, FALSE
);
584 gtk_widget_get_allocation (GTK_WIDGET(page_view
), ¤t_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 */
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
);
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);
604 gschem_page_view_zoom_extents (page_view
, NULL
);
608 page_view
->configured
= TRUE
;
610 gschem_page_view_set_page (page_view
, p_current
);
615 /*! \todo Finish function documentation!!!
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 */
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.
640 x_event_key (GschemPageView
*page_view
, GdkEventKey
*event
, GschemToplevel
*w_current
)
642 gboolean retval
= FALSE
;
644 gboolean special
= FALSE
;
646 g_return_val_if_fail (page_view
!= NULL
, FALSE
);
649 printf("x_event_key_pressed: Pressed key %i.\n", event
->keyval
);
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
) {
662 w_current
->ALTKEY
= pressed
;
667 w_current
->SHIFTKEY
= pressed
;
673 w_current
->CONTROLKEY
= pressed
;
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 */
684 x_event_faked_motion (page_view
, event
);
688 retval
= g_keys_execute (w_current
, event
) ? TRUE
: FALSE
;
696 /*! \todo Finish function documentation!!!
698 * \par Function Description
700 * \param [in] widget The GschemPageView with the scroll event.
702 * \param [in] w_current
704 gint
x_event_scroll (GtkWidget
*widget
, GdkEventScroll
*event
,
705 GschemToplevel
*w_current
)
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
;
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
);
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
;
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
) {
752 /* You must have scrollbars enabled if you want to use the scroll wheel to pan */
753 if (!w_current
->scrollbars_flag
) {
758 switch (event
->direction
) {
760 case GDK_SCROLL_LEFT
:
762 zoom_direction
= ZOOM_IN
;
764 case GDK_SCROLL_DOWN
:
765 case GDK_SCROLL_RIGHT
:
767 zoom_direction
= ZOOM_OUT
;
772 /*! \todo Change "HOTKEY" TO new "MOUSE" specifier? */
773 a_zoom(w_current
, GSCHEM_PAGE_VIEW (widget
), zoom_direction
, HOTKEY
);
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
));
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 */
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.
820 x_event_get_pointer_position (GschemToplevel
*w_current
, gboolean snapped
, gint
*wx
, gint
*wy
)
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
)) {
844 gschem_page_view_SCREENtoWORLD (page_view
, sx
, sy
, &x
, &y
);
847 x
= snap_grid (w_current
, x
);
848 y
= snap_grid (w_current
, y
);
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
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.
871 x_event_faked_motion (GschemPageView
*view
, GdkEventKey
*event
) {
874 GdkEventMotion
*newevent
;
876 gtk_widget_get_pointer (GTK_WIDGET (view
), &x
, &y
);
877 newevent
= (GdkEventMotion
*)gdk_event_new(GDK_MOTION_NOTIFY
);
881 if (event
!= NULL
) {
882 switch (event
->keyval
) {
885 if (event
->type
== GDK_KEY_PRESS
) {
886 newevent
->state
|= GDK_CONTROL_MASK
;
888 newevent
->state
&= ~GDK_CONTROL_MASK
;
894 if (event
->type
== GDK_KEY_PRESS
) {
895 newevent
->state
|= GDK_SHIFT_MASK
;
897 newevent
->state
&= ~GDK_SHIFT_MASK
;
903 g_signal_emit_by_name (view
, "motion-notify-event", newevent
, &ret
);
905 gdk_event_free((GdkEvent
*)newevent
);