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;
97 new (&node_context->sel_changed_connection) sigc::connection();
98 }
100 static void
101 sp_node_context_dispose(GObject *object)
102 {
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);
118 }
120 static void
121 sp_node_context_setup(SPEventContext *ec)
122 {
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();
159 }
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)
167 {
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();
176 }
178 void
179 sp_node_context_show_modifier_tip(SPEventContext *event_context, GdkEvent *event)
180 {
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"));
186 }
189 static gint
190 sp_node_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event)
191 {
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;
301 }
303 static gint
304 sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
305 {
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.
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;
748 }
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 :