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 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();
161 }
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)
169 {
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();
178 }
180 void
181 sp_node_context_show_modifier_tip(SPEventContext *event_context, GdkEvent *event)
182 {
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"));
188 }
191 static gint
192 sp_node_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event)
193 {
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;
200 }
202 static gint
203 sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
204 {
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.
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;
726 }
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 :