Code

specialize MaybeStorage for Rect, and start using reference maybes to
[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     SPDesktop *desktop = event_context->desktop;
195     Inkscape::Selection *selection = sp_desktop_selection (desktop);
197     SPNodeContext *nc = SP_NODE_CONTEXT(event_context);
199     switch (event->type) {
200         case GDK_2BUTTON_PRESS:
201         case GDK_BUTTON_RELEASE:
202             if (event->button.button == 1) {
203                 if (!nc->drag) {
205                     // find out clicked item, disregarding groups, honoring Alt
206                     SPItem *item_clicked = sp_event_context_find_item (desktop,
207                             NR::Point(event->button.x, event->button.y),
208                             (event->button.state & GDK_MOD1_MASK) && !(event->button.state & GDK_CONTROL_MASK), TRUE);
209                     // find out if we're over the selected item, disregarding groups
210                     SPItem *item_over = sp_event_context_over_item (desktop, selection->singleItem(),
211                                                                     NR::Point(event->button.x, event->button.y));
213                     bool over_stroke = false;
214                     if (item_over && nc->shape_editor->has_nodepath()) {
215                         over_stroke = nc->shape_editor->is_over_stroke(NR::Point(event->button.x, event->button.y), false);
216                     }
218                     if (over_stroke || nc->added_node) {
219                         switch (event->type) {
220                             case GDK_BUTTON_RELEASE:
221                                 if (event->button.state & GDK_CONTROL_MASK && event->button.state & GDK_MOD1_MASK) {
222                                     //add a node
223                                     nc->shape_editor->add_node_near_point();
224                                 } else {
225                                     if (nc->added_node) { // we just received double click, ignore release
226                                         nc->added_node = false;
227                                         break;
228                                     }
229                                     //select the segment
230                                     if (event->button.state & GDK_SHIFT_MASK) {
231                                         nc->shape_editor->select_segment_near_point(true);
232                                     } else {
233                                         nc->shape_editor->select_segment_near_point(false);
234                                     }
235                                     desktop->updateNow();
236                                 }
237                                 break;
238                             case GDK_2BUTTON_PRESS:
239                                 //add a node
240                                 nc->shape_editor->add_node_near_point();
241                                 nc->added_node = true;
242                                 break;
243                             default:
244                                 break;
245                         }
246                     } else if (event->button.state & GDK_SHIFT_MASK) {
247                         selection->toggle(item_clicked);
248                         desktop->updateNow();
249                     } else {
250                         selection->set(item_clicked);
251                         desktop->updateNow();
252                     }
254                     ret = TRUE;
255                 }
256                 break;
257             }
258             break;
259         case GDK_BUTTON_PRESS:
260             if (event->button.button == 1 && !(event->button.state & GDK_SHIFT_MASK)) {
261                 // save drag origin
262                 event_context->xp = (gint) event->button.x;
263                 event_context->yp = (gint) event->button.y;
264                 event_context->within_tolerance = true;
265                 nc->shape_editor->cancel_hit();
267                 if (!nc->drag) {
268                     // find out if we're over the selected item, disregarding groups
269                     SPItem *item_over = sp_event_context_over_item (desktop, selection->singleItem(),
270                                                                     NR::Point(event->button.x, event->button.y));
272                     if (nc->shape_editor->has_nodepath() && selection->single() && item_over) {
274                             // save drag origin
275                             bool over_stroke = nc->shape_editor->is_over_stroke(NR::Point(event->button.x, event->button.y), true);
276                             //only dragging curves
277                             if (over_stroke) {
278                                 ret = TRUE;
279                             } else {
280                                 break;
281                             }
282                         } else {
283                             break;
284                         }
286                     ret = TRUE;
287                 }
288                 break;
289             }
290             break;
291         default:
292             break;
293     }
295     if (!ret) {
296         if (((SPEventContextClass *) parent_class)->item_handler)
297             ret = ((SPEventContextClass *) parent_class)->item_handler(event_context, item, event);
298     }
300     return ret;
303 static gint
304 sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
306     SPDesktop *desktop = event_context->desktop;
307     Inkscape::Selection *selection = sp_desktop_selection (desktop);
309     SPNodeContext *nc = SP_NODE_CONTEXT(event_context);
310     double const nudge = prefs_get_double_attribute_limited("options.nudgedistance", "value", 2, 0, 1000); // in px
311     event_context->tolerance = prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100); // read every time, to make prefs changes really live
312     int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
313     double const offset = prefs_get_double_attribute_limited("options.defaultscale", "value", 2, 0, 1000);
315     gint ret = FALSE;
317     switch (event->type) {
318         case GDK_BUTTON_PRESS:
319             if (event->button.button == 1) {
320                 // save drag origin
321                 event_context->xp = (gint) event->button.x;
322                 event_context->yp = (gint) event->button.y;
323                 event_context->within_tolerance = true;
324                 nc->shape_editor->cancel_hit();
326                 NR::Point const button_w(event->button.x,
327                                          event->button.y);
328                 NR::Point const button_dt(desktop->w2d(button_w));
329                 Inkscape::Rubberband::get()->start(desktop, button_dt);
330                 nc->current_state = SP_NODE_CONTEXT_INACTIVE;
331                 desktop->updateNow();
332                 ret = TRUE;
333             }
334             break;
335         case GDK_MOTION_NOTIFY:
336             if (event->motion.state & GDK_BUTTON1_MASK) {
338                 if ( event_context->within_tolerance
339                      && ( abs( (gint) event->motion.x - event_context->xp ) < event_context->tolerance )
340                      && ( abs( (gint) event->motion.y - event_context->yp ) < event_context->tolerance ) ) {
341                     break; // do not drag if we're within tolerance from origin
342                 }
344                 // The path went away while dragging; throw away any further motion
345                 // events until the mouse pointer is released.
346                 
347                 if (nc->shape_editor->hits_curve() && !nc->shape_editor->has_nodepath()) {
348                   break;
349                 }
351                 // Once the user has moved farther than tolerance from the original location
352                 // (indicating they intend to move the object, not click), then always process the
353                 // motion notify coordinates as given (no snapping back to origin)
354                 event_context->within_tolerance = false;
356                 // Once we determine what the user is doing (dragging either a node or the
357                 // selection rubberband), make sure we continue to perform that operation
358                 // until the mouse pointer is lifted.
359                 if (nc->current_state == SP_NODE_CONTEXT_INACTIVE) {
360                     if (nc->shape_editor->hits_curve() && nc->shape_editor->has_nodepath()) {
361                         nc->current_state = SP_NODE_CONTEXT_NODE_DRAGGING;
362                     } else {
363                         nc->current_state = SP_NODE_CONTEXT_RUBBERBAND_DRAGGING;
364                     }
365                 }
367                 switch (nc->current_state) {
368                     case SP_NODE_CONTEXT_NODE_DRAGGING:
369                         {
370                             nc->shape_editor->curve_drag (event->motion.x, event->motion.y);
372                             gobble_motion_events(GDK_BUTTON1_MASK);
373                             break;
374                         }
375                     case SP_NODE_CONTEXT_RUBBERBAND_DRAGGING:
376                         if (Inkscape::Rubberband::get()->is_started()) {
377                             NR::Point const motion_w(event->motion.x,
378                                                 event->motion.y);
379                             NR::Point const motion_dt(desktop->w2d(motion_w));
380                             Inkscape::Rubberband::get()->move(motion_dt);
381                         }
382                         break;
383                 }
385                 nc->drag = TRUE;
386                 ret = TRUE;
387             } else {
388                 if (!nc->shape_editor->has_nodepath() || selection->singleItem() == NULL) {
389                     break;
390                 }
392                 SPItem *item_over = sp_event_context_over_item (desktop, selection->singleItem(),
393                                                                 NR::Point(event->motion.x, event->motion.y));
394                 bool over_stroke = false;
395                 if (item_over && nc->shape_editor->has_nodepath()) {
396                     over_stroke = nc->shape_editor->is_over_stroke(NR::Point(event->motion.x, event->motion.y), false);
397                 }
399                 if (nc->cursor_drag && !over_stroke) {
400                     event_context->cursor_shape = cursor_node_xpm;
401                     event_context->hot_x = 1;
402                     event_context->hot_y = 1;
403                     sp_event_context_update_cursor(event_context);
404                     nc->cursor_drag = false;
405                 } else if (!nc->cursor_drag && over_stroke) {
406                     event_context->cursor_shape = cursor_node_d_xpm;
407                     event_context->hot_x = 1;
408                     event_context->hot_y = 1;
409                     sp_event_context_update_cursor(event_context);
410                     nc->cursor_drag = true;
411                 }
412             }
413             break;
414         case GDK_BUTTON_RELEASE:
415             event_context->xp = event_context->yp = 0;
416             if (event->button.button == 1) {
418                 NR::Maybe<NR::Rect> b = Inkscape::Rubberband::get()->getRectangle();
420                 if (nc->shape_editor->hits_curve() && !event_context->within_tolerance) { //drag curve
421                     nc->shape_editor->finish_drag();
422                 } else if (b && !event_context->within_tolerance) { // drag to select
423                     nc->shape_editor->select_rect(*b, event->button.state & GDK_SHIFT_MASK);
424                 } else {
425                     if (!(nc->rb_escaped)) { // unless something was cancelled
426                         if (nc->shape_editor->has_selection())
427                             nc->shape_editor->deselect();
428                         else
429                             sp_desktop_selection(desktop)->clear();
430                     }
431                 }
432                 ret = TRUE;
433                 Inkscape::Rubberband::get()->stop();
434                 desktop->updateNow();
435                 nc->rb_escaped = false;
436                 nc->drag = FALSE;
437                 nc->shape_editor->cancel_hit();
438                 nc->current_state = SP_NODE_CONTEXT_INACTIVE;
439                 break;
440             }
441             break;
442         case GDK_KEY_PRESS:
443             switch (get_group0_keyval(&event->key)) {
444                 case GDK_Insert:
445                 case GDK_KP_Insert:
446                     // with any modifiers
447                     nc->shape_editor->add_node();
448                     ret = TRUE;
449                     break;
450                 case GDK_Delete:
451                 case GDK_KP_Delete:
452                 case GDK_BackSpace:
453                     if (MOD__CTRL_ONLY) {
454                         nc->shape_editor->delete_nodes();
455                     } else {
456                         nc->shape_editor->delete_nodes_preserving_shape();
457                     }
458                     ret = TRUE;
459                     break;
460                 case GDK_C:
461                 case GDK_c:
462                     if (MOD__SHIFT_ONLY) {
463                         nc->shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
464                         ret = TRUE;
465                     }
466                     break;
467                 case GDK_S:
468                 case GDK_s:
469                     if (MOD__SHIFT_ONLY) {
470                         nc->shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
471                         ret = TRUE;
472                     }
473                     break;
474                 case GDK_Y:
475                 case GDK_y:
476                     if (MOD__SHIFT_ONLY) {
477                         nc->shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
478                         ret = TRUE;
479                     }
480                     break;
481                 case GDK_B:
482                 case GDK_b:
483                     if (MOD__SHIFT_ONLY) {
484                         nc->shape_editor->break_at_nodes();
485                         ret = TRUE;
486                     }
487                     break;
488                 case GDK_J:
489                 case GDK_j:
490                     if (MOD__SHIFT_ONLY) {
491                         nc->shape_editor->join_nodes();
492                         ret = TRUE;
493                     }
494                     break;
495                 case GDK_D:
496                 case GDK_d:
497                     if (MOD__SHIFT_ONLY) {
498                         nc->shape_editor->duplicate_nodes();
499                         ret = TRUE;
500                     }
501                     break;
502                 case GDK_L:
503                 case GDK_l:
504                     if (MOD__SHIFT_ONLY) {
505                         nc->shape_editor->set_type_of_segments(NR_LINETO);
506                         ret = TRUE;
507                     }
508                     break;
509                 case GDK_U:
510                 case GDK_u:
511                     if (MOD__SHIFT_ONLY) {
512                         nc->shape_editor->set_type_of_segments(NR_CURVETO);
513                         ret = TRUE;
514                     }
515                     break;
516                 case GDK_R:
517                 case GDK_r:
518                     if (MOD__SHIFT_ONLY) {
519                         // FIXME: add top panel button
520                         sp_selected_path_reverse();
521                         ret = TRUE;
522                     }
523                     break;
524                 case GDK_Left: // move selection left
525                 case GDK_KP_Left:
526                 case GDK_KP_4:
527                     if (!MOD__CTRL) { // not ctrl
528                         if (MOD__ALT) { // alt
529                             if (MOD__SHIFT) nc->shape_editor->move_nodes_screen(-10, 0); // shift
530                             else nc->shape_editor->move_nodes_screen(-1, 0); // no shift
531                         }
532                         else { // no alt
533                             if (MOD__SHIFT) nc->shape_editor->move_nodes(-10*nudge, 0); // shift
534                             else nc->shape_editor->move_nodes(-nudge, 0); // no shift
535                         }
536                         ret = TRUE;
537                     }
538                     break;
539                 case GDK_Up: // move selection up
540                 case GDK_KP_Up:
541                 case GDK_KP_8:
542                     if (!MOD__CTRL) { // not ctrl
543                         if (MOD__ALT) { // alt
544                             if (MOD__SHIFT) nc->shape_editor->move_nodes_screen(0, 10); // shift
545                             else nc->shape_editor->move_nodes_screen(0, 1); // no shift
546                         }
547                         else { // no alt
548                             if (MOD__SHIFT) nc->shape_editor->move_nodes(0, 10*nudge); // shift
549                             else nc->shape_editor->move_nodes(0, nudge); // no shift
550                         }
551                         ret = TRUE;
552                     }
553                     break;
554                 case GDK_Right: // move selection right
555                 case GDK_KP_Right:
556                 case GDK_KP_6:
557                     if (!MOD__CTRL) { // not ctrl
558                         if (MOD__ALT) { // alt
559                             if (MOD__SHIFT) nc->shape_editor->move_nodes_screen(10, 0); // shift
560                             else nc->shape_editor->move_nodes_screen(1, 0); // no shift
561                         }
562                         else { // no alt
563                             if (MOD__SHIFT) nc->shape_editor->move_nodes(10*nudge, 0); // shift
564                             else nc->shape_editor->move_nodes(nudge, 0); // no shift
565                         }
566                         ret = TRUE;
567                     }
568                     break;
569                 case GDK_Down: // move selection down
570                 case GDK_KP_Down:
571                 case GDK_KP_2:
572                     if (!MOD__CTRL) { // not ctrl
573                         if (MOD__ALT) { // alt
574                             if (MOD__SHIFT) nc->shape_editor->move_nodes_screen(0, -10); // shift
575                             else nc->shape_editor->move_nodes_screen(0, -1); // no shift
576                         }
577                         else { // no alt
578                             if (MOD__SHIFT) nc->shape_editor->move_nodes(0, -10*nudge); // shift
579                             else nc->shape_editor->move_nodes(0, -nudge); // no shift
580                         }
581                         ret = TRUE;
582                     }
583                     break;
584                 case GDK_Escape:
585                 {
586                     NR::Maybe<NR::Rect> const b = Inkscape::Rubberband::get()->getRectangle();
587                     if (b) {
588                         Inkscape::Rubberband::get()->stop();
589                         nc->current_state = SP_NODE_CONTEXT_INACTIVE;
590                         nc->rb_escaped = true;
591                     } else {
592                         if (nc->shape_editor->has_selection()) {
593                             nc->shape_editor->deselect();
594                         } else {
595                             sp_desktop_selection(desktop)->clear();
596                         }
597                     }
598                     ret = TRUE;
599                     break;
600                 }
602                 case GDK_bracketleft:
603                     if ( MOD__CTRL && !MOD__ALT && ( snaps != 0 ) ) {
604                         if (nc->leftctrl)
605                             nc->shape_editor->rotate_nodes (M_PI/snaps, -1, false);
606                         if (nc->rightctrl)
607                             nc->shape_editor->rotate_nodes (M_PI/snaps, 1, false);
608                     } else if ( MOD__ALT && !MOD__CTRL ) {
609                         if (nc->leftalt && nc->rightalt)
610                             nc->shape_editor->rotate_nodes (1, 0, true);
611                         else {
612                             if (nc->leftalt)
613                                 nc->shape_editor->rotate_nodes (1, -1, true);
614                             if (nc->rightalt)
615                                 nc->shape_editor->rotate_nodes (1, 1, true);
616                         }
617                     } else if ( snaps != 0 ) {
618                         nc->shape_editor->rotate_nodes (M_PI/snaps, 0, false);
619                     }
620                     ret = TRUE;
621                     break;
622                 case GDK_bracketright:
623                     if ( MOD__CTRL && !MOD__ALT && ( snaps != 0 ) ) {
624                         if (nc->leftctrl)
625                             nc->shape_editor->rotate_nodes (-M_PI/snaps, -1, false);
626                         if (nc->rightctrl)
627                             nc->shape_editor->rotate_nodes (-M_PI/snaps, 1, false);
628                     } else if ( MOD__ALT && !MOD__CTRL ) {
629                         if (nc->leftalt && nc->rightalt)
630                             nc->shape_editor->rotate_nodes (-1, 0, true);
631                         else {
632                             if (nc->leftalt)
633                                 nc->shape_editor->rotate_nodes (-1, -1, true);
634                             if (nc->rightalt)
635                                 nc->shape_editor->rotate_nodes (-1, 1, true);
636                         }
637                     } else if ( snaps != 0 ) {
638                         nc->shape_editor->rotate_nodes (-M_PI/snaps, 0, false);
639                     }
640                     ret = TRUE;
641                     break;
642                 case GDK_less:
643                 case GDK_comma:
644                     if (MOD__CTRL) {
645                         if (nc->leftctrl)
646                             nc->shape_editor->scale_nodes(-offset, -1);
647                         if (nc->rightctrl)
648                             nc->shape_editor->scale_nodes(-offset, 1);
649                     } else if (MOD__ALT) {
650                         if (nc->leftalt && nc->rightalt)
651                             nc->shape_editor->scale_nodes_screen (-1, 0);
652                         else {
653                             if (nc->leftalt)
654                                 nc->shape_editor->scale_nodes_screen (-1, -1);
655                             if (nc->rightalt)
656                                 nc->shape_editor->scale_nodes_screen (-1, 1);
657                         }
658                     } else {
659                         nc->shape_editor->scale_nodes (-offset, 0);
660                     }
661                     ret = TRUE;
662                     break;
663                 case GDK_greater:
664                 case GDK_period:
665                     if (MOD__CTRL) {
666                         if (nc->leftctrl)
667                             nc->shape_editor->scale_nodes (offset, -1);
668                         if (nc->rightctrl)
669                             nc->shape_editor->scale_nodes (offset, 1);
670                     } else if (MOD__ALT) {
671                         if (nc->leftalt && nc->rightalt)
672                             nc->shape_editor->scale_nodes_screen (1, 0);
673                         else {
674                             if (nc->leftalt)
675                                 nc->shape_editor->scale_nodes_screen (1, -1);
676                             if (nc->rightalt)
677                                 nc->shape_editor->scale_nodes_screen (1, 1);
678                         }
679                     } else {
680                         nc->shape_editor->scale_nodes (offset, 0);
681                     }
682                     ret = TRUE;
683                     break;
685                 case GDK_Alt_L:
686                     nc->leftalt = TRUE;
687                     sp_node_context_show_modifier_tip(event_context, event);
688                     break;
689                 case GDK_Alt_R:
690                     nc->rightalt = TRUE;
691                     sp_node_context_show_modifier_tip(event_context, event);
692                     break;
693                 case GDK_Control_L:
694                     nc->leftctrl = TRUE;
695                     sp_node_context_show_modifier_tip(event_context, event);
696                     break;
697                 case GDK_Control_R:
698                     nc->rightctrl = TRUE;
699                     sp_node_context_show_modifier_tip(event_context, event);
700                     break;
701                 case GDK_Shift_L:
702                 case GDK_Shift_R:
703                 case GDK_Meta_L:
704                 case GDK_Meta_R:
705                     sp_node_context_show_modifier_tip(event_context, event);
706                     break;
707                 default:
708                     ret = node_key(event);
709                     break;
710             }
711             break;
712         case GDK_KEY_RELEASE:
713             switch (get_group0_keyval(&event->key)) {
714                 case GDK_Alt_L:
715                     nc->leftalt = FALSE;
716                     event_context->defaultMessageContext()->clear();
717                     break;
718                 case GDK_Alt_R:
719                     nc->rightalt = FALSE;
720                     event_context->defaultMessageContext()->clear();
721                     break;
722                 case GDK_Control_L:
723                     nc->leftctrl = FALSE;
724                     event_context->defaultMessageContext()->clear();
725                     break;
726                 case GDK_Control_R:
727                     nc->rightctrl = FALSE;
728                     event_context->defaultMessageContext()->clear();
729                     break;
730                 case GDK_Shift_L:
731                 case GDK_Shift_R:
732                 case GDK_Meta_L:
733                 case GDK_Meta_R:
734                     event_context->defaultMessageContext()->clear();
735                     break;
736             }
737             break;
738         default:
739             break;
740     }
742     if (!ret) {
743         if (((SPEventContextClass *) parent_class)->root_handler)
744             ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event);
745     }
747     return ret;
751 /*
752   Local Variables:
753   mode:c++
754   c-file-style:"stroustrup"
755   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
756   indent-tabs-mode:nil
757   fill-column:99
758   End:
759 */
760 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :