Code

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