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;
99 new (&node_context->sel_changed_connection) sigc::connection();
100 }
102 static void
103 sp_node_context_dispose(GObject *object)
104 {
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);
120 }
122 static void
123 sp_node_context_setup(SPEventContext *ec)
124 {
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();
163 }
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)
171 {
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();
180 }
182 void
183 sp_node_context_show_modifier_tip(SPEventContext *event_context, GdkEvent *event)
184 {
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"));
190 }
193 static gint
194 sp_node_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event)
195 {
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;
202 }
204 static gint
205 sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
206 {
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.
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;
728 }
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 :