Code

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