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 if (nc->grabbed) {
122 sp_canvas_item_ungrab(nc->grabbed, GDK_CURRENT_TIME);
123 nc->grabbed = NULL;
124 }
126 nc->sel_changed_connection.disconnect();
127 nc->sel_changed_connection.~connection();
129 delete ec->shape_editor;
131 if (nc->_node_message_context) {
132 delete nc->_node_message_context;
133 }
135 G_OBJECT_CLASS(parent_class)->dispose(object);
136 }
138 static void
139 sp_node_context_setup(SPEventContext *ec)
140 {
141 SPNodeContext *nc = SP_NODE_CONTEXT(ec);
143 if (((SPEventContextClass *) parent_class)->setup)
144 ((SPEventContextClass *) parent_class)->setup(ec);
146 Inkscape::Selection *selection = sp_desktop_selection (ec->desktop);
147 nc->sel_changed_connection.disconnect();
148 nc->sel_changed_connection =
149 selection->connectChanged(sigc::bind(sigc::ptr_fun(&sp_node_context_selection_changed), (gpointer)nc));
151 SPItem *item = selection->singleItem();
153 ec->shape_editor = new ShapeEditor(ec->desktop);
155 nc->rb_escaped = false;
157 nc->cursor_drag = false;
159 nc->added_node = false;
161 nc->current_state = SP_NODE_CONTEXT_INACTIVE;
163 if (item) {
164 ec->shape_editor->set_item(item, SH_NODEPATH);
165 ec->shape_editor->set_item(item, SH_KNOTHOLDER);
166 }
168 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
169 if (prefs->getBool("/tools/nodes/selcue")) {
170 ec->enableSelectionCue();
171 }
172 if (prefs->getBool("/tools/nodes/gradientdrag")) {
173 ec->enableGrDrag();
174 }
176 ec->desktop->emitToolSubselectionChanged(NULL); // sets the coord entry fields to inactive
178 nc->_node_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack());
180 ec->shape_editor->update_statusbar();
181 }
183 static void
184 sp_node_context_flash_path(SPEventContext *event_context, SPItem *item, guint timeout) {
185 SPNodeContext *nc = SP_NODE_CONTEXT(event_context);
187 nc->remove_flash_counter = 3; // for some reason root_handler is called twice after each item_handler...
188 if (nc->flashed_item != item) {
189 // we entered a new item
190 nc->flashed_item = item;
191 SPDesktop *desktop = event_context->desktop;
192 if (nc->flash_tempitem) {
193 desktop->remove_temporary_canvasitem(nc->flash_tempitem);
194 nc->flash_tempitem = NULL;
195 }
197 SPCanvasItem *canvasitem = sp_nodepath_generate_helperpath(desktop, item);
199 if (canvasitem) {
200 nc->flash_tempitem = desktop->add_temporary_canvasitem (canvasitem, timeout);
201 }
202 }
203 }
205 /**
206 \brief Callback that processes the "changed" signal on the selection;
207 destroys old and creates new nodepath and reassigns listeners to the new selected item's repr
208 */
209 void
210 sp_node_context_selection_changed(Inkscape::Selection *selection, gpointer data)
211 {
212 SPEventContext *ec = SP_EVENT_CONTEXT(data);
214 // TODO: update ShapeEditorsCollective instead
215 ec->shape_editor->unset_item(SH_NODEPATH);
216 ec->shape_editor->unset_item(SH_KNOTHOLDER);
217 SPItem *item = selection->singleItem();
218 ec->shape_editor->set_item(item, SH_NODEPATH);
219 ec->shape_editor->set_item(item, SH_KNOTHOLDER);
220 ec->shape_editor->update_statusbar();
221 }
223 void
224 sp_node_context_show_modifier_tip(SPEventContext *event_context, GdkEvent *event)
225 {
226 sp_event_show_modifier_tip
227 (event_context->defaultMessageContext(), event,
228 _("<b>Ctrl</b>: toggle node type, snap handle angle, move hor/vert; <b>Ctrl+Alt</b>: move along handles"),
229 _("<b>Shift</b>: toggle node selection, disable snapping, rotate both handles"),
230 _("<b>Alt</b>: lock handle length; <b>Ctrl+Alt</b>: move along handles"));
231 }
233 static gint
234 sp_node_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event)
235 {
236 gint ret = FALSE;
237 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
238 SPDesktop *desktop = event_context->desktop;
240 switch (event->type) {
241 case GDK_MOTION_NOTIFY:
242 {
243 // find out actual item we're over, disregarding groups
244 SPItem *actual_item = sp_event_context_find_item (desktop,
245 Geom::Point(event->button.x, event->button.y), FALSE, TRUE);
246 if (!actual_item)
247 break;
250 if (prefs->getBool("/tools/nodes/pathflash_enabled")) {
251 if (prefs->getBool("/tools/nodes/pathflash_unselected")) {
252 // do not flash if we have some path selected and a single item in selection (i.e. it
253 // is the same path that we're editing)
254 SPDesktop *desktop = event_context->desktop;
255 ShapeEditor* se = event_context->shape_editor;
256 Inkscape::Selection *selection = sp_desktop_selection (desktop);
257 if (se->has_nodepath() && selection->singleItem()) {
258 break;
259 }
260 }
261 if (SP_IS_LPE_ITEM(actual_item)) {
262 Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(actual_item));
263 if (lpe && (lpe->providesOwnFlashPaths() ||
264 lpe->pathFlashType() == Inkscape::LivePathEffect::SUPPRESS_FLASH)) {
265 // path should be suppressed or permanent; this is handled in
266 // sp_node_context_selection_changed()
267 break;
268 }
269 }
270 guint timeout = prefs->getInt("/tools/nodes/pathflash_timeout", 500);
271 sp_node_context_flash_path(event_context, actual_item, timeout);
272 }
273 }
274 break;
276 default:
277 break;
278 }
280 if (((SPEventContextClass *) parent_class)->item_handler)
281 ret = ((SPEventContextClass *) parent_class)->item_handler(event_context, item, event);
283 return ret;
284 }
286 static gint
287 sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
288 {
289 SPDesktop *desktop = event_context->desktop;
290 ShapeEditor* se = event_context->shape_editor;
291 Inkscape::Selection *selection = sp_desktop_selection (desktop);
292 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
294 SPNodeContext *nc = SP_NODE_CONTEXT(event_context);
295 double const nudge = prefs->getDoubleLimited("/options/nudgedistance/value", 2, 0, 1000); // in px
296 event_context->tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); // read every time, to make prefs changes really live
297 int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
298 double const offset = prefs->getDoubleLimited("/options/defaultscale/value", 2, 0, 1000);
300 if ( (nc->flash_tempitem) && (nc->remove_flash_counter <= 0) ) {
301 desktop->remove_temporary_canvasitem(nc->flash_tempitem);
302 nc->flash_tempitem = NULL;
303 nc->flashed_item = NULL; // also reset this one, so the next time the same object is hovered over it shows again the highlight
304 } else {
305 nc->remove_flash_counter--;
306 }
308 gint ret = FALSE;
309 switch (event->type) {
310 case GDK_BUTTON_PRESS:
311 if (event->button.button == 1 && !event_context->space_panning) {
312 // save drag origin
313 event_context->xp = (gint) event->button.x;
314 event_context->yp = (gint) event->button.y;
315 event_context->within_tolerance = true;
316 se->cancel_hit();
318 if (!(event->button.state & GDK_SHIFT_MASK)) {
319 if (!nc->drag) {
320 if (se->has_nodepath() && selection->single() /* && item_over */) {
321 // save drag origin
322 bool over_stroke = se->is_over_stroke(Geom::Point(event->button.x, event->button.y), true);
323 //only dragging curves
324 if (over_stroke) {
325 ret = TRUE;
326 break;
327 }
328 }
329 }
330 }
331 Geom::Point const button_w(event->button.x,
332 event->button.y);
333 Geom::Point const button_dt(desktop->w2d(button_w));
334 Inkscape::Rubberband::get(desktop)->start(desktop, button_dt);
336 if (nc->grabbed) {
337 sp_canvas_item_ungrab(nc->grabbed, event->button.time);
338 nc->grabbed = NULL;
339 }
341 sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate),
342 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK,
343 NULL, event->button.time);
344 nc->grabbed = SP_CANVAS_ITEM(desktop->acetate);
346 nc->current_state = SP_NODE_CONTEXT_INACTIVE;
347 desktop->updateNow();
348 ret = TRUE;
349 }
350 break;
351 case GDK_MOTION_NOTIFY:
352 if (event->motion.state & GDK_BUTTON1_MASK && !event_context->space_panning) {
354 if ( event_context->within_tolerance
355 && ( abs( (gint) event->motion.x - event_context->xp ) < event_context->tolerance )
356 && ( abs( (gint) event->motion.y - event_context->yp ) < event_context->tolerance ) ) {
357 break; // do not drag if we're within tolerance from origin
358 }
360 // The path went away while dragging; throw away any further motion
361 // events until the mouse pointer is released.
363 if (se->hits_curve() && !se->has_nodepath()) {
364 break;
365 }
367 // Once the user has moved farther than tolerance from the original location
368 // (indicating they intend to move the object, not click), then always process the
369 // motion notify coordinates as given (no snapping back to origin)
370 event_context->within_tolerance = false;
372 // Once we determine what the user is doing (dragging either a node or the
373 // selection rubberband), make sure we continue to perform that operation
374 // until the mouse pointer is lifted.
375 if (nc->current_state == SP_NODE_CONTEXT_INACTIVE) {
376 if (se->hits_curve() && se->has_nodepath()) {
377 nc->current_state = SP_NODE_CONTEXT_NODE_DRAGGING;
378 } else {
379 nc->current_state = SP_NODE_CONTEXT_RUBBERBAND_DRAGGING;
380 }
381 }
383 switch (nc->current_state) {
384 case SP_NODE_CONTEXT_NODE_DRAGGING:
385 {
386 se->curve_drag (event->motion.x, event->motion.y);
388 gobble_motion_events(GDK_BUTTON1_MASK);
389 break;
390 }
391 case SP_NODE_CONTEXT_RUBBERBAND_DRAGGING:
392 if (Inkscape::Rubberband::get(desktop)->is_started()) {
393 Geom::Point const motion_w(event->motion.x,
394 event->motion.y);
395 Geom::Point const motion_dt(desktop->w2d(motion_w));
396 Inkscape::Rubberband::get(desktop)->move(motion_dt);
397 }
398 break;
399 }
401 nc->drag = TRUE;
402 ret = TRUE;
403 } else {
404 if (!se->has_nodepath() || selection->singleItem() == NULL) {
405 break;
406 }
408 bool over_stroke = false;
409 over_stroke = se->is_over_stroke(Geom::Point(event->motion.x, event->motion.y), false);
411 if (nc->cursor_drag && !over_stroke) {
412 event_context->cursor_shape = cursor_node_xpm;
413 event_context->hot_x = 1;
414 event_context->hot_y = 1;
415 sp_event_context_update_cursor(event_context);
416 nc->cursor_drag = false;
417 } else if (!nc->cursor_drag && over_stroke) {
418 event_context->cursor_shape = cursor_node_d_xpm;
419 event_context->hot_x = 1;
420 event_context->hot_y = 1;
421 sp_event_context_update_cursor(event_context);
422 nc->cursor_drag = true;
423 }
424 }
425 break;
427 case GDK_2BUTTON_PRESS:
428 case GDK_BUTTON_RELEASE:
429 if ( (event->button.button == 1) && (!nc->drag) && !event_context->space_panning) {
430 // find out clicked item, disregarding groups, honoring Alt
431 SPItem *item_clicked = sp_event_context_find_item (desktop,
432 Geom::Point(event->button.x, event->button.y),
433 (event->button.state & GDK_MOD1_MASK) && !(event->button.state & GDK_CONTROL_MASK), TRUE);
435 event_context->xp = event_context->yp = 0;
437 bool over_stroke = false;
438 if (se->has_nodepath()) {
439 over_stroke = se->is_over_stroke(Geom::Point(event->button.x, event->button.y), false);
440 }
442 if (item_clicked || over_stroke) {
443 if (over_stroke || nc->added_node) {
444 switch (event->type) {
445 case GDK_BUTTON_RELEASE:
446 if (event->button.state & GDK_CONTROL_MASK && event->button.state & GDK_MOD1_MASK) {
447 //add a node
448 se->add_node_near_point();
449 } else {
450 if (nc->added_node) { // we just received double click, ignore release
451 nc->added_node = false;
452 break;
453 }
454 //select the segment
455 if (event->button.state & GDK_SHIFT_MASK) {
456 se->select_segment_near_point(true);
457 } else {
458 se->select_segment_near_point(false);
459 }
460 desktop->updateNow();
461 }
462 break;
463 case GDK_2BUTTON_PRESS:
464 //add a node
465 se->add_node_near_point();
466 nc->added_node = true;
467 break;
468 default:
469 break;
470 }
471 } else if (event->button.state & GDK_SHIFT_MASK) {
472 selection->toggle(item_clicked);
473 desktop->updateNow();
474 } else {
475 selection->set(item_clicked);
476 desktop->updateNow();
477 }
478 Inkscape::Rubberband::get(desktop)->stop();
479 if (nc->grabbed) {
480 sp_canvas_item_ungrab(nc->grabbed, event->button.time);
481 nc->grabbed = NULL;
482 }
483 ret = TRUE;
484 break;
485 }
486 }
487 if (event->type == GDK_BUTTON_RELEASE) {
488 event_context->xp = event_context->yp = 0;
489 if (event->button.button == 1) {
490 Geom::OptRect b = Inkscape::Rubberband::get(desktop)->getRectangle();
492 if (se->hits_curve() && !event_context->within_tolerance) { //drag curve
493 se->finish_drag();
494 } else if (b && !event_context->within_tolerance) { // drag to select
495 se->select_rect(*b, event->button.state & GDK_SHIFT_MASK);
496 } else {
497 if (!(nc->rb_escaped)) { // unless something was canceled
498 if (se->has_selection())
499 se->deselect();
500 else
501 sp_desktop_selection(desktop)->clear();
502 }
503 }
504 ret = TRUE;
505 Inkscape::Rubberband::get(desktop)->stop();
507 if (nc->grabbed) {
508 sp_canvas_item_ungrab(nc->grabbed, event->button.time);
509 nc->grabbed = NULL;
510 }
512 desktop->updateNow();
513 nc->rb_escaped = false;
514 nc->drag = FALSE;
515 se->cancel_hit();
516 nc->current_state = SP_NODE_CONTEXT_INACTIVE;
517 }
518 }
519 break;
520 case GDK_KEY_PRESS:
521 switch (get_group0_keyval(&event->key)) {
522 case GDK_Insert:
523 case GDK_KP_Insert:
524 // with any modifiers
525 se->add_node();
526 ret = TRUE;
527 break;
528 case GDK_I:
529 case GDK_i:
530 // apple keyboards have no Insert
531 if (MOD__SHIFT_ONLY) {
532 se->add_node();
533 ret = TRUE;
534 }
535 break;
536 case GDK_Delete:
537 case GDK_KP_Delete:
538 case GDK_BackSpace:
539 if (MOD__CTRL_ONLY) {
540 se->delete_nodes();
541 } else {
542 se->delete_nodes_preserving_shape();
543 }
544 ret = TRUE;
545 break;
546 case GDK_C:
547 case GDK_c:
548 if (MOD__SHIFT_ONLY) {
549 se->set_node_type(Inkscape::NodePath::NODE_CUSP);
550 ret = TRUE;
551 }
552 break;
553 case GDK_S:
554 case GDK_s:
555 if (MOD__SHIFT_ONLY) {
556 se->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
557 ret = TRUE;
558 }
559 break;
560 case GDK_A:
561 case GDK_a:
562 if (MOD__SHIFT_ONLY) {
563 se->set_node_type(Inkscape::NodePath::NODE_AUTO);
564 ret = TRUE;
565 }
566 break;
567 case GDK_Y:
568 case GDK_y:
569 if (MOD__SHIFT_ONLY) {
570 se->set_node_type(Inkscape::NodePath::NODE_SYMM);
571 ret = TRUE;
572 }
573 break;
574 case GDK_B:
575 case GDK_b:
576 if (MOD__SHIFT_ONLY) {
577 se->break_at_nodes();
578 ret = TRUE;
579 }
580 break;
581 case GDK_J:
582 case GDK_j:
583 if (MOD__SHIFT_ONLY) {
584 se->join_nodes();
585 ret = TRUE;
586 }
587 break;
588 case GDK_D:
589 case GDK_d:
590 if (MOD__SHIFT_ONLY) {
591 se->duplicate_nodes();
592 ret = TRUE;
593 }
594 break;
595 case GDK_L:
596 case GDK_l:
597 if (MOD__SHIFT_ONLY) {
598 se->set_type_of_segments(NR_LINETO);
599 ret = TRUE;
600 }
601 break;
602 case GDK_U:
603 case GDK_u:
604 if (MOD__SHIFT_ONLY) {
605 se->set_type_of_segments(NR_CURVETO);
606 ret = TRUE;
607 }
608 break;
609 case GDK_R:
610 case GDK_r:
611 if (MOD__SHIFT_ONLY) {
612 // FIXME: add top panel button
613 sp_selected_path_reverse(desktop);
614 ret = TRUE;
615 }
616 break;
617 case GDK_x:
618 case GDK_X:
619 if (MOD__ALT_ONLY) {
620 desktop->setToolboxFocusTo ("altx-nodes");
621 ret = TRUE;
622 }
623 break;
624 case GDK_Left: // move selection left
625 case GDK_KP_Left:
626 case GDK_KP_4:
627 if (!MOD__CTRL) { // not ctrl
628 gint mul = 1 + gobble_key_events(
629 get_group0_keyval(&event->key), 0); // with any mask
630 if (MOD__ALT) { // alt
631 if (MOD__SHIFT) se->move_nodes_screen(desktop, mul*-10, 0); // shift
632 else se->move_nodes_screen(desktop, mul*-1, 0); // no shift
633 }
634 else { // no alt
635 if (MOD__SHIFT) se->move_nodes(mul*-10*nudge, 0); // shift
636 else se->move_nodes(mul*-nudge, 0); // no shift
637 }
638 ret = TRUE;
639 }
640 break;
641 case GDK_Up: // move selection up
642 case GDK_KP_Up:
643 case GDK_KP_8:
644 if (!MOD__CTRL) { // not ctrl
645 gint mul = 1 + gobble_key_events(
646 get_group0_keyval(&event->key), 0); // with any mask
647 if (MOD__ALT) { // alt
648 if (MOD__SHIFT) se->move_nodes_screen(desktop, 0, mul*10); // shift
649 else se->move_nodes_screen(desktop, 0, mul*1); // no shift
650 }
651 else { // no alt
652 if (MOD__SHIFT) se->move_nodes(0, mul*10*nudge); // shift
653 else se->move_nodes(0, mul*nudge); // no shift
654 }
655 ret = TRUE;
656 }
657 break;
658 case GDK_Right: // move selection right
659 case GDK_KP_Right:
660 case GDK_KP_6:
661 if (!MOD__CTRL) { // not ctrl
662 gint mul = 1 + gobble_key_events(
663 get_group0_keyval(&event->key), 0); // with any mask
664 if (MOD__ALT) { // alt
665 if (MOD__SHIFT) se->move_nodes_screen(desktop, mul*10, 0); // shift
666 else se->move_nodes_screen(desktop, mul*1, 0); // no shift
667 }
668 else { // no alt
669 if (MOD__SHIFT) se->move_nodes(mul*10*nudge, 0); // shift
670 else se->move_nodes(mul*nudge, 0); // no shift
671 }
672 ret = TRUE;
673 }
674 break;
675 case GDK_Down: // move selection down
676 case GDK_KP_Down:
677 case GDK_KP_2:
678 if (!MOD__CTRL) { // not ctrl
679 gint mul = 1 + gobble_key_events(
680 get_group0_keyval(&event->key), 0); // with any mask
681 if (MOD__ALT) { // alt
682 if (MOD__SHIFT) se->move_nodes_screen(desktop, 0, mul*-10); // shift
683 else se->move_nodes_screen(desktop, 0, mul*-1); // no shift
684 }
685 else { // no alt
686 if (MOD__SHIFT) se->move_nodes(0, mul*-10*nudge); // shift
687 else se->move_nodes(0, mul*-nudge); // no shift
688 }
689 ret = TRUE;
690 }
691 break;
692 case GDK_Escape:
693 {
694 Geom::OptRect const b = Inkscape::Rubberband::get(desktop)->getRectangle();
695 if (b) {
696 Inkscape::Rubberband::get(desktop)->stop();
697 nc->current_state = SP_NODE_CONTEXT_INACTIVE;
698 nc->rb_escaped = true;
699 } else {
700 if (se->has_selection()) {
701 se->deselect();
702 } else {
703 sp_desktop_selection(desktop)->clear();
704 }
705 }
706 ret = TRUE;
707 break;
708 }
710 case GDK_bracketleft:
711 if ( MOD__CTRL && !MOD__ALT && ( snaps != 0 ) ) {
712 if (nc->leftctrl)
713 se->rotate_nodes (M_PI/snaps, -1, false);
714 if (nc->rightctrl)
715 se->rotate_nodes (M_PI/snaps, 1, false);
716 } else if ( MOD__ALT && !MOD__CTRL ) {
717 if (nc->leftalt && nc->rightalt)
718 se->rotate_nodes (1, 0, true);
719 else {
720 if (nc->leftalt)
721 se->rotate_nodes (1, -1, true);
722 if (nc->rightalt)
723 se->rotate_nodes (1, 1, true);
724 }
725 } else if ( snaps != 0 ) {
726 se->rotate_nodes (M_PI/snaps, 0, false);
727 }
728 ret = TRUE;
729 break;
730 case GDK_bracketright:
731 if ( MOD__CTRL && !MOD__ALT && ( snaps != 0 ) ) {
732 if (nc->leftctrl)
733 se->rotate_nodes (-M_PI/snaps, -1, false);
734 if (nc->rightctrl)
735 se->rotate_nodes (-M_PI/snaps, 1, false);
736 } else if ( MOD__ALT && !MOD__CTRL ) {
737 if (nc->leftalt && nc->rightalt)
738 se->rotate_nodes (-1, 0, true);
739 else {
740 if (nc->leftalt)
741 se->rotate_nodes (-1, -1, true);
742 if (nc->rightalt)
743 se->rotate_nodes (-1, 1, true);
744 }
745 } else if ( snaps != 0 ) {
746 se->rotate_nodes (-M_PI/snaps, 0, false);
747 }
748 ret = TRUE;
749 break;
750 case GDK_less:
751 case GDK_comma:
752 if (MOD__CTRL) {
753 if (nc->leftctrl)
754 se->scale_nodes(-offset, -1);
755 if (nc->rightctrl)
756 se->scale_nodes(-offset, 1);
757 } else if (MOD__ALT) {
758 if (nc->leftalt && nc->rightalt)
759 se->scale_nodes_screen (-1, 0);
760 else {
761 if (nc->leftalt)
762 se->scale_nodes_screen (-1, -1);
763 if (nc->rightalt)
764 se->scale_nodes_screen (-1, 1);
765 }
766 } else {
767 se->scale_nodes (-offset, 0);
768 }
769 ret = TRUE;
770 break;
771 case GDK_greater:
772 case GDK_period:
773 if (MOD__CTRL) {
774 if (nc->leftctrl)
775 se->scale_nodes (offset, -1);
776 if (nc->rightctrl)
777 se->scale_nodes (offset, 1);
778 } else if (MOD__ALT) {
779 if (nc->leftalt && nc->rightalt)
780 se->scale_nodes_screen (1, 0);
781 else {
782 if (nc->leftalt)
783 se->scale_nodes_screen (1, -1);
784 if (nc->rightalt)
785 se->scale_nodes_screen (1, 1);
786 }
787 } else {
788 se->scale_nodes (offset, 0);
789 }
790 ret = TRUE;
791 break;
793 case GDK_Alt_L:
794 nc->leftalt = TRUE;
795 sp_node_context_show_modifier_tip(event_context, event);
796 break;
797 case GDK_Alt_R:
798 nc->rightalt = TRUE;
799 sp_node_context_show_modifier_tip(event_context, event);
800 break;
801 case GDK_Control_L:
802 nc->leftctrl = TRUE;
803 sp_node_context_show_modifier_tip(event_context, event);
804 break;
805 case GDK_Control_R:
806 nc->rightctrl = TRUE;
807 sp_node_context_show_modifier_tip(event_context, event);
808 break;
809 case GDK_Shift_L:
810 case GDK_Shift_R:
811 case GDK_Meta_L:
812 case GDK_Meta_R:
813 sp_node_context_show_modifier_tip(event_context, event);
814 break;
815 default:
816 ret = node_key(event);
817 break;
818 }
819 break;
820 case GDK_KEY_RELEASE:
821 switch (get_group0_keyval(&event->key)) {
822 case GDK_Alt_L:
823 nc->leftalt = FALSE;
824 event_context->defaultMessageContext()->clear();
825 break;
826 case GDK_Alt_R:
827 nc->rightalt = FALSE;
828 event_context->defaultMessageContext()->clear();
829 break;
830 case GDK_Control_L:
831 nc->leftctrl = FALSE;
832 event_context->defaultMessageContext()->clear();
833 break;
834 case GDK_Control_R:
835 nc->rightctrl = FALSE;
836 event_context->defaultMessageContext()->clear();
837 break;
838 case GDK_Shift_L:
839 case GDK_Shift_R:
840 case GDK_Meta_L:
841 case GDK_Meta_R:
842 event_context->defaultMessageContext()->clear();
843 break;
844 }
845 break;
846 default:
847 break;
848 }
850 if (!ret) {
851 if (((SPEventContextClass *) parent_class)->root_handler)
852 ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event);
853 }
855 return ret;
856 }
859 /*
860 Local Variables:
861 mode:c++
862 c-file-style:"stroustrup"
863 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
864 indent-tabs-mode:nil
865 fill-column:99
866 End:
867 */
868 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :