1 #define __SP_NODE_CONTEXT_C__
3 /*
4 * Node editing context
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * bulia byak <buliabyak@users.sf.net>
9 *
10 * This code is in public domain
11 */
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16 #include <cstring>
17 #include <string>
18 #include <gdk/gdkkeysyms.h>
19 #include "macros.h"
20 #include <glibmm/i18n.h>
21 #include "display/sp-canvas-util.h"
22 #include "object-edit.h"
23 #include "sp-path.h"
24 #include "path-chemistry.h"
25 #include "rubberband.h"
26 #include "desktop.h"
27 #include "desktop-handles.h"
28 #include "selection.h"
29 #include "pixmaps/cursor-node.xpm"
30 #include "message-context.h"
31 #include "node-context.h"
32 #include "pixmaps/cursor-node-d.xpm"
33 #include "preferences.h"
34 #include "xml/node-event-vector.h"
35 #include "style.h"
36 #include "splivarot.h"
37 #include "shape-editor.h"
38 #include "live_effects/effect.h"
40 #include "sp-lpe-item.h"
42 // needed for flash nodepath upon mouseover:
43 #include "display/canvas-bpath.h"
44 #include "display/curve.h"
46 static void sp_node_context_class_init(SPNodeContextClass *klass);
47 static void sp_node_context_init(SPNodeContext *node_context);
48 static void sp_node_context_dispose(GObject *object);
50 static void sp_node_context_setup(SPEventContext *ec);
51 static gint sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event);
52 static gint sp_node_context_item_handler(SPEventContext *event_context,
53 SPItem *item, GdkEvent *event);
55 static SPEventContextClass *parent_class;
57 GType
58 sp_node_context_get_type()
59 {
60 static GType type = 0;
61 if (!type) {
62 GTypeInfo info = {
63 sizeof(SPNodeContextClass),
64 NULL, NULL,
65 (GClassInitFunc) sp_node_context_class_init,
66 NULL, NULL,
67 sizeof(SPNodeContext),
68 4,
69 (GInstanceInitFunc) sp_node_context_init,
70 NULL, /* value_table */
71 };
72 type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "SPNodeContext", &info, (GTypeFlags)0);
73 }
74 return type;
75 }
77 static void
78 sp_node_context_class_init(SPNodeContextClass *klass)
79 {
80 GObjectClass *object_class = (GObjectClass *) klass;
81 SPEventContextClass *event_context_class = (SPEventContextClass *) klass;
83 parent_class = (SPEventContextClass*)g_type_class_peek_parent(klass);
85 object_class->dispose = sp_node_context_dispose;
87 event_context_class->setup = sp_node_context_setup;
88 event_context_class->root_handler = sp_node_context_root_handler;
89 event_context_class->item_handler = sp_node_context_item_handler;
90 }
92 static void
93 sp_node_context_init(SPNodeContext *node_context)
94 {
95 SPEventContext *event_context = SP_EVENT_CONTEXT(node_context);
97 event_context->cursor_shape = cursor_node_xpm;
98 event_context->hot_x = 1;
99 event_context->hot_y = 1;
101 node_context->leftalt = FALSE;
102 node_context->rightalt = FALSE;
103 node_context->leftctrl = FALSE;
104 node_context->rightctrl = FALSE;
106 new (&node_context->sel_changed_connection) sigc::connection();
108 node_context->flash_tempitem = NULL;
109 node_context->flashed_item = NULL;
110 node_context->remove_flash_counter = 0;
111 }
113 static void
114 sp_node_context_dispose(GObject *object)
115 {
116 SPNodeContext *nc = SP_NODE_CONTEXT(object);
117 SPEventContext *ec = SP_EVENT_CONTEXT(object);
119 ec->enableGrDrag(false);
121 nc->sel_changed_connection.disconnect();
122 nc->sel_changed_connection.~connection();
124 delete ec->shape_editor;
126 if (nc->_node_message_context) {
127 delete nc->_node_message_context;
128 }
130 G_OBJECT_CLASS(parent_class)->dispose(object);
131 }
133 static void
134 sp_node_context_setup(SPEventContext *ec)
135 {
136 SPNodeContext *nc = SP_NODE_CONTEXT(ec);
138 if (((SPEventContextClass *) parent_class)->setup)
139 ((SPEventContextClass *) parent_class)->setup(ec);
141 Inkscape::Selection *selection = sp_desktop_selection (ec->desktop);
142 nc->sel_changed_connection.disconnect();
143 nc->sel_changed_connection =
144 selection->connectChanged(sigc::bind(sigc::ptr_fun(&sp_node_context_selection_changed), (gpointer)nc));
146 SPItem *item = selection->singleItem();
148 ec->shape_editor = new ShapeEditor(ec->desktop);
150 nc->rb_escaped = false;
152 nc->cursor_drag = false;
154 nc->added_node = false;
156 nc->current_state = SP_NODE_CONTEXT_INACTIVE;
158 if (item) {
159 ec->shape_editor->set_item(item, SH_NODEPATH);
160 ec->shape_editor->set_item(item, SH_KNOTHOLDER);
161 }
163 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
164 if (prefs->getBool("/tools/nodes/selcue")) {
165 ec->enableSelectionCue();
166 }
167 if (prefs->getBool("/tools/nodes/gradientdrag")) {
168 ec->enableGrDrag();
169 }
171 ec->desktop->emitToolSubselectionChanged(NULL); // sets the coord entry fields to inactive
173 nc->_node_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack());
175 ec->shape_editor->update_statusbar();
176 }
178 static void
179 sp_node_context_flash_path(SPEventContext *event_context, SPItem *item, guint timeout) {
180 SPNodeContext *nc = SP_NODE_CONTEXT(event_context);
182 nc->remove_flash_counter = 3; // for some reason root_handler is called twice after each item_handler...
183 if (nc->flashed_item != item) {
184 // we entered a new item
185 nc->flashed_item = item;
186 SPDesktop *desktop = event_context->desktop;
187 if (nc->flash_tempitem) {
188 desktop->remove_temporary_canvasitem(nc->flash_tempitem);
189 nc->flash_tempitem = NULL;
190 }
192 SPCanvasItem *canvasitem = sp_nodepath_generate_helperpath(desktop, item);
194 if (canvasitem) {
195 nc->flash_tempitem = desktop->add_temporary_canvasitem (canvasitem, timeout);
196 }
197 }
198 }
200 /**
201 \brief Callback that processes the "changed" signal on the selection;
202 destroys old and creates new nodepath and reassigns listeners to the new selected item's repr
203 */
204 void
205 sp_node_context_selection_changed(Inkscape::Selection *selection, gpointer data)
206 {
207 SPEventContext *ec = SP_EVENT_CONTEXT(data);
209 // TODO: update ShapeEditorsCollective instead
210 ec->shape_editor->unset_item(SH_NODEPATH);
211 ec->shape_editor->unset_item(SH_KNOTHOLDER);
212 SPItem *item = selection->singleItem();
213 ec->shape_editor->set_item(item, SH_NODEPATH);
214 ec->shape_editor->set_item(item, SH_KNOTHOLDER);
215 ec->shape_editor->update_statusbar();
216 }
218 void
219 sp_node_context_show_modifier_tip(SPEventContext *event_context, GdkEvent *event)
220 {
221 sp_event_show_modifier_tip
222 (event_context->defaultMessageContext(), event,
223 _("<b>Ctrl</b>: toggle node type, snap handle angle, move hor/vert; <b>Ctrl+Alt</b>: move along handles"),
224 _("<b>Shift</b>: toggle node selection, disable snapping, rotate both handles"),
225 _("<b>Alt</b>: lock handle length; <b>Ctrl+Alt</b>: move along handles"));
226 }
228 static gint
229 sp_node_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event)
230 {
231 gint ret = FALSE;
232 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
234 if (prefs->getBool("/tools/nodes/pathflash_enabled")) {
235 if (prefs->getBool("/tools/nodes/pathflash_unselected")) {
236 SPDesktop *desktop = event_context->desktop;
237 ShapeEditor* se = event_context->shape_editor;
238 Inkscape::Selection *selection = sp_desktop_selection (desktop);
239 if (se->has_nodepath() && selection->singleItem()) {
240 return ret;
241 }
242 }
243 if (SP_IS_LPE_ITEM(item)) {
244 Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item));
245 if (lpe && (lpe->providesOwnFlashPaths() ||
246 lpe->pathFlashType() == Inkscape::LivePathEffect::SUPPRESS_FLASH)) {
247 // path should be suppressed or permanent; this is handled in
248 // sp_node_context_selection_changed()
249 return ret;
250 }
251 }
252 guint timeout = prefs->getInt("/tools/nodes/pathflash_timeout", 500);
253 sp_node_context_flash_path(event_context, item, timeout);
254 }
256 if (((SPEventContextClass *) parent_class)->item_handler)
257 ret = ((SPEventContextClass *) parent_class)->item_handler(event_context, item, event);
259 return ret;
260 }
262 static gint
263 sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
264 {
265 SPDesktop *desktop = event_context->desktop;
266 ShapeEditor* se = event_context->shape_editor;
267 Inkscape::Selection *selection = sp_desktop_selection (desktop);
268 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
270 SPNodeContext *nc = SP_NODE_CONTEXT(event_context);
271 double const nudge = prefs->getDoubleLimited("/options/nudgedistance/value", 2, 0, 1000); // in px
272 event_context->tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); // read every time, to make prefs changes really live
273 int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
274 double const offset = prefs->getDoubleLimited("/options/defaultscale/value", 2, 0, 1000);
276 if ( (nc->flash_tempitem) && (nc->remove_flash_counter <= 0) ) {
277 desktop->remove_temporary_canvasitem(nc->flash_tempitem);
278 nc->flash_tempitem = NULL;
279 nc->flashed_item = NULL; // also reset this one, so the next time the same object is hovered over it shows again the highlight
280 } else {
281 nc->remove_flash_counter--;
282 }
284 gint ret = FALSE;
285 switch (event->type) {
286 case GDK_BUTTON_PRESS:
287 if (event->button.button == 1 && !event_context->space_panning) {
288 // save drag origin
289 event_context->xp = (gint) event->button.x;
290 event_context->yp = (gint) event->button.y;
291 event_context->within_tolerance = true;
292 se->cancel_hit();
294 if (!(event->button.state & GDK_SHIFT_MASK)) {
295 if (!nc->drag) {
296 if (se->has_nodepath() && selection->single() /* && item_over */) {
297 sp_canvas_set_snap_delay_active(desktop->canvas, true);
298 // save drag origin
299 bool over_stroke = se->is_over_stroke(Geom::Point(event->button.x, event->button.y), true);
300 //only dragging curves
301 if (over_stroke) {
302 ret = TRUE;
303 break;
304 }
305 }
306 }
307 }
308 Geom::Point const button_w(event->button.x,
309 event->button.y);
310 Geom::Point const button_dt(desktop->w2d(button_w));
311 Inkscape::Rubberband::get(desktop)->start(desktop, button_dt);
312 nc->current_state = SP_NODE_CONTEXT_INACTIVE;
313 desktop->updateNow();
314 ret = TRUE;
315 }
316 break;
317 case GDK_MOTION_NOTIFY:
318 if (event->motion.state & GDK_BUTTON1_MASK && !event_context->space_panning) {
320 if ( event_context->within_tolerance
321 && ( abs( (gint) event->motion.x - event_context->xp ) < event_context->tolerance )
322 && ( abs( (gint) event->motion.y - event_context->yp ) < event_context->tolerance ) ) {
323 break; // do not drag if we're within tolerance from origin
324 }
326 // The path went away while dragging; throw away any further motion
327 // events until the mouse pointer is released.
329 if (se->hits_curve() && !se->has_nodepath()) {
330 break;
331 }
333 // Once the user has moved farther than tolerance from the original location
334 // (indicating they intend to move the object, not click), then always process the
335 // motion notify coordinates as given (no snapping back to origin)
336 event_context->within_tolerance = false;
338 // Once we determine what the user is doing (dragging either a node or the
339 // selection rubberband), make sure we continue to perform that operation
340 // until the mouse pointer is lifted.
341 if (nc->current_state == SP_NODE_CONTEXT_INACTIVE) {
342 if (se->hits_curve() && se->has_nodepath()) {
343 nc->current_state = SP_NODE_CONTEXT_NODE_DRAGGING;
344 } else {
345 nc->current_state = SP_NODE_CONTEXT_RUBBERBAND_DRAGGING;
346 }
347 }
349 switch (nc->current_state) {
350 case SP_NODE_CONTEXT_NODE_DRAGGING:
351 {
352 se->curve_drag (event->motion.x, event->motion.y);
354 gobble_motion_events(GDK_BUTTON1_MASK);
355 break;
356 }
357 case SP_NODE_CONTEXT_RUBBERBAND_DRAGGING:
358 if (Inkscape::Rubberband::get(desktop)->is_started()) {
359 Geom::Point const motion_w(event->motion.x,
360 event->motion.y);
361 Geom::Point const motion_dt(desktop->w2d(motion_w));
362 Inkscape::Rubberband::get(desktop)->move(motion_dt);
363 }
364 break;
365 }
367 nc->drag = TRUE;
368 ret = TRUE;
369 } else {
370 if (!se->has_nodepath() || selection->singleItem() == NULL) {
371 break;
372 }
374 bool over_stroke = false;
375 over_stroke = se->is_over_stroke(Geom::Point(event->motion.x, event->motion.y), false);
377 if (nc->cursor_drag && !over_stroke) {
378 event_context->cursor_shape = cursor_node_xpm;
379 event_context->hot_x = 1;
380 event_context->hot_y = 1;
381 sp_event_context_update_cursor(event_context);
382 nc->cursor_drag = false;
383 } else if (!nc->cursor_drag && over_stroke) {
384 event_context->cursor_shape = cursor_node_d_xpm;
385 event_context->hot_x = 1;
386 event_context->hot_y = 1;
387 sp_event_context_update_cursor(event_context);
388 nc->cursor_drag = true;
389 }
390 }
391 break;
393 case GDK_2BUTTON_PRESS:
394 case GDK_BUTTON_RELEASE:
395 if ( (event->button.button == 1) && (!nc->drag) && !event_context->space_panning) {
396 // find out clicked item, disregarding groups, honoring Alt
397 SPItem *item_clicked = sp_event_context_find_item (desktop,
398 Geom::Point(event->button.x, event->button.y),
399 (event->button.state & GDK_MOD1_MASK) && !(event->button.state & GDK_CONTROL_MASK), TRUE);
401 event_context->xp = event_context->yp = 0;
403 bool over_stroke = false;
404 if (se->has_nodepath()) {
405 over_stroke = se->is_over_stroke(Geom::Point(event->button.x, event->button.y), false);
406 }
408 if (item_clicked || over_stroke) {
409 if (over_stroke || nc->added_node) {
410 switch (event->type) {
411 case GDK_BUTTON_RELEASE:
412 if (event->button.state & GDK_CONTROL_MASK && event->button.state & GDK_MOD1_MASK) {
413 //add a node
414 se->add_node_near_point();
415 } else {
416 if (nc->added_node) { // we just received double click, ignore release
417 nc->added_node = false;
418 break;
419 }
420 //select the segment
421 if (event->button.state & GDK_SHIFT_MASK) {
422 se->select_segment_near_point(true);
423 } else {
424 se->select_segment_near_point(false);
425 }
426 desktop->updateNow();
427 }
428 sp_canvas_set_snap_delay_active(desktop->canvas, false);
429 break;
430 case GDK_2BUTTON_PRESS:
431 //add a node
432 se->add_node_near_point();
433 nc->added_node = true;
434 sp_canvas_set_snap_delay_active(desktop->canvas, false);
435 break;
436 default:
437 break;
438 }
439 } else if (event->button.state & GDK_SHIFT_MASK) {
440 selection->toggle(item_clicked);
441 desktop->updateNow();
442 } else {
443 selection->set(item_clicked);
444 desktop->updateNow();
445 }
446 Inkscape::Rubberband::get(desktop)->stop();
447 ret = TRUE;
448 break;
449 }
450 }
451 if (event->type == GDK_BUTTON_RELEASE) {
452 event_context->xp = event_context->yp = 0;
453 if (event->button.button == 1) {
454 Geom::OptRect b = Inkscape::Rubberband::get(desktop)->getRectangle();
456 if (se->hits_curve() && !event_context->within_tolerance) { //drag curve
457 se->finish_drag();
458 } else if (b && !event_context->within_tolerance) { // drag to select
459 se->select_rect(*b, event->button.state & GDK_SHIFT_MASK);
460 } else {
461 if (!(nc->rb_escaped)) { // unless something was canceled
462 if (se->has_selection())
463 se->deselect();
464 else
465 sp_desktop_selection(desktop)->clear();
466 }
467 }
468 ret = TRUE;
469 Inkscape::Rubberband::get(desktop)->stop();
470 desktop->updateNow();
471 nc->rb_escaped = false;
472 nc->drag = FALSE;
473 se->cancel_hit();
474 nc->current_state = SP_NODE_CONTEXT_INACTIVE;
475 }
476 }
477 break;
478 case GDK_KEY_PRESS:
479 switch (get_group0_keyval(&event->key)) {
480 case GDK_Insert:
481 case GDK_KP_Insert:
482 // with any modifiers
483 se->add_node();
484 ret = TRUE;
485 break;
486 case GDK_I:
487 case GDK_i:
488 // apple keyboards have no Insert
489 if (MOD__SHIFT_ONLY) {
490 se->add_node();
491 ret = TRUE;
492 }
493 break;
494 case GDK_Delete:
495 case GDK_KP_Delete:
496 case GDK_BackSpace:
497 if (MOD__CTRL_ONLY) {
498 se->delete_nodes();
499 } else {
500 se->delete_nodes_preserving_shape();
501 }
502 ret = TRUE;
503 break;
504 case GDK_C:
505 case GDK_c:
506 if (MOD__SHIFT_ONLY) {
507 se->set_node_type(Inkscape::NodePath::NODE_CUSP);
508 ret = TRUE;
509 }
510 break;
511 case GDK_S:
512 case GDK_s:
513 if (MOD__SHIFT_ONLY) {
514 se->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
515 ret = TRUE;
516 }
517 break;
518 case GDK_A:
519 case GDK_a:
520 if (MOD__SHIFT_ONLY) {
521 se->set_node_type(Inkscape::NodePath::NODE_AUTO);
522 ret = TRUE;
523 }
524 break;
525 case GDK_Y:
526 case GDK_y:
527 if (MOD__SHIFT_ONLY) {
528 se->set_node_type(Inkscape::NodePath::NODE_SYMM);
529 ret = TRUE;
530 }
531 break;
532 case GDK_B:
533 case GDK_b:
534 if (MOD__SHIFT_ONLY) {
535 se->break_at_nodes();
536 ret = TRUE;
537 }
538 break;
539 case GDK_J:
540 case GDK_j:
541 if (MOD__SHIFT_ONLY) {
542 se->join_nodes();
543 ret = TRUE;
544 }
545 break;
546 case GDK_D:
547 case GDK_d:
548 if (MOD__SHIFT_ONLY) {
549 se->duplicate_nodes();
550 ret = TRUE;
551 }
552 break;
553 case GDK_L:
554 case GDK_l:
555 if (MOD__SHIFT_ONLY) {
556 se->set_type_of_segments(NR_LINETO);
557 ret = TRUE;
558 }
559 break;
560 case GDK_U:
561 case GDK_u:
562 if (MOD__SHIFT_ONLY) {
563 se->set_type_of_segments(NR_CURVETO);
564 ret = TRUE;
565 }
566 break;
567 case GDK_R:
568 case GDK_r:
569 if (MOD__SHIFT_ONLY) {
570 // FIXME: add top panel button
571 sp_selected_path_reverse(desktop);
572 ret = TRUE;
573 }
574 break;
575 case GDK_x:
576 case GDK_X:
577 if (MOD__ALT_ONLY) {
578 desktop->setToolboxFocusTo ("altx-nodes");
579 ret = TRUE;
580 }
581 break;
582 case GDK_Left: // move selection left
583 case GDK_KP_Left:
584 case GDK_KP_4:
585 if (!MOD__CTRL) { // not ctrl
586 gint mul = 1 + gobble_key_events(
587 get_group0_keyval(&event->key), 0); // with any mask
588 if (MOD__ALT) { // alt
589 if (MOD__SHIFT) se->move_nodes_screen(desktop, mul*-10, 0); // shift
590 else se->move_nodes_screen(desktop, mul*-1, 0); // no shift
591 }
592 else { // no alt
593 if (MOD__SHIFT) se->move_nodes(mul*-10*nudge, 0); // shift
594 else se->move_nodes(mul*-nudge, 0); // no shift
595 }
596 ret = TRUE;
597 }
598 break;
599 case GDK_Up: // move selection up
600 case GDK_KP_Up:
601 case GDK_KP_8:
602 if (!MOD__CTRL) { // not ctrl
603 gint mul = 1 + gobble_key_events(
604 get_group0_keyval(&event->key), 0); // with any mask
605 if (MOD__ALT) { // alt
606 if (MOD__SHIFT) se->move_nodes_screen(desktop, 0, mul*10); // shift
607 else se->move_nodes_screen(desktop, 0, mul*1); // no shift
608 }
609 else { // no alt
610 if (MOD__SHIFT) se->move_nodes(0, mul*10*nudge); // shift
611 else se->move_nodes(0, mul*nudge); // no shift
612 }
613 ret = TRUE;
614 }
615 break;
616 case GDK_Right: // move selection right
617 case GDK_KP_Right:
618 case GDK_KP_6:
619 if (!MOD__CTRL) { // not ctrl
620 gint mul = 1 + gobble_key_events(
621 get_group0_keyval(&event->key), 0); // with any mask
622 if (MOD__ALT) { // alt
623 if (MOD__SHIFT) se->move_nodes_screen(desktop, mul*10, 0); // shift
624 else se->move_nodes_screen(desktop, mul*1, 0); // no shift
625 }
626 else { // no alt
627 if (MOD__SHIFT) se->move_nodes(mul*10*nudge, 0); // shift
628 else se->move_nodes(mul*nudge, 0); // no shift
629 }
630 ret = TRUE;
631 }
632 break;
633 case GDK_Down: // move selection down
634 case GDK_KP_Down:
635 case GDK_KP_2:
636 if (!MOD__CTRL) { // not ctrl
637 gint mul = 1 + gobble_key_events(
638 get_group0_keyval(&event->key), 0); // with any mask
639 if (MOD__ALT) { // alt
640 if (MOD__SHIFT) se->move_nodes_screen(desktop, 0, mul*-10); // shift
641 else se->move_nodes_screen(desktop, 0, mul*-1); // no shift
642 }
643 else { // no alt
644 if (MOD__SHIFT) se->move_nodes(0, mul*-10*nudge); // shift
645 else se->move_nodes(0, mul*-nudge); // no shift
646 }
647 ret = TRUE;
648 }
649 break;
650 case GDK_Escape:
651 {
652 Geom::OptRect const b = Inkscape::Rubberband::get(desktop)->getRectangle();
653 if (b) {
654 Inkscape::Rubberband::get(desktop)->stop();
655 nc->current_state = SP_NODE_CONTEXT_INACTIVE;
656 nc->rb_escaped = true;
657 } else {
658 if (se->has_selection()) {
659 se->deselect();
660 } else {
661 sp_desktop_selection(desktop)->clear();
662 }
663 }
664 ret = TRUE;
665 break;
666 }
668 case GDK_bracketleft:
669 if ( MOD__CTRL && !MOD__ALT && ( snaps != 0 ) ) {
670 if (nc->leftctrl)
671 se->rotate_nodes (M_PI/snaps, -1, false);
672 if (nc->rightctrl)
673 se->rotate_nodes (M_PI/snaps, 1, false);
674 } else if ( MOD__ALT && !MOD__CTRL ) {
675 if (nc->leftalt && nc->rightalt)
676 se->rotate_nodes (1, 0, true);
677 else {
678 if (nc->leftalt)
679 se->rotate_nodes (1, -1, true);
680 if (nc->rightalt)
681 se->rotate_nodes (1, 1, true);
682 }
683 } else if ( snaps != 0 ) {
684 se->rotate_nodes (M_PI/snaps, 0, false);
685 }
686 ret = TRUE;
687 break;
688 case GDK_bracketright:
689 if ( MOD__CTRL && !MOD__ALT && ( snaps != 0 ) ) {
690 if (nc->leftctrl)
691 se->rotate_nodes (-M_PI/snaps, -1, false);
692 if (nc->rightctrl)
693 se->rotate_nodes (-M_PI/snaps, 1, false);
694 } else if ( MOD__ALT && !MOD__CTRL ) {
695 if (nc->leftalt && nc->rightalt)
696 se->rotate_nodes (-1, 0, true);
697 else {
698 if (nc->leftalt)
699 se->rotate_nodes (-1, -1, true);
700 if (nc->rightalt)
701 se->rotate_nodes (-1, 1, true);
702 }
703 } else if ( snaps != 0 ) {
704 se->rotate_nodes (-M_PI/snaps, 0, false);
705 }
706 ret = TRUE;
707 break;
708 case GDK_less:
709 case GDK_comma:
710 if (MOD__CTRL) {
711 if (nc->leftctrl)
712 se->scale_nodes(-offset, -1);
713 if (nc->rightctrl)
714 se->scale_nodes(-offset, 1);
715 } else if (MOD__ALT) {
716 if (nc->leftalt && nc->rightalt)
717 se->scale_nodes_screen (-1, 0);
718 else {
719 if (nc->leftalt)
720 se->scale_nodes_screen (-1, -1);
721 if (nc->rightalt)
722 se->scale_nodes_screen (-1, 1);
723 }
724 } else {
725 se->scale_nodes (-offset, 0);
726 }
727 ret = TRUE;
728 break;
729 case GDK_greater:
730 case GDK_period:
731 if (MOD__CTRL) {
732 if (nc->leftctrl)
733 se->scale_nodes (offset, -1);
734 if (nc->rightctrl)
735 se->scale_nodes (offset, 1);
736 } else if (MOD__ALT) {
737 if (nc->leftalt && nc->rightalt)
738 se->scale_nodes_screen (1, 0);
739 else {
740 if (nc->leftalt)
741 se->scale_nodes_screen (1, -1);
742 if (nc->rightalt)
743 se->scale_nodes_screen (1, 1);
744 }
745 } else {
746 se->scale_nodes (offset, 0);
747 }
748 ret = TRUE;
749 break;
751 case GDK_Alt_L:
752 nc->leftalt = TRUE;
753 sp_node_context_show_modifier_tip(event_context, event);
754 break;
755 case GDK_Alt_R:
756 nc->rightalt = TRUE;
757 sp_node_context_show_modifier_tip(event_context, event);
758 break;
759 case GDK_Control_L:
760 nc->leftctrl = TRUE;
761 sp_node_context_show_modifier_tip(event_context, event);
762 break;
763 case GDK_Control_R:
764 nc->rightctrl = TRUE;
765 sp_node_context_show_modifier_tip(event_context, event);
766 break;
767 case GDK_Shift_L:
768 case GDK_Shift_R:
769 case GDK_Meta_L:
770 case GDK_Meta_R:
771 sp_node_context_show_modifier_tip(event_context, event);
772 break;
773 default:
774 ret = node_key(event);
775 break;
776 }
777 break;
778 case GDK_KEY_RELEASE:
779 switch (get_group0_keyval(&event->key)) {
780 case GDK_Alt_L:
781 nc->leftalt = FALSE;
782 event_context->defaultMessageContext()->clear();
783 break;
784 case GDK_Alt_R:
785 nc->rightalt = FALSE;
786 event_context->defaultMessageContext()->clear();
787 break;
788 case GDK_Control_L:
789 nc->leftctrl = FALSE;
790 event_context->defaultMessageContext()->clear();
791 break;
792 case GDK_Control_R:
793 nc->rightctrl = FALSE;
794 event_context->defaultMessageContext()->clear();
795 break;
796 case GDK_Shift_L:
797 case GDK_Shift_R:
798 case GDK_Meta_L:
799 case GDK_Meta_R:
800 event_context->defaultMessageContext()->clear();
801 break;
802 }
803 break;
804 default:
805 break;
806 }
808 if (!ret) {
809 if (((SPEventContextClass *) parent_class)->root_handler)
810 ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event);
811 }
813 return ret;
814 }
817 /*
818 Local Variables:
819 mode:c++
820 c-file-style:"stroustrup"
821 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
822 indent-tabs-mode:nil
823 fill-column:99
824 End:
825 */
826 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :