Code

only display flash helperpath warning for shapes other than paths
[inkscape.git] / src / node-context.cpp
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;
105     
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;
113 static void
114 sp_node_context_dispose(GObject *object)
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 nc->shape_editor;
126     if (nc->_node_message_context) {
127         delete nc->_node_message_context;
128     }
130     G_OBJECT_CLASS(parent_class)->dispose(object);
133 static void
134 sp_node_context_setup(SPEventContext *ec)
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     nc->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         nc->shape_editor->set_item(item, SH_NODEPATH);
160         nc->shape_editor->set_item(item, SH_KNOTHOLDER);
161     }
163     if (prefs_get_int_attribute("tools.nodes", "selcue", 0) != 0) {
164         ec->enableSelectionCue();
165     }
167     if (prefs_get_int_attribute("tools.nodes", "gradientdrag", 0) != 0) {
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     nc->shape_editor->update_statusbar();
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 = NULL;
193         if (SP_IS_PATH(item)) {
194             canvasitem = sp_nodepath_helperpath_from_path(desktop, SP_PATH(item));
195         } else {
196             g_print ("-----> sp_node_context_flash_path(): TODO: generate the helper path!!\n");
197         }
198         
199         if (canvasitem) {
200             nc->flash_tempitem = desktop->add_temporary_canvasitem (canvasitem, timeout);
201         }
202     }
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)
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();
223 void
224 sp_node_context_show_modifier_tip(SPEventContext *event_context, GdkEvent *event)
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"));
233 static gint
234 sp_node_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event)
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;
258 static gint
259 sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
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(desktop)->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.
321                 
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(desktop)->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(desktop)->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(desktop)->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                     boost::optional<NR::Rect> b = Inkscape::Rubberband::get(desktop)->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(desktop)->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(desktop);
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(desktop, mul*-10, 0); // shift
566                             else nc->shape_editor->move_nodes_screen(desktop, 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(desktop, 0, mul*10); // shift
583                             else nc->shape_editor->move_nodes_screen(desktop, 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(desktop, mul*10, 0); // shift
600                             else nc->shape_editor->move_nodes_screen(desktop, 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(desktop, 0, mul*-10); // shift
617                             else nc->shape_editor->move_nodes_screen(desktop, 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                     boost::optional<NR::Rect> const b = Inkscape::Rubberband::get(desktop)->getRectangle();
629                     if (b) {
630                         Inkscape::Rubberband::get(desktop)->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;
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 :