a72501af71e92115592fc5cee1674f985a6530a0
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 "prefs-utils.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 // TODO: should this be here?
125 SPItem *item = sp_desktop_selection(ec->desktop)->singleItem();
126 if (item && SP_IS_LPE_ITEM(item)) {
127 sp_lpe_item_remove_temporary_canvasitems(SP_LPE_ITEM(item), ec->desktop);
128 }
130 delete nc->shape_editor;
132 if (nc->_node_message_context) {
133 delete nc->_node_message_context;
134 }
136 G_OBJECT_CLASS(parent_class)->dispose(object);
137 }
139 static void
140 sp_node_context_setup(SPEventContext *ec)
141 {
142 SPNodeContext *nc = SP_NODE_CONTEXT(ec);
144 if (((SPEventContextClass *) parent_class)->setup)
145 ((SPEventContextClass *) parent_class)->setup(ec);
147 Inkscape::Selection *selection = sp_desktop_selection (ec->desktop);
148 nc->sel_changed_connection.disconnect();
149 nc->sel_changed_connection =
150 selection->connectChanged(sigc::bind(sigc::ptr_fun(&sp_node_context_selection_changed), (gpointer)nc));
152 SPItem *item = selection->singleItem();
154 nc->shape_editor = new ShapeEditor(ec->desktop);
156 nc->rb_escaped = false;
158 nc->cursor_drag = false;
160 nc->added_node = false;
162 nc->current_state = SP_NODE_CONTEXT_INACTIVE;
164 if (item) {
165 nc->shape_editor->set_item(item, SH_NODEPATH);
166 nc->shape_editor->set_item(item, SH_KNOTHOLDER);
167 }
169 if (prefs_get_int_attribute("tools.nodes", "selcue", 0) != 0) {
170 ec->enableSelectionCue();
171 }
173 if (prefs_get_int_attribute("tools.nodes", "gradientdrag", 0) != 0) {
174 ec->enableGrDrag();
175 }
177 ec->desktop->emitToolSubselectionChanged(NULL); // sets the coord entry fields to inactive
179 nc->_node_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack());
181 nc->shape_editor->update_statusbar();
182 }
184 static void
185 sp_node_context_flash_path(SPEventContext *event_context, SPItem *item, guint timeout) {
186 SPNodeContext *nc = SP_NODE_CONTEXT(event_context);
188 nc->remove_flash_counter = 3; // for some reason root_handler is called twice after each item_handler...
189 if (nc->flashed_item != item) {
190 // we entered a new item
191 nc->flashed_item = item;
192 SPDesktop *desktop = event_context->desktop;
193 if (nc->flash_tempitem) {
194 desktop->remove_temporary_canvasitem(nc->flash_tempitem);
195 nc->flash_tempitem = NULL;
196 }
198 if (SP_IS_PATH(item)) {
199 SPCanvasItem *canvasitem = sp_nodepath_generate_helperpath(desktop, SP_PATH(item));
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 SPNodeContext *nc = SP_NODE_CONTEXT(data);
214 // TODO: update ShapeEditorsCollective instead
215 nc->shape_editor->unset_item(SH_NODEPATH);
216 nc->shape_editor->unset_item(SH_KNOTHOLDER);
217 SPItem *item = selection->singleItem();
218 nc->shape_editor->set_item(item, SH_NODEPATH);
219 nc->shape_editor->set_item(item, SH_KNOTHOLDER);
220 nc->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;
238 if (prefs_get_int_attribute ("tools.nodes", "pathflash_enabled", 0) == 1) {
239 guint timeout = prefs_get_int_attribute("tools.nodes", "pathflash_timeout", 500);
240 if (SP_IS_LPE_ITEM(item)) {
241 Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item));
242 if (lpe && (lpe->providesOwnFlashPaths() ||
243 lpe->pathFlashType() == Inkscape::LivePathEffect::SUPPRESS_FLASH)) {
244 // path should be suppressed or permanent; this is handled in
245 // sp_node_context_selection_changed()
246 return ret;
247 }
248 }
249 sp_node_context_flash_path(event_context, item, timeout);
250 }
252 if (((SPEventContextClass *) parent_class)->item_handler)
253 ret = ((SPEventContextClass *) parent_class)->item_handler(event_context, item, event);
255 return ret;
256 }
258 static gint
259 sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
260 {
261 SPDesktop *desktop = event_context->desktop;
262 Inkscape::Selection *selection = sp_desktop_selection (desktop);
264 SPNodeContext *nc = SP_NODE_CONTEXT(event_context);
265 double const nudge = prefs_get_double_attribute_limited("options.nudgedistance", "value", 2, 0, 1000); // in px
266 event_context->tolerance = prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100); // read every time, to make prefs changes really live
267 int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
268 double const offset = prefs_get_double_attribute_limited("options.defaultscale", "value", 2, 0, 1000);
270 if ( (nc->flash_tempitem) && (nc->remove_flash_counter <= 0) ) {
271 desktop->remove_temporary_canvasitem(nc->flash_tempitem);
272 nc->flash_tempitem = NULL;
273 nc->flashed_item = NULL; // also reset this one, so the next time the same object is hovered over it shows again the highlight
274 } else {
275 nc->remove_flash_counter--;
276 }
278 gint ret = FALSE;
279 switch (event->type) {
280 case GDK_BUTTON_PRESS:
281 if (event->button.button == 1 && !event_context->space_panning) {
282 // save drag origin
283 event_context->xp = (gint) event->button.x;
284 event_context->yp = (gint) event->button.y;
285 event_context->within_tolerance = true;
286 nc->shape_editor->cancel_hit();
288 if (!(event->button.state & GDK_SHIFT_MASK)) {
289 if (!nc->drag) {
290 if (nc->shape_editor->has_nodepath() && selection->single() /* && item_over */) {
291 // save drag origin
292 bool over_stroke = nc->shape_editor->is_over_stroke(NR::Point(event->button.x, event->button.y), true);
293 //only dragging curves
294 if (over_stroke) {
295 ret = TRUE;
296 break;
297 }
298 }
299 }
300 }
301 NR::Point const button_w(event->button.x,
302 event->button.y);
303 NR::Point const button_dt(desktop->w2d(button_w));
304 Inkscape::Rubberband::get()->start(desktop, button_dt);
305 nc->current_state = SP_NODE_CONTEXT_INACTIVE;
306 desktop->updateNow();
307 ret = TRUE;
308 }
309 break;
310 case GDK_MOTION_NOTIFY:
311 if (event->motion.state & GDK_BUTTON1_MASK && !event_context->space_panning) {
313 if ( event_context->within_tolerance
314 && ( abs( (gint) event->motion.x - event_context->xp ) < event_context->tolerance )
315 && ( abs( (gint) event->motion.y - event_context->yp ) < event_context->tolerance ) ) {
316 break; // do not drag if we're within tolerance from origin
317 }
319 // The path went away while dragging; throw away any further motion
320 // events until the mouse pointer is released.
322 if (nc->shape_editor->hits_curve() && !nc->shape_editor->has_nodepath()) {
323 break;
324 }
326 // Once the user has moved farther than tolerance from the original location
327 // (indicating they intend to move the object, not click), then always process the
328 // motion notify coordinates as given (no snapping back to origin)
329 event_context->within_tolerance = false;
331 // Once we determine what the user is doing (dragging either a node or the
332 // selection rubberband), make sure we continue to perform that operation
333 // until the mouse pointer is lifted.
334 if (nc->current_state == SP_NODE_CONTEXT_INACTIVE) {
335 if (nc->shape_editor->hits_curve() && nc->shape_editor->has_nodepath()) {
336 nc->current_state = SP_NODE_CONTEXT_NODE_DRAGGING;
337 } else {
338 nc->current_state = SP_NODE_CONTEXT_RUBBERBAND_DRAGGING;
339 }
340 }
342 switch (nc->current_state) {
343 case SP_NODE_CONTEXT_NODE_DRAGGING:
344 {
345 nc->shape_editor->curve_drag (event->motion.x, event->motion.y);
347 gobble_motion_events(GDK_BUTTON1_MASK);
348 break;
349 }
350 case SP_NODE_CONTEXT_RUBBERBAND_DRAGGING:
351 if (Inkscape::Rubberband::get()->is_started()) {
352 NR::Point const motion_w(event->motion.x,
353 event->motion.y);
354 NR::Point const motion_dt(desktop->w2d(motion_w));
355 Inkscape::Rubberband::get()->move(motion_dt);
356 }
357 break;
358 }
360 nc->drag = TRUE;
361 ret = TRUE;
362 } else {
363 if (!nc->shape_editor->has_nodepath() || selection->singleItem() == NULL) {
364 break;
365 }
367 bool over_stroke = false;
368 over_stroke = nc->shape_editor->is_over_stroke(NR::Point(event->motion.x, event->motion.y), false);
370 if (nc->cursor_drag && !over_stroke) {
371 event_context->cursor_shape = cursor_node_xpm;
372 event_context->hot_x = 1;
373 event_context->hot_y = 1;
374 sp_event_context_update_cursor(event_context);
375 nc->cursor_drag = false;
376 } else if (!nc->cursor_drag && over_stroke) {
377 event_context->cursor_shape = cursor_node_d_xpm;
378 event_context->hot_x = 1;
379 event_context->hot_y = 1;
380 sp_event_context_update_cursor(event_context);
381 nc->cursor_drag = true;
382 }
383 }
384 break;
386 case GDK_2BUTTON_PRESS:
387 case GDK_BUTTON_RELEASE:
388 if ( (event->button.button == 1) && (!nc->drag) && !event_context->space_panning) {
389 // find out clicked item, disregarding groups, honoring Alt
390 SPItem *item_clicked = sp_event_context_find_item (desktop,
391 NR::Point(event->button.x, event->button.y),
392 (event->button.state & GDK_MOD1_MASK) && !(event->button.state & GDK_CONTROL_MASK), TRUE);
394 event_context->xp = event_context->yp = 0;
396 bool over_stroke = false;
397 if (nc->shape_editor->has_nodepath()) {
398 over_stroke = nc->shape_editor->is_over_stroke(NR::Point(event->button.x, event->button.y), false);
399 }
401 if (item_clicked || over_stroke) {
402 if (over_stroke || nc->added_node) {
403 switch (event->type) {
404 case GDK_BUTTON_RELEASE:
405 if (event->button.state & GDK_CONTROL_MASK && event->button.state & GDK_MOD1_MASK) {
406 //add a node
407 nc->shape_editor->add_node_near_point();
408 } else {
409 if (nc->added_node) { // we just received double click, ignore release
410 nc->added_node = false;
411 break;
412 }
413 //select the segment
414 if (event->button.state & GDK_SHIFT_MASK) {
415 nc->shape_editor->select_segment_near_point(true);
416 } else {
417 nc->shape_editor->select_segment_near_point(false);
418 }
419 desktop->updateNow();
420 }
421 break;
422 case GDK_2BUTTON_PRESS:
423 //add a node
424 nc->shape_editor->add_node_near_point();
425 nc->added_node = true;
426 break;
427 default:
428 break;
429 }
430 } else if (event->button.state & GDK_SHIFT_MASK) {
431 selection->toggle(item_clicked);
432 desktop->updateNow();
433 } else {
434 selection->set(item_clicked);
435 desktop->updateNow();
436 }
437 Inkscape::Rubberband::get()->stop();
438 ret = TRUE;
439 break;
440 }
441 }
442 if (event->type == GDK_BUTTON_RELEASE) {
443 event_context->xp = event_context->yp = 0;
444 if (event->button.button == 1) {
445 NR::Maybe<NR::Rect> b = Inkscape::Rubberband::get()->getRectangle();
447 if (nc->shape_editor->hits_curve() && !event_context->within_tolerance) { //drag curve
448 nc->shape_editor->finish_drag();
449 } else if (b && !event_context->within_tolerance) { // drag to select
450 nc->shape_editor->select_rect(*b, event->button.state & GDK_SHIFT_MASK);
451 } else {
452 if (!(nc->rb_escaped)) { // unless something was cancelled
453 if (nc->shape_editor->has_selection())
454 nc->shape_editor->deselect();
455 else
456 sp_desktop_selection(desktop)->clear();
457 }
458 }
459 ret = TRUE;
460 Inkscape::Rubberband::get()->stop();
461 desktop->updateNow();
462 nc->rb_escaped = false;
463 nc->drag = FALSE;
464 nc->shape_editor->cancel_hit();
465 nc->current_state = SP_NODE_CONTEXT_INACTIVE;
466 }
467 }
468 break;
469 case GDK_KEY_PRESS:
470 switch (get_group0_keyval(&event->key)) {
471 case GDK_Insert:
472 case GDK_KP_Insert:
473 // with any modifiers
474 nc->shape_editor->add_node();
475 ret = TRUE;
476 break;
477 case GDK_Delete:
478 case GDK_KP_Delete:
479 case GDK_BackSpace:
480 if (MOD__CTRL_ONLY) {
481 nc->shape_editor->delete_nodes();
482 } else {
483 nc->shape_editor->delete_nodes_preserving_shape();
484 }
485 ret = TRUE;
486 break;
487 case GDK_C:
488 case GDK_c:
489 if (MOD__SHIFT_ONLY) {
490 nc->shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
491 ret = TRUE;
492 }
493 break;
494 case GDK_S:
495 case GDK_s:
496 if (MOD__SHIFT_ONLY) {
497 nc->shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
498 ret = TRUE;
499 }
500 break;
501 case GDK_Y:
502 case GDK_y:
503 if (MOD__SHIFT_ONLY) {
504 nc->shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
505 ret = TRUE;
506 }
507 break;
508 case GDK_B:
509 case GDK_b:
510 if (MOD__SHIFT_ONLY) {
511 nc->shape_editor->break_at_nodes();
512 ret = TRUE;
513 }
514 break;
515 case GDK_J:
516 case GDK_j:
517 if (MOD__SHIFT_ONLY) {
518 nc->shape_editor->join_nodes();
519 ret = TRUE;
520 }
521 break;
522 case GDK_D:
523 case GDK_d:
524 if (MOD__SHIFT_ONLY) {
525 nc->shape_editor->duplicate_nodes();
526 ret = TRUE;
527 }
528 break;
529 case GDK_L:
530 case GDK_l:
531 if (MOD__SHIFT_ONLY) {
532 nc->shape_editor->set_type_of_segments(NR_LINETO);
533 ret = TRUE;
534 }
535 break;
536 case GDK_U:
537 case GDK_u:
538 if (MOD__SHIFT_ONLY) {
539 nc->shape_editor->set_type_of_segments(NR_CURVETO);
540 ret = TRUE;
541 }
542 break;
543 case GDK_R:
544 case GDK_r:
545 if (MOD__SHIFT_ONLY) {
546 // FIXME: add top panel button
547 sp_selected_path_reverse();
548 ret = TRUE;
549 }
550 break;
551 case GDK_x:
552 case GDK_X:
553 if (MOD__ALT_ONLY) {
554 desktop->setToolboxFocusTo ("altx-nodes");
555 ret = TRUE;
556 }
557 break;
558 case GDK_Left: // move selection left
559 case GDK_KP_Left:
560 case GDK_KP_4:
561 if (!MOD__CTRL) { // not ctrl
562 gint mul = 1 + gobble_key_events(
563 get_group0_keyval(&event->key), 0); // with any mask
564 if (MOD__ALT) { // alt
565 if (MOD__SHIFT) nc->shape_editor->move_nodes_screen(mul*-10, 0); // shift
566 else nc->shape_editor->move_nodes_screen(mul*-1, 0); // no shift
567 }
568 else { // no alt
569 if (MOD__SHIFT) nc->shape_editor->move_nodes(mul*-10*nudge, 0); // shift
570 else nc->shape_editor->move_nodes(mul*-nudge, 0); // no shift
571 }
572 ret = TRUE;
573 }
574 break;
575 case GDK_Up: // move selection up
576 case GDK_KP_Up:
577 case GDK_KP_8:
578 if (!MOD__CTRL) { // not ctrl
579 gint mul = 1 + gobble_key_events(
580 get_group0_keyval(&event->key), 0); // with any mask
581 if (MOD__ALT) { // alt
582 if (MOD__SHIFT) nc->shape_editor->move_nodes_screen(0, mul*10); // shift
583 else nc->shape_editor->move_nodes_screen(0, mul*1); // no shift
584 }
585 else { // no alt
586 if (MOD__SHIFT) nc->shape_editor->move_nodes(0, mul*10*nudge); // shift
587 else nc->shape_editor->move_nodes(0, mul*nudge); // no shift
588 }
589 ret = TRUE;
590 }
591 break;
592 case GDK_Right: // move selection right
593 case GDK_KP_Right:
594 case GDK_KP_6:
595 if (!MOD__CTRL) { // not ctrl
596 gint mul = 1 + gobble_key_events(
597 get_group0_keyval(&event->key), 0); // with any mask
598 if (MOD__ALT) { // alt
599 if (MOD__SHIFT) nc->shape_editor->move_nodes_screen(mul*10, 0); // shift
600 else nc->shape_editor->move_nodes_screen(mul*1, 0); // no shift
601 }
602 else { // no alt
603 if (MOD__SHIFT) nc->shape_editor->move_nodes(mul*10*nudge, 0); // shift
604 else nc->shape_editor->move_nodes(mul*nudge, 0); // no shift
605 }
606 ret = TRUE;
607 }
608 break;
609 case GDK_Down: // move selection down
610 case GDK_KP_Down:
611 case GDK_KP_2:
612 if (!MOD__CTRL) { // not ctrl
613 gint mul = 1 + gobble_key_events(
614 get_group0_keyval(&event->key), 0); // with any mask
615 if (MOD__ALT) { // alt
616 if (MOD__SHIFT) nc->shape_editor->move_nodes_screen(0, mul*-10); // shift
617 else nc->shape_editor->move_nodes_screen(0, mul*-1); // no shift
618 }
619 else { // no alt
620 if (MOD__SHIFT) nc->shape_editor->move_nodes(0, mul*-10*nudge); // shift
621 else nc->shape_editor->move_nodes(0, mul*-nudge); // no shift
622 }
623 ret = TRUE;
624 }
625 break;
626 case GDK_Escape:
627 {
628 NR::Maybe<NR::Rect> const b = Inkscape::Rubberband::get()->getRectangle();
629 if (b) {
630 Inkscape::Rubberband::get()->stop();
631 nc->current_state = SP_NODE_CONTEXT_INACTIVE;
632 nc->rb_escaped = true;
633 } else {
634 if (nc->shape_editor->has_selection()) {
635 nc->shape_editor->deselect();
636 } else {
637 sp_desktop_selection(desktop)->clear();
638 }
639 }
640 ret = TRUE;
641 break;
642 }
644 case GDK_bracketleft:
645 if ( MOD__CTRL && !MOD__ALT && ( snaps != 0 ) ) {
646 if (nc->leftctrl)
647 nc->shape_editor->rotate_nodes (M_PI/snaps, -1, false);
648 if (nc->rightctrl)
649 nc->shape_editor->rotate_nodes (M_PI/snaps, 1, false);
650 } else if ( MOD__ALT && !MOD__CTRL ) {
651 if (nc->leftalt && nc->rightalt)
652 nc->shape_editor->rotate_nodes (1, 0, true);
653 else {
654 if (nc->leftalt)
655 nc->shape_editor->rotate_nodes (1, -1, true);
656 if (nc->rightalt)
657 nc->shape_editor->rotate_nodes (1, 1, true);
658 }
659 } else if ( snaps != 0 ) {
660 nc->shape_editor->rotate_nodes (M_PI/snaps, 0, false);
661 }
662 ret = TRUE;
663 break;
664 case GDK_bracketright:
665 if ( MOD__CTRL && !MOD__ALT && ( snaps != 0 ) ) {
666 if (nc->leftctrl)
667 nc->shape_editor->rotate_nodes (-M_PI/snaps, -1, false);
668 if (nc->rightctrl)
669 nc->shape_editor->rotate_nodes (-M_PI/snaps, 1, false);
670 } else if ( MOD__ALT && !MOD__CTRL ) {
671 if (nc->leftalt && nc->rightalt)
672 nc->shape_editor->rotate_nodes (-1, 0, true);
673 else {
674 if (nc->leftalt)
675 nc->shape_editor->rotate_nodes (-1, -1, true);
676 if (nc->rightalt)
677 nc->shape_editor->rotate_nodes (-1, 1, true);
678 }
679 } else if ( snaps != 0 ) {
680 nc->shape_editor->rotate_nodes (-M_PI/snaps, 0, false);
681 }
682 ret = TRUE;
683 break;
684 case GDK_less:
685 case GDK_comma:
686 if (MOD__CTRL) {
687 if (nc->leftctrl)
688 nc->shape_editor->scale_nodes(-offset, -1);
689 if (nc->rightctrl)
690 nc->shape_editor->scale_nodes(-offset, 1);
691 } else if (MOD__ALT) {
692 if (nc->leftalt && nc->rightalt)
693 nc->shape_editor->scale_nodes_screen (-1, 0);
694 else {
695 if (nc->leftalt)
696 nc->shape_editor->scale_nodes_screen (-1, -1);
697 if (nc->rightalt)
698 nc->shape_editor->scale_nodes_screen (-1, 1);
699 }
700 } else {
701 nc->shape_editor->scale_nodes (-offset, 0);
702 }
703 ret = TRUE;
704 break;
705 case GDK_greater:
706 case GDK_period:
707 if (MOD__CTRL) {
708 if (nc->leftctrl)
709 nc->shape_editor->scale_nodes (offset, -1);
710 if (nc->rightctrl)
711 nc->shape_editor->scale_nodes (offset, 1);
712 } else if (MOD__ALT) {
713 if (nc->leftalt && nc->rightalt)
714 nc->shape_editor->scale_nodes_screen (1, 0);
715 else {
716 if (nc->leftalt)
717 nc->shape_editor->scale_nodes_screen (1, -1);
718 if (nc->rightalt)
719 nc->shape_editor->scale_nodes_screen (1, 1);
720 }
721 } else {
722 nc->shape_editor->scale_nodes (offset, 0);
723 }
724 ret = TRUE;
725 break;
727 case GDK_Alt_L:
728 nc->leftalt = TRUE;
729 sp_node_context_show_modifier_tip(event_context, event);
730 break;
731 case GDK_Alt_R:
732 nc->rightalt = TRUE;
733 sp_node_context_show_modifier_tip(event_context, event);
734 break;
735 case GDK_Control_L:
736 nc->leftctrl = TRUE;
737 sp_node_context_show_modifier_tip(event_context, event);
738 break;
739 case GDK_Control_R:
740 nc->rightctrl = TRUE;
741 sp_node_context_show_modifier_tip(event_context, event);
742 break;
743 case GDK_Shift_L:
744 case GDK_Shift_R:
745 case GDK_Meta_L:
746 case GDK_Meta_R:
747 sp_node_context_show_modifier_tip(event_context, event);
748 break;
749 default:
750 ret = node_key(event);
751 break;
752 }
753 break;
754 case GDK_KEY_RELEASE:
755 switch (get_group0_keyval(&event->key)) {
756 case GDK_Alt_L:
757 nc->leftalt = FALSE;
758 event_context->defaultMessageContext()->clear();
759 break;
760 case GDK_Alt_R:
761 nc->rightalt = FALSE;
762 event_context->defaultMessageContext()->clear();
763 break;
764 case GDK_Control_L:
765 nc->leftctrl = FALSE;
766 event_context->defaultMessageContext()->clear();
767 break;
768 case GDK_Control_R:
769 nc->rightctrl = FALSE;
770 event_context->defaultMessageContext()->clear();
771 break;
772 case GDK_Shift_L:
773 case GDK_Shift_R:
774 case GDK_Meta_L:
775 case GDK_Meta_R:
776 event_context->defaultMessageContext()->clear();
777 break;
778 }
779 break;
780 default:
781 break;
782 }
784 if (!ret) {
785 if (((SPEventContextClass *) parent_class)->root_handler)
786 ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event);
787 }
789 return ret;
790 }
793 /*
794 Local Variables:
795 mode:c++
796 c-file-style:"stroustrup"
797 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
798 indent-tabs-mode:nil
799 fill-column:99
800 End:
801 */
802 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :