Code

prevent wrong rubberband origin after click-selecting in node tool
[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 <gdk/gdkkeysyms.h>
17 #include "macros.h"
18 #include <glibmm/i18n.h>
19 #include "display/sp-canvas-util.h"
20 #include "object-edit.h"
21 #include "sp-path.h"
22 #include "path-chemistry.h"
23 #include "rubberband.h"
24 #include "desktop.h"
25 #include "desktop-handles.h"
26 #include "selection.h"
27 #include "pixmaps/cursor-node.xpm"
28 #include "message-context.h"
29 #include "node-context.h"
30 #include "pixmaps/cursor-node-d.xpm"
31 #include "prefs-utils.h"
32 #include "xml/node-event-vector.h"
33 #include "style.h"
34 #include "splivarot.h"
35 #include "shape-editor.h"
37 static void sp_node_context_class_init(SPNodeContextClass *klass);
38 static void sp_node_context_init(SPNodeContext *node_context);
39 static void sp_node_context_dispose(GObject *object);
41 static void sp_node_context_setup(SPEventContext *ec);
42 static gint sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event);
43 static gint sp_node_context_item_handler(SPEventContext *event_context,
44                                          SPItem *item, GdkEvent *event);
46 static SPEventContextClass *parent_class;
48 GType
49 sp_node_context_get_type()
50 {
51     static GType type = 0;
52     if (!type) {
53         GTypeInfo info = {
54             sizeof(SPNodeContextClass),
55             NULL, NULL,
56             (GClassInitFunc) sp_node_context_class_init,
57             NULL, NULL,
58             sizeof(SPNodeContext),
59             4,
60             (GInstanceInitFunc) sp_node_context_init,
61             NULL,    /* value_table */
62         };
63         type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "SPNodeContext", &info, (GTypeFlags)0);
64     }
65     return type;
66 }
68 static void
69 sp_node_context_class_init(SPNodeContextClass *klass)
70 {
71     GObjectClass *object_class = (GObjectClass *) klass;
72     SPEventContextClass *event_context_class = (SPEventContextClass *) klass;
74     parent_class = (SPEventContextClass*)g_type_class_peek_parent(klass);
76     object_class->dispose = sp_node_context_dispose;
78     event_context_class->setup = sp_node_context_setup;
79     event_context_class->root_handler = sp_node_context_root_handler;
80     event_context_class->item_handler = sp_node_context_item_handler;
81 }
83 static void
84 sp_node_context_init(SPNodeContext *node_context)
85 {
86     SPEventContext *event_context = SP_EVENT_CONTEXT(node_context);
88     event_context->cursor_shape = cursor_node_xpm;
89     event_context->hot_x = 1;
90     event_context->hot_y = 1;
92     node_context->leftalt = FALSE;
93     node_context->rightalt = FALSE;
94     node_context->leftctrl = FALSE;
95     node_context->rightctrl = FALSE;
96     
97     new (&node_context->sel_changed_connection) sigc::connection();
98 }
100 static void
101 sp_node_context_dispose(GObject *object)
103     SPNodeContext *nc = SP_NODE_CONTEXT(object);
104     SPEventContext *ec = SP_EVENT_CONTEXT(object);
106     ec->enableGrDrag(false);
108     nc->sel_changed_connection.disconnect();
109     nc->sel_changed_connection.~connection();
111     delete nc->shape_editor;
113     if (nc->_node_message_context) {
114         delete nc->_node_message_context;
115     }
117     G_OBJECT_CLASS(parent_class)->dispose(object);
120 static void
121 sp_node_context_setup(SPEventContext *ec)
123     SPNodeContext *nc = SP_NODE_CONTEXT(ec);
125     if (((SPEventContextClass *) parent_class)->setup)
126         ((SPEventContextClass *) parent_class)->setup(ec);
128     nc->sel_changed_connection.disconnect();
129     nc->sel_changed_connection = sp_desktop_selection(ec->desktop)->connectChanged(sigc::bind(sigc::ptr_fun(&sp_node_context_selection_changed), (gpointer)nc));
131     Inkscape::Selection *selection = sp_desktop_selection(ec->desktop);
132     SPItem *item = selection->singleItem();
134     nc->shape_editor = new ShapeEditor(ec->desktop);
136     nc->rb_escaped = false;
138     nc->cursor_drag = false;
140     nc->added_node = false;
142     nc->current_state = SP_NODE_CONTEXT_INACTIVE;
144     if (item) {
145         nc->shape_editor->set_item(item);
146     }
148     if (prefs_get_int_attribute("tools.nodes", "selcue", 0) != 0) {
149         ec->enableSelectionCue();
150     }
152     if (prefs_get_int_attribute("tools.nodes", "gradientdrag", 0) != 0) {
153         ec->enableGrDrag();
154     }
156     nc->_node_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack());
158     nc->shape_editor->update_statusbar();
161 /**
162 \brief  Callback that processes the "changed" signal on the selection;
163 destroys old and creates new nodepath and reassigns listeners to the new selected item's repr
164 */
165 void
166 sp_node_context_selection_changed(Inkscape::Selection *selection, gpointer data)
168     SPNodeContext *nc = SP_NODE_CONTEXT(data);
170     // TODO: update ShapeEditorsCollective instead
171     nc->shape_editor->unset_item();
172     SPItem *item = selection->singleItem(); 
173     nc->shape_editor->set_item(item);
175     nc->shape_editor->update_statusbar();
178 void
179 sp_node_context_show_modifier_tip(SPEventContext *event_context, GdkEvent *event)
181     sp_event_show_modifier_tip
182         (event_context->defaultMessageContext(), event,
183          _("<b>Ctrl</b>: toggle node type, snap handle angle, move hor/vert; <b>Ctrl+Alt</b>: move along handles"),
184          _("<b>Shift</b>: toggle node selection, disable snapping, rotate both handles"),
185          _("<b>Alt</b>: lock handle length; <b>Ctrl+Alt</b>: move along handles"));
189 static gint
190 sp_node_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event)
192     gint ret = FALSE;
194     if (((SPEventContextClass *) parent_class)->item_handler)
195         ret = ((SPEventContextClass *) parent_class)->item_handler(event_context, item, event);
197     return ret;
200 static gint
201 sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
203     SPDesktop *desktop = event_context->desktop;
204     Inkscape::Selection *selection = sp_desktop_selection (desktop);
206     SPNodeContext *nc = SP_NODE_CONTEXT(event_context);
207     double const nudge = prefs_get_double_attribute_limited("options.nudgedistance", "value", 2, 0, 1000); // in px
208     event_context->tolerance = prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100); // read every time, to make prefs changes really live
209     int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
210     double const offset = prefs_get_double_attribute_limited("options.defaultscale", "value", 2, 0, 1000);
212     gint ret = FALSE;
213     switch (event->type) {
214         case GDK_BUTTON_PRESS:
215             if (event->button.button == 1 && !event_context->space_panning) {
216                 // save drag origin
217                 event_context->xp = (gint) event->button.x;
218                 event_context->yp = (gint) event->button.y;
219                 event_context->within_tolerance = true;
220                 nc->shape_editor->cancel_hit();
222                 if (!(event->button.state & GDK_SHIFT_MASK)) {
223                     if (!nc->drag) {
224                         if (nc->shape_editor->has_nodepath() && selection->single() /* && item_over */) {
225                             // save drag origin
226                             bool over_stroke = nc->shape_editor->is_over_stroke(NR::Point(event->button.x, event->button.y), true);
227                             //only dragging curves
228                             if (over_stroke) {
229                                 ret = TRUE;
230                                 break;
231                             }
232                         }
233                     }
234                 }
235                 NR::Point const button_w(event->button.x,
236                                          event->button.y);
237                 NR::Point const button_dt(desktop->w2d(button_w));
238                 Inkscape::Rubberband::get()->start(desktop, button_dt);
239                 nc->current_state = SP_NODE_CONTEXT_INACTIVE;
240                 desktop->updateNow();
241                 ret = TRUE;
242             }
243             break;
244         case GDK_MOTION_NOTIFY:
245             if (event->motion.state & GDK_BUTTON1_MASK && !event_context->space_panning) {
247                 if ( event_context->within_tolerance
248                      && ( abs( (gint) event->motion.x - event_context->xp ) < event_context->tolerance )
249                      && ( abs( (gint) event->motion.y - event_context->yp ) < event_context->tolerance ) ) {
250                     break; // do not drag if we're within tolerance from origin
251                 }
253                 // The path went away while dragging; throw away any further motion
254                 // events until the mouse pointer is released.
255                 
256                 if (nc->shape_editor->hits_curve() && !nc->shape_editor->has_nodepath()) {
257                   break;
258                 }
260                 // Once the user has moved farther than tolerance from the original location
261                 // (indicating they intend to move the object, not click), then always process the
262                 // motion notify coordinates as given (no snapping back to origin)
263                 event_context->within_tolerance = false;
265                 // Once we determine what the user is doing (dragging either a node or the
266                 // selection rubberband), make sure we continue to perform that operation
267                 // until the mouse pointer is lifted.
268                 if (nc->current_state == SP_NODE_CONTEXT_INACTIVE) {
269                     if (nc->shape_editor->hits_curve() && nc->shape_editor->has_nodepath()) {
270                         nc->current_state = SP_NODE_CONTEXT_NODE_DRAGGING;
271                     } else {
272                         nc->current_state = SP_NODE_CONTEXT_RUBBERBAND_DRAGGING;
273                     }
274                 }
276                 switch (nc->current_state) {
277                     case SP_NODE_CONTEXT_NODE_DRAGGING:
278                         {
279                             nc->shape_editor->curve_drag (event->motion.x, event->motion.y);
281                             gobble_motion_events(GDK_BUTTON1_MASK);
282                             break;
283                         }
284                     case SP_NODE_CONTEXT_RUBBERBAND_DRAGGING:
285                         if (Inkscape::Rubberband::get()->is_started()) {
286                             NR::Point const motion_w(event->motion.x,
287                                                 event->motion.y);
288                             NR::Point const motion_dt(desktop->w2d(motion_w));
289                             Inkscape::Rubberband::get()->move(motion_dt);
290                         }
291                         break;
292                 }
294                 nc->drag = TRUE;
295                 ret = TRUE;
296             } else {
297                 if (!nc->shape_editor->has_nodepath() || selection->singleItem() == NULL) {
298                     break;
299                 }
301                 bool over_stroke = false;
302                 over_stroke = nc->shape_editor->is_over_stroke(NR::Point(event->motion.x, event->motion.y), false);
304                 if (nc->cursor_drag && !over_stroke) {
305                     event_context->cursor_shape = cursor_node_xpm;
306                     event_context->hot_x = 1;
307                     event_context->hot_y = 1;
308                     sp_event_context_update_cursor(event_context);
309                     nc->cursor_drag = false;
310                 } else if (!nc->cursor_drag && over_stroke) {
311                     event_context->cursor_shape = cursor_node_d_xpm;
312                     event_context->hot_x = 1;
313                     event_context->hot_y = 1;
314                     sp_event_context_update_cursor(event_context);
315                     nc->cursor_drag = true;
316                 }
317             }
318             break;
320         case GDK_2BUTTON_PRESS:
321         case GDK_BUTTON_RELEASE:
322             if ( (event->button.button == 1) && (!nc->drag) && !event_context->space_panning) {
323                 // find out clicked item, disregarding groups, honoring Alt
324                 SPItem *item_clicked = sp_event_context_find_item (desktop,
325                         NR::Point(event->button.x, event->button.y),
326                         (event->button.state & GDK_MOD1_MASK) && !(event->button.state & GDK_CONTROL_MASK), TRUE);
328                 event_context->xp = event_context->yp = 0;
330                 bool over_stroke = false;
331                 if (nc->shape_editor->has_nodepath()) {
332                     over_stroke = nc->shape_editor->is_over_stroke(NR::Point(event->button.x, event->button.y), false);
333                 }
335                 if (item_clicked || over_stroke) {
336                     if (over_stroke || nc->added_node) {
337                         switch (event->type) {
338                             case GDK_BUTTON_RELEASE:
339                                 if (event->button.state & GDK_CONTROL_MASK && event->button.state & GDK_MOD1_MASK) {
340                                     //add a node
341                                     nc->shape_editor->add_node_near_point();
342                                 } else {
343                                     if (nc->added_node) { // we just received double click, ignore release
344                                         nc->added_node = false;
345                                         break;
346                                     }
347                                     //select the segment
348                                     if (event->button.state & GDK_SHIFT_MASK) {
349                                         nc->shape_editor->select_segment_near_point(true);
350                                     } else {
351                                         nc->shape_editor->select_segment_near_point(false);
352                                     }
353                                     desktop->updateNow();
354                                 }
355                                 break;
356                             case GDK_2BUTTON_PRESS:
357                                 //add a node
358                                 nc->shape_editor->add_node_near_point();
359                                 nc->added_node = true;
360                                 break;
361                             default:
362                                 break;
363                         }
364                     } else if (event->button.state & GDK_SHIFT_MASK) {
365                         selection->toggle(item_clicked);
366                         desktop->updateNow();
367                     } else {
368                         selection->set(item_clicked);
369                         desktop->updateNow();
370                     }
371                     Inkscape::Rubberband::get()->stop();
372                     ret = TRUE;
373                     break;
374                 }
375             } 
376             if (event->type == GDK_BUTTON_RELEASE) {
377                 event_context->xp = event_context->yp = 0;
378                 if (event->button.button == 1) {
379                     NR::Maybe<NR::Rect> b = Inkscape::Rubberband::get()->getRectangle();
381                     if (nc->shape_editor->hits_curve() && !event_context->within_tolerance) { //drag curve
382                         nc->shape_editor->finish_drag();
383                     } else if (b && !event_context->within_tolerance) { // drag to select
384                         nc->shape_editor->select_rect(*b, event->button.state & GDK_SHIFT_MASK);
385                     } else {
386                         if (!(nc->rb_escaped)) { // unless something was cancelled
387                             if (nc->shape_editor->has_selection())
388                                 nc->shape_editor->deselect();
389                             else
390                                 sp_desktop_selection(desktop)->clear();
391                         }
392                     }
393                     ret = TRUE;
394                     Inkscape::Rubberband::get()->stop();
395                     desktop->updateNow();
396                     nc->rb_escaped = false;
397                     nc->drag = FALSE;
398                     nc->shape_editor->cancel_hit();
399                     nc->current_state = SP_NODE_CONTEXT_INACTIVE;
400                 }
401             }
402             break;
403         case GDK_KEY_PRESS:
404             switch (get_group0_keyval(&event->key)) {
405                 case GDK_Insert:
406                 case GDK_KP_Insert:
407                     // with any modifiers
408                     nc->shape_editor->add_node();
409                     ret = TRUE;
410                     break;
411                 case GDK_Delete:
412                 case GDK_KP_Delete:
413                 case GDK_BackSpace:
414                     if (MOD__CTRL_ONLY) {
415                         nc->shape_editor->delete_nodes();
416                     } else {
417                         nc->shape_editor->delete_nodes_preserving_shape();
418                     }
419                     ret = TRUE;
420                     break;
421                 case GDK_C:
422                 case GDK_c:
423                     if (MOD__SHIFT_ONLY) {
424                         nc->shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
425                         ret = TRUE;
426                     }
427                     break;
428                 case GDK_S:
429                 case GDK_s:
430                     if (MOD__SHIFT_ONLY) {
431                         nc->shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
432                         ret = TRUE;
433                     }
434                     break;
435                 case GDK_Y:
436                 case GDK_y:
437                     if (MOD__SHIFT_ONLY) {
438                         nc->shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
439                         ret = TRUE;
440                     }
441                     break;
442                 case GDK_B:
443                 case GDK_b:
444                     if (MOD__SHIFT_ONLY) {
445                         nc->shape_editor->break_at_nodes();
446                         ret = TRUE;
447                     }
448                     break;
449                 case GDK_J:
450                 case GDK_j:
451                     if (MOD__SHIFT_ONLY) {
452                         nc->shape_editor->join_nodes();
453                         ret = TRUE;
454                     }
455                     break;
456                 case GDK_D:
457                 case GDK_d:
458                     if (MOD__SHIFT_ONLY) {
459                         nc->shape_editor->duplicate_nodes();
460                         ret = TRUE;
461                     }
462                     break;
463                 case GDK_L:
464                 case GDK_l:
465                     if (MOD__SHIFT_ONLY) {
466                         nc->shape_editor->set_type_of_segments(NR_LINETO);
467                         ret = TRUE;
468                     }
469                     break;
470                 case GDK_U:
471                 case GDK_u:
472                     if (MOD__SHIFT_ONLY) {
473                         nc->shape_editor->set_type_of_segments(NR_CURVETO);
474                         ret = TRUE;
475                     }
476                     break;
477                 case GDK_R:
478                 case GDK_r:
479                     if (MOD__SHIFT_ONLY) {
480                         // FIXME: add top panel button
481                         sp_selected_path_reverse();
482                         ret = TRUE;
483                     }
484                     break;
485                 case GDK_Left: // move selection left
486                 case GDK_KP_Left:
487                 case GDK_KP_4:
488                     if (!MOD__CTRL) { // not ctrl
489                         if (MOD__ALT) { // alt
490                             if (MOD__SHIFT) nc->shape_editor->move_nodes_screen(-10, 0); // shift
491                             else nc->shape_editor->move_nodes_screen(-1, 0); // no shift
492                         }
493                         else { // no alt
494                             if (MOD__SHIFT) nc->shape_editor->move_nodes(-10*nudge, 0); // shift
495                             else nc->shape_editor->move_nodes(-nudge, 0); // no shift
496                         }
497                         ret = TRUE;
498                     }
499                     break;
500                 case GDK_Up: // move selection up
501                 case GDK_KP_Up:
502                 case GDK_KP_8:
503                     if (!MOD__CTRL) { // not ctrl
504                         if (MOD__ALT) { // alt
505                             if (MOD__SHIFT) nc->shape_editor->move_nodes_screen(0, 10); // shift
506                             else nc->shape_editor->move_nodes_screen(0, 1); // no shift
507                         }
508                         else { // no alt
509                             if (MOD__SHIFT) nc->shape_editor->move_nodes(0, 10*nudge); // shift
510                             else nc->shape_editor->move_nodes(0, nudge); // no shift
511                         }
512                         ret = TRUE;
513                     }
514                     break;
515                 case GDK_Right: // move selection right
516                 case GDK_KP_Right:
517                 case GDK_KP_6:
518                     if (!MOD__CTRL) { // not ctrl
519                         if (MOD__ALT) { // alt
520                             if (MOD__SHIFT) nc->shape_editor->move_nodes_screen(10, 0); // shift
521                             else nc->shape_editor->move_nodes_screen(1, 0); // no shift
522                         }
523                         else { // no alt
524                             if (MOD__SHIFT) nc->shape_editor->move_nodes(10*nudge, 0); // shift
525                             else nc->shape_editor->move_nodes(nudge, 0); // no shift
526                         }
527                         ret = TRUE;
528                     }
529                     break;
530                 case GDK_Down: // move selection down
531                 case GDK_KP_Down:
532                 case GDK_KP_2:
533                     if (!MOD__CTRL) { // not ctrl
534                         if (MOD__ALT) { // alt
535                             if (MOD__SHIFT) nc->shape_editor->move_nodes_screen(0, -10); // shift
536                             else nc->shape_editor->move_nodes_screen(0, -1); // no shift
537                         }
538                         else { // no alt
539                             if (MOD__SHIFT) nc->shape_editor->move_nodes(0, -10*nudge); // shift
540                             else nc->shape_editor->move_nodes(0, -nudge); // no shift
541                         }
542                         ret = TRUE;
543                     }
544                     break;
545                 case GDK_Escape:
546                 {
547                     NR::Maybe<NR::Rect> const b = Inkscape::Rubberband::get()->getRectangle();
548                     if (b) {
549                         Inkscape::Rubberband::get()->stop();
550                         nc->current_state = SP_NODE_CONTEXT_INACTIVE;
551                         nc->rb_escaped = true;
552                     } else {
553                         if (nc->shape_editor->has_selection()) {
554                             nc->shape_editor->deselect();
555                         } else {
556                             sp_desktop_selection(desktop)->clear();
557                         }
558                     }
559                     ret = TRUE;
560                     break;
561                 }
563                 case GDK_bracketleft:
564                     if ( MOD__CTRL && !MOD__ALT && ( snaps != 0 ) ) {
565                         if (nc->leftctrl)
566                             nc->shape_editor->rotate_nodes (M_PI/snaps, -1, false);
567                         if (nc->rightctrl)
568                             nc->shape_editor->rotate_nodes (M_PI/snaps, 1, false);
569                     } else if ( MOD__ALT && !MOD__CTRL ) {
570                         if (nc->leftalt && nc->rightalt)
571                             nc->shape_editor->rotate_nodes (1, 0, true);
572                         else {
573                             if (nc->leftalt)
574                                 nc->shape_editor->rotate_nodes (1, -1, true);
575                             if (nc->rightalt)
576                                 nc->shape_editor->rotate_nodes (1, 1, true);
577                         }
578                     } else if ( snaps != 0 ) {
579                         nc->shape_editor->rotate_nodes (M_PI/snaps, 0, false);
580                     }
581                     ret = TRUE;
582                     break;
583                 case GDK_bracketright:
584                     if ( MOD__CTRL && !MOD__ALT && ( snaps != 0 ) ) {
585                         if (nc->leftctrl)
586                             nc->shape_editor->rotate_nodes (-M_PI/snaps, -1, false);
587                         if (nc->rightctrl)
588                             nc->shape_editor->rotate_nodes (-M_PI/snaps, 1, false);
589                     } else if ( MOD__ALT && !MOD__CTRL ) {
590                         if (nc->leftalt && nc->rightalt)
591                             nc->shape_editor->rotate_nodes (-1, 0, true);
592                         else {
593                             if (nc->leftalt)
594                                 nc->shape_editor->rotate_nodes (-1, -1, true);
595                             if (nc->rightalt)
596                                 nc->shape_editor->rotate_nodes (-1, 1, true);
597                         }
598                     } else if ( snaps != 0 ) {
599                         nc->shape_editor->rotate_nodes (-M_PI/snaps, 0, false);
600                     }
601                     ret = TRUE;
602                     break;
603                 case GDK_less:
604                 case GDK_comma:
605                     if (MOD__CTRL) {
606                         if (nc->leftctrl)
607                             nc->shape_editor->scale_nodes(-offset, -1);
608                         if (nc->rightctrl)
609                             nc->shape_editor->scale_nodes(-offset, 1);
610                     } else if (MOD__ALT) {
611                         if (nc->leftalt && nc->rightalt)
612                             nc->shape_editor->scale_nodes_screen (-1, 0);
613                         else {
614                             if (nc->leftalt)
615                                 nc->shape_editor->scale_nodes_screen (-1, -1);
616                             if (nc->rightalt)
617                                 nc->shape_editor->scale_nodes_screen (-1, 1);
618                         }
619                     } else {
620                         nc->shape_editor->scale_nodes (-offset, 0);
621                     }
622                     ret = TRUE;
623                     break;
624                 case GDK_greater:
625                 case GDK_period:
626                     if (MOD__CTRL) {
627                         if (nc->leftctrl)
628                             nc->shape_editor->scale_nodes (offset, -1);
629                         if (nc->rightctrl)
630                             nc->shape_editor->scale_nodes (offset, 1);
631                     } else if (MOD__ALT) {
632                         if (nc->leftalt && nc->rightalt)
633                             nc->shape_editor->scale_nodes_screen (1, 0);
634                         else {
635                             if (nc->leftalt)
636                                 nc->shape_editor->scale_nodes_screen (1, -1);
637                             if (nc->rightalt)
638                                 nc->shape_editor->scale_nodes_screen (1, 1);
639                         }
640                     } else {
641                         nc->shape_editor->scale_nodes (offset, 0);
642                     }
643                     ret = TRUE;
644                     break;
646                 case GDK_Alt_L:
647                     nc->leftalt = TRUE;
648                     sp_node_context_show_modifier_tip(event_context, event);
649                     break;
650                 case GDK_Alt_R:
651                     nc->rightalt = TRUE;
652                     sp_node_context_show_modifier_tip(event_context, event);
653                     break;
654                 case GDK_Control_L:
655                     nc->leftctrl = TRUE;
656                     sp_node_context_show_modifier_tip(event_context, event);
657                     break;
658                 case GDK_Control_R:
659                     nc->rightctrl = TRUE;
660                     sp_node_context_show_modifier_tip(event_context, event);
661                     break;
662                 case GDK_Shift_L:
663                 case GDK_Shift_R:
664                 case GDK_Meta_L:
665                 case GDK_Meta_R:
666                     sp_node_context_show_modifier_tip(event_context, event);
667                     break;
668                 default:
669                     ret = node_key(event);
670                     break;
671             }
672             break;
673         case GDK_KEY_RELEASE:
674             switch (get_group0_keyval(&event->key)) {
675                 case GDK_Alt_L:
676                     nc->leftalt = FALSE;
677                     event_context->defaultMessageContext()->clear();
678                     break;
679                 case GDK_Alt_R:
680                     nc->rightalt = FALSE;
681                     event_context->defaultMessageContext()->clear();
682                     break;
683                 case GDK_Control_L:
684                     nc->leftctrl = FALSE;
685                     event_context->defaultMessageContext()->clear();
686                     break;
687                 case GDK_Control_R:
688                     nc->rightctrl = FALSE;
689                     event_context->defaultMessageContext()->clear();
690                     break;
691                 case GDK_Shift_L:
692                 case GDK_Shift_R:
693                 case GDK_Meta_L:
694                 case GDK_Meta_R:
695                     event_context->defaultMessageContext()->clear();
696                     break;
697             }
698             break;
699         default:
700             break;
701     }
703     if (!ret) {
704         if (((SPEventContextClass *) parent_class)->root_handler)
705             ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event);
706     }
708     return ret;
712 /*
713   Local Variables:
714   mode:c++
715   c-file-style:"stroustrup"
716   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
717   indent-tabs-mode:nil
718   fill-column:99
719   End:
720 */
721 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :