f213b58aaa88869cc1c87d6bf4e7cce81ad88d6b
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, except stamping code,
11 * which is Copyright (C) Masatake Yamato 2002
12 */
14 #ifdef HAVE_CONFIG_H
15 # include "config.h"
16 #endif
17 #include <gdk/gdkkeysyms.h>
18 #include "macros.h"
19 #include <glibmm/i18n.h>
20 #include "display/sp-canvas-util.h"
21 #include "object-edit.h"
22 #include "sp-path.h"
23 #include "path-chemistry.h"
24 #include "rubberband.h"
25 #include "desktop.h"
26 #include "desktop-handles.h"
27 #include "selection.h"
28 #include "pixmaps/cursor-node.xpm"
29 #include "pixmaps/cursor-node.pixbuf"
30 #include "message-context.h"
31 #include "node-context.h"
32 #include "pixmaps/cursor-node-d.xpm"
33 #include "pixmaps/cursor-node-d.pixbuf"
34 #include "prefs-utils.h"
35 #include "xml/node-event-vector.h"
36 #include "style.h"
37 #include "splivarot.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 void nodepath_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
49 gchar const *old_value, gchar const *new_value,
50 bool is_interactive, gpointer data);
52 static Inkscape::XML::NodeEventVector nodepath_repr_events = {
53 NULL, /* child_added */
54 NULL, /* child_removed */
55 nodepath_event_attr_changed,
56 NULL, /* content_changed */
57 NULL /* order_changed */
58 };
60 static SPEventContextClass *parent_class;
62 GType
63 sp_node_context_get_type()
64 {
65 static GType type = 0;
66 if (!type) {
67 GTypeInfo info = {
68 sizeof(SPNodeContextClass),
69 NULL, NULL,
70 (GClassInitFunc) sp_node_context_class_init,
71 NULL, NULL,
72 sizeof(SPNodeContext),
73 4,
74 (GInstanceInitFunc) sp_node_context_init,
75 NULL, /* value_table */
76 };
77 type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "SPNodeContext", &info, (GTypeFlags)0);
78 }
79 return type;
80 }
82 static void
83 sp_node_context_class_init(SPNodeContextClass *klass)
84 {
85 GObjectClass *object_class = (GObjectClass *) klass;
86 SPEventContextClass *event_context_class = (SPEventContextClass *) klass;
88 parent_class = (SPEventContextClass*)g_type_class_peek_parent(klass);
90 object_class->dispose = sp_node_context_dispose;
92 event_context_class->setup = sp_node_context_setup;
93 event_context_class->root_handler = sp_node_context_root_handler;
94 event_context_class->item_handler = sp_node_context_item_handler;
95 }
97 static void
98 sp_node_context_init(SPNodeContext *node_context)
99 {
100 SPEventContext *event_context = SP_EVENT_CONTEXT(node_context);
102 event_context->cursor_shape = cursor_node_xpm;
103 event_context->cursor_pixbuf = gdk_pixbuf_new_from_inline(
104 -1,
105 cursor_node_pixbuf,
106 FALSE,
107 NULL);
108 event_context->hot_x = 1;
109 event_context->hot_y = 1;
111 node_context->leftalt = FALSE;
112 node_context->rightalt = FALSE;
113 node_context->leftctrl = FALSE;
114 node_context->rightctrl = FALSE;
116 new (&node_context->sel_changed_connection) sigc::connection();
117 }
119 static void
120 sp_node_context_dispose(GObject *object)
121 {
122 SPNodeContext *nc = SP_NODE_CONTEXT(object);
123 SPEventContext *ec = SP_EVENT_CONTEXT(object);
125 ec->enableGrDrag(false);
127 nc->sel_changed_connection.disconnect();
128 nc->sel_changed_connection.~connection();
130 Inkscape::XML::Node *repr = NULL;
131 if (nc->nodepath) {
132 repr = nc->nodepath->repr;
133 }
134 if (!repr && ec->shape_knot_holder) {
135 repr = ec->shape_knot_holder->repr;
136 }
138 if (repr) {
139 sp_repr_remove_listener_by_data(repr, ec);
140 Inkscape::GC::release(repr);
141 }
143 if (nc->nodepath) {
144 sp_nodepath_destroy(nc->nodepath);
145 nc->nodepath = NULL;
146 }
148 if (ec->shape_knot_holder) {
149 sp_knot_holder_destroy(ec->shape_knot_holder);
150 ec->shape_knot_holder = NULL;
151 }
153 if (nc->_node_message_context) {
154 delete nc->_node_message_context;
155 }
157 G_OBJECT_CLASS(parent_class)->dispose(object);
158 }
160 static void
161 sp_node_context_setup(SPEventContext *ec)
162 {
163 SPNodeContext *nc = SP_NODE_CONTEXT(ec);
165 if (((SPEventContextClass *) parent_class)->setup)
166 ((SPEventContextClass *) parent_class)->setup(ec);
168 nc->sel_changed_connection.disconnect();
169 nc->sel_changed_connection = sp_desktop_selection(ec->desktop)->connectChanged(sigc::bind(sigc::ptr_fun(&sp_node_context_selection_changed), (gpointer)nc));
171 Inkscape::Selection *selection = sp_desktop_selection(ec->desktop);
172 SPItem *item = selection->singleItem();
174 nc->nodepath = NULL;
175 ec->shape_knot_holder = NULL;
177 nc->rb_escaped = false;
179 nc->cursor_drag = false;
181 nc->added_node = false;
183 if (item) {
184 nc->nodepath = sp_nodepath_new(ec->desktop, item, (prefs_get_int_attribute("tools.nodes", "show_handles", 1) != 0));
185 if ( nc->nodepath) {
186 //point pack to parent in case nodepath is deleted
187 nc->nodepath->nodeContext = nc;
188 }
189 ec->shape_knot_holder = sp_item_knot_holder(item, ec->desktop);
191 if (nc->nodepath || ec->shape_knot_holder) {
192 // setting listener
193 Inkscape::XML::Node *repr;
194 if (ec->shape_knot_holder)
195 repr = ec->shape_knot_holder->repr;
196 else
197 repr = SP_OBJECT_REPR(item);
198 if (repr) {
199 Inkscape::GC::anchor(repr);
200 sp_repr_add_listener(repr, &nodepath_repr_events, ec);
201 }
202 }
203 }
205 if (prefs_get_int_attribute("tools.nodes", "selcue", 0) != 0) {
206 ec->enableSelectionCue();
207 }
209 if (prefs_get_int_attribute("tools.nodes", "gradientdrag", 0) != 0) {
210 ec->enableGrDrag();
211 }
213 nc->_node_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack());
214 sp_nodepath_update_statusbar(nc->nodepath);
215 }
217 /**
218 \brief Callback that processes the "changed" signal on the selection;
219 destroys old and creates new nodepath and reassigns listeners to the new selected item's repr
220 */
221 void
222 sp_node_context_selection_changed(Inkscape::Selection *selection, gpointer data)
223 {
224 SPNodeContext *nc = SP_NODE_CONTEXT(data);
225 SPEventContext *ec = SP_EVENT_CONTEXT(nc);
227 Inkscape::XML::Node *old_repr = NULL;
229 if (nc->nodepath) {
230 old_repr = nc->nodepath->repr;
231 sp_nodepath_destroy(nc->nodepath);
232 nc->nodepath = NULL;
233 }
235 if (ec->shape_knot_holder) {
236 old_repr = ec->shape_knot_holder->repr;
237 sp_knot_holder_destroy(ec->shape_knot_holder);
238 }
240 if (old_repr) { // remove old listener
241 sp_repr_remove_listener_by_data(old_repr, ec);
242 Inkscape::GC::release(old_repr);
243 }
245 SPItem *item = selection->singleItem();
247 SPDesktop *desktop = selection->desktop();
248 nc->nodepath = NULL;
249 ec->shape_knot_holder = NULL;
250 if (item) {
251 nc->nodepath = sp_nodepath_new(desktop, item, (prefs_get_int_attribute("tools.nodes", "show_handles", 1) != 0));
252 if (nc->nodepath) {
253 nc->nodepath->nodeContext = nc;
254 }
255 ec->shape_knot_holder = sp_item_knot_holder(item, desktop);
257 if (nc->nodepath || ec->shape_knot_holder) {
258 // setting new listener
259 Inkscape::XML::Node *repr;
260 if (ec->shape_knot_holder)
261 repr = ec->shape_knot_holder->repr;
262 else
263 repr = SP_OBJECT_REPR(item);
264 if (repr) {
265 Inkscape::GC::anchor(repr);
266 sp_repr_add_listener(repr, &nodepath_repr_events, ec);
267 }
268 }
269 }
270 sp_nodepath_update_statusbar(nc->nodepath);
271 }
273 /**
274 \brief Regenerates nodepath when the item's repr was change outside of node edit
275 (e.g. by undo, or xml editor, or edited in another view). The item is assumed to be the same
276 (otherwise sp_node_context_selection_changed() would have been called), so repr and listeners
277 are not changed.
278 */
279 void
280 sp_nodepath_update_from_item(SPNodeContext *nc, SPItem *item)
281 {
282 g_assert(nc);
283 SPEventContext *ec = ((SPEventContext *) nc);
285 SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(SP_EVENT_CONTEXT(nc));
286 g_assert(desktop);
288 if (nc->nodepath) {
289 sp_nodepath_destroy(nc->nodepath);
290 nc->nodepath = NULL;
291 }
293 if (ec->shape_knot_holder) {
294 sp_knot_holder_destroy(ec->shape_knot_holder);
295 ec->shape_knot_holder = NULL;
296 }
298 Inkscape::Selection *selection = sp_desktop_selection(desktop);
299 item = selection->singleItem();
301 if (item) {
302 nc->nodepath = sp_nodepath_new(desktop, item, (prefs_get_int_attribute("tools.nodes", "show_handles", 1) != 0));
303 if (nc->nodepath) {
304 nc->nodepath->nodeContext = nc;
305 }
306 ec->shape_knot_holder = sp_item_knot_holder(item, desktop);
307 }
308 sp_nodepath_update_statusbar(nc->nodepath);
309 }
311 /**
312 \brief Callback that is fired whenever an attribute of the selected item (which we have in the nodepath) changes
313 */
314 static void
315 nodepath_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
316 gchar const *old_value, gchar const *new_value,
317 bool is_interactive, gpointer data)
318 {
319 SPItem *item = NULL;
320 gboolean changed = FALSE;
322 g_assert(data);
323 SPNodeContext *nc = ((SPNodeContext *) data);
324 SPEventContext *ec = ((SPEventContext *) data);
325 g_assert(nc);
326 Inkscape::NodePath::Path *np = nc->nodepath;
327 SPKnotHolder *kh = ec->shape_knot_holder;
329 if (np) {
330 item = SP_ITEM(np->path);
331 if (!strcmp(name, "d") || !strcmp(name, "sodipodi:nodetypes")) { // With paths, we only need to act if one of the path-affecting attributes has changed.
332 changed = (np->local_change == 0);
333 if (np->local_change > 0)
334 np->local_change--;
335 }
337 } else if (kh) {
338 item = SP_ITEM(kh->item);
339 changed = !(kh->local_change);
340 kh->local_change = FALSE;
341 }
343 if (np && changed) {
344 GList *saved = NULL;
345 SPDesktop *desktop = np->desktop;
346 g_assert(desktop);
347 Inkscape::Selection *selection = desktop->selection;
348 g_assert(selection);
350 saved = save_nodepath_selection(nc->nodepath);
351 sp_nodepath_update_from_item(nc, item);
352 if (nc->nodepath && saved) restore_nodepath_selection(nc->nodepath, saved);
354 } else if (kh && changed) {
355 sp_nodepath_update_from_item(nc, item);
356 }
358 sp_nodepath_update_statusbar(nc->nodepath);
359 }
361 void
362 sp_node_context_show_modifier_tip(SPEventContext *event_context, GdkEvent *event)
363 {
364 sp_event_show_modifier_tip
365 (event_context->defaultMessageContext(), event,
366 _("<b>Ctrl</b>: toggle node type, snap handle angle, move hor/vert; <b>Ctrl+Alt</b>: move along handles"),
367 _("<b>Shift</b>: toggle node selection, disable snapping, rotate both handles"),
368 _("<b>Alt</b>: lock handle length; <b>Ctrl+Alt</b>: move along handles"));
369 }
371 bool
372 sp_node_context_is_over_stroke (SPNodeContext *nc, SPItem *item, NR::Point event_p, bool remember)
373 {
374 SPDesktop *desktop = SP_EVENT_CONTEXT (nc)->desktop;
376 //Translate click point into proper coord system
377 nc->curvepoint_doc = desktop->w2d(event_p);
378 nc->curvepoint_doc *= sp_item_dt2i_affine(item);
379 nc->curvepoint_doc *= sp_item_i2doc_affine(item);
381 NR::Maybe<Path::cut_position> position = get_nearest_position_on_Path(nc->nodepath->livarot_path, nc->curvepoint_doc);
382 NR::Point nearest = get_point_on_Path(nc->nodepath->livarot_path, position.assume().piece, position.assume().t);
383 NR::Point delta = nearest - nc->curvepoint_doc;
385 delta = desktop->d2w(delta);
387 double stroke_tolerance =
388 (SP_OBJECT_STYLE (item)->stroke.type != SP_PAINT_TYPE_NONE?
389 desktop->current_zoom() *
390 SP_OBJECT_STYLE (item)->stroke_width.computed *
391 sp_item_i2d_affine (item).expansion() * 0.5
392 : 0.0)
393 + (double) SP_EVENT_CONTEXT(nc)->tolerance;
395 bool close = (NR::L2 (delta) < stroke_tolerance);
397 if (remember && close) {
398 nc->curvepoint_event[NR::X] = (gint) event_p [NR::X];
399 nc->curvepoint_event[NR::Y] = (gint) event_p [NR::Y];
400 nc->hit = true;
401 nc->grab_t = position.assume().t;
402 nc->grab_node = sp_nodepath_get_node_by_index(position.assume().piece);
403 }
405 return close;
406 }
409 static gint
410 sp_node_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event)
411 {
412 gint ret = FALSE;
414 SPDesktop *desktop = event_context->desktop;
415 Inkscape::Selection *selection = sp_desktop_selection (desktop);
417 SPNodeContext *nc = SP_NODE_CONTEXT(event_context);
419 switch (event->type) {
420 case GDK_2BUTTON_PRESS:
421 case GDK_BUTTON_RELEASE:
422 if (event->button.button == 1) {
423 if (!nc->drag) {
425 // find out clicked item, disregarding groups, honoring Alt
426 SPItem *item_clicked = sp_event_context_find_item (desktop,
427 NR::Point(event->button.x, event->button.y),
428 (event->button.state & GDK_MOD1_MASK) && !(event->button.state & GDK_CONTROL_MASK), TRUE);
429 // find out if we're over the selected item, disregarding groups
430 SPItem *item_over = sp_event_context_over_item (desktop, selection->singleItem(),
431 NR::Point(event->button.x, event->button.y));
433 bool over_stroke = false;
434 if (item_over && nc->nodepath) {
435 over_stroke = sp_node_context_is_over_stroke (nc, item_over, NR::Point(event->button.x, event->button.y), false);
436 }
438 if (over_stroke || nc->added_node) {
439 switch (event->type) {
440 case GDK_BUTTON_RELEASE:
441 if (event->button.state & GDK_CONTROL_MASK && event->button.state & GDK_MOD1_MASK) {
442 //add a node
443 sp_nodepath_add_node_near_point(nc->nodepath, nc->curvepoint_doc);
444 } else {
445 if (nc->added_node) { // we just received double click, ignore release
446 nc->added_node = false;
447 break;
448 }
449 //select the segment
450 if (event->button.state & GDK_SHIFT_MASK) {
451 sp_nodepath_select_segment_near_point(nc->nodepath, nc->curvepoint_doc, true);
452 } else {
453 sp_nodepath_select_segment_near_point(nc->nodepath, nc->curvepoint_doc, false);
454 }
455 desktop->updateNow();
456 }
457 break;
458 case GDK_2BUTTON_PRESS:
459 //add a node
460 sp_nodepath_add_node_near_point(nc->nodepath, nc->curvepoint_doc);
461 nc->added_node = true;
462 break;
463 default:
464 break;
465 }
466 } else if (event->button.state & GDK_SHIFT_MASK) {
467 selection->toggle(item_clicked);
468 desktop->updateNow();
469 } else {
470 selection->set(item_clicked);
471 desktop->updateNow();
472 }
474 ret = TRUE;
475 }
476 break;
477 }
478 break;
479 case GDK_BUTTON_PRESS:
480 if (event->button.button == 1 && !(event->button.state & GDK_SHIFT_MASK)) {
481 // save drag origin
482 event_context->xp = (gint) event->button.x;
483 event_context->yp = (gint) event->button.y;
484 event_context->within_tolerance = true;
485 nc->hit = false;
487 if (!nc->drag) {
488 // find out if we're over the selected item, disregarding groups
489 SPItem *item_over = sp_event_context_over_item (desktop, selection->singleItem(),
490 NR::Point(event->button.x, event->button.y));
492 if (nc->nodepath && selection->single() && item_over) {
494 // save drag origin
495 bool over_stroke = sp_node_context_is_over_stroke (nc, item_over, NR::Point(event->button.x, event->button.y), true);
496 //only dragging curves
497 if (over_stroke) {
498 sp_nodepath_select_segment_near_point(nc->nodepath, nc->curvepoint_doc, false);
499 ret = TRUE;
500 } else {
501 break;
502 }
503 } else {
504 break;
505 }
507 ret = TRUE;
508 }
509 break;
510 }
511 break;
512 default:
513 break;
514 }
516 if (!ret) {
517 if (((SPEventContextClass *) parent_class)->item_handler)
518 ret = ((SPEventContextClass *) parent_class)->item_handler(event_context, item, event);
519 }
521 return ret;
522 }
524 static gint
525 sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event)
526 {
527 SPDesktop *desktop = event_context->desktop;
528 Inkscape::Selection *selection = sp_desktop_selection (desktop);
530 SPNodeContext *nc = SP_NODE_CONTEXT(event_context);
531 double const nudge = prefs_get_double_attribute_limited("options.nudgedistance", "value", 2, 0, 1000); // in px
532 event_context->tolerance = prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100); // read every time, to make prefs changes really live
533 int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
534 double const offset = prefs_get_double_attribute_limited("options.defaultscale", "value", 2, 0, 1000);
536 gint ret = FALSE;
538 switch (event->type) {
539 case GDK_BUTTON_PRESS:
540 if (event->button.button == 1) {
541 // save drag origin
542 event_context->xp = (gint) event->button.x;
543 event_context->yp = (gint) event->button.y;
544 event_context->within_tolerance = true;
545 nc->hit = false;
547 NR::Point const button_w(event->button.x,
548 event->button.y);
549 NR::Point const button_dt(desktop->w2d(button_w));
550 Inkscape::Rubberband::get()->start(desktop, button_dt);
551 desktop->updateNow();
552 ret = TRUE;
553 }
554 break;
555 case GDK_MOTION_NOTIFY:
556 if (event->motion.state & GDK_BUTTON1_MASK) {
558 if ( event_context->within_tolerance
559 && ( abs( (gint) event->motion.x - event_context->xp ) < event_context->tolerance )
560 && ( abs( (gint) event->motion.y - event_context->yp ) < event_context->tolerance ) ) {
561 break; // do not drag if we're within tolerance from origin
562 }
563 // Once the user has moved farther than tolerance from the original location
564 // (indicating they intend to move the object, not click), then always process the
565 // motion notify coordinates as given (no snapping back to origin)
566 event_context->within_tolerance = false;
568 if (nc->nodepath && nc->hit) {
569 NR::Point const delta_w(event->motion.x - nc->curvepoint_event[NR::X],
570 event->motion.y - nc->curvepoint_event[NR::Y]);
571 NR::Point const delta_dt(desktop->w2d(delta_w));
572 sp_nodepath_curve_drag (nc->grab_node, nc->grab_t, delta_dt);
573 nc->curvepoint_event[NR::X] = (gint) event->motion.x;
574 nc->curvepoint_event[NR::Y] = (gint) event->motion.y;
575 gobble_motion_events(GDK_BUTTON1_MASK);
576 } else {
577 NR::Point const motion_w(event->motion.x,
578 event->motion.y);
579 NR::Point const motion_dt(desktop->w2d(motion_w));
580 Inkscape::Rubberband::get()->move(motion_dt);
581 }
582 nc->drag = TRUE;
583 ret = TRUE;
584 } else {
585 if (!nc->nodepath || selection->singleItem() == NULL) {
586 break;
587 }
589 SPItem *item_over = sp_event_context_over_item (desktop, selection->singleItem(),
590 NR::Point(event->motion.x, event->motion.y));
591 bool over_stroke = false;
592 if (item_over && nc->nodepath) {
593 over_stroke = sp_node_context_is_over_stroke (nc, item_over, NR::Point(event->motion.x, event->motion.y), false);
594 }
596 if (nc->cursor_drag && !over_stroke) {
597 event_context->cursor_shape = cursor_node_xpm;
598 event_context->cursor_pixbuf = gdk_pixbuf_new_from_inline(
599 -1,
600 cursor_node_pixbuf,
601 FALSE,
602 NULL);
603 event_context->hot_x = 1;
604 event_context->hot_y = 1;
605 sp_event_context_update_cursor(event_context);
606 nc->cursor_drag = false;
607 } else if (!nc->cursor_drag && over_stroke) {
608 event_context->cursor_shape = cursor_node_d_xpm;
609 event_context->cursor_pixbuf = gdk_pixbuf_new_from_inline(
610 -1,
611 cursor_node_d_pixbuf,
612 FALSE,
613 NULL);
614 event_context->hot_x = 1;
615 event_context->hot_y = 1;
616 sp_event_context_update_cursor(event_context);
617 nc->cursor_drag = true;
618 }
619 }
620 break;
621 case GDK_BUTTON_RELEASE:
622 event_context->xp = event_context->yp = 0;
623 if (event->button.button == 1) {
625 NR::Maybe<NR::Rect> b = Inkscape::Rubberband::get()->getRectangle();
627 if (nc->hit && !event_context->within_tolerance) { //drag curve
628 sp_nodepath_update_repr (nc->nodepath, _("Drag curve"));
629 } else if (b != NR::Nothing() && !event_context->within_tolerance) { // drag to select
630 if (nc->nodepath) {
631 sp_nodepath_select_rect(nc->nodepath, b.assume(), event->button.state & GDK_SHIFT_MASK);
632 }
633 } else {
634 if (!(nc->rb_escaped)) { // unless something was cancelled
635 if (nc->nodepath && nc->nodepath->selected)
636 sp_nodepath_deselect(nc->nodepath);
637 else
638 sp_desktop_selection(desktop)->clear();
639 }
640 }
641 ret = TRUE;
642 Inkscape::Rubberband::get()->stop();
643 desktop->updateNow();
644 nc->rb_escaped = false;
645 nc->drag = FALSE;
646 nc->hit = false;
647 break;
648 }
649 break;
650 case GDK_KEY_PRESS:
651 switch (get_group0_keyval(&event->key)) {
652 case GDK_Insert:
653 case GDK_KP_Insert:
654 // with any modifiers
655 sp_node_selected_add_node();
656 ret = TRUE;
657 break;
658 case GDK_Delete:
659 case GDK_KP_Delete:
660 case GDK_BackSpace:
661 if (MOD__CTRL_ONLY) {
662 sp_node_selected_delete();
663 } else {
664 if (nc->nodepath && nc->nodepath->selected) {
665 sp_node_delete_preserve(g_list_copy(nc->nodepath->selected));
666 }
667 }
668 ret = TRUE;
669 break;
670 case GDK_C:
671 case GDK_c:
672 if (MOD__SHIFT_ONLY) {
673 sp_node_selected_set_type(Inkscape::NodePath::NODE_CUSP);
674 ret = TRUE;
675 }
676 break;
677 case GDK_S:
678 case GDK_s:
679 if (MOD__SHIFT_ONLY) {
680 sp_node_selected_set_type(Inkscape::NodePath::NODE_SMOOTH);
681 ret = TRUE;
682 }
683 break;
684 case GDK_Y:
685 case GDK_y:
686 if (MOD__SHIFT_ONLY) {
687 sp_node_selected_set_type(Inkscape::NodePath::NODE_SYMM);
688 ret = TRUE;
689 }
690 break;
691 case GDK_B:
692 case GDK_b:
693 if (MOD__SHIFT_ONLY) {
694 sp_node_selected_break();
695 ret = TRUE;
696 }
697 break;
698 case GDK_J:
699 case GDK_j:
700 if (MOD__SHIFT_ONLY) {
701 sp_node_selected_join();
702 ret = TRUE;
703 }
704 break;
705 case GDK_D:
706 case GDK_d:
707 if (MOD__SHIFT_ONLY) {
708 sp_node_selected_duplicate();
709 ret = TRUE;
710 }
711 break;
712 case GDK_L:
713 case GDK_l:
714 if (MOD__SHIFT_ONLY) {
715 sp_node_selected_set_line_type(NR_LINETO);
716 ret = TRUE;
717 }
718 break;
719 case GDK_U:
720 case GDK_u:
721 if (MOD__SHIFT_ONLY) {
722 sp_node_selected_set_line_type(NR_CURVETO);
723 ret = TRUE;
724 }
725 break;
726 case GDK_R:
727 case GDK_r:
728 if (MOD__SHIFT_ONLY) {
729 // FIXME: add top panel button
730 sp_selected_path_reverse();
731 ret = TRUE;
732 }
733 break;
734 case GDK_Left: // move selection left
735 case GDK_KP_Left:
736 case GDK_KP_4:
737 if (!MOD__CTRL) { // not ctrl
738 if (MOD__ALT) { // alt
739 if (MOD__SHIFT) sp_node_selected_move_screen(-10, 0); // shift
740 else sp_node_selected_move_screen(-1, 0); // no shift
741 }
742 else { // no alt
743 if (MOD__SHIFT) sp_node_selected_move(-10*nudge, 0); // shift
744 else sp_node_selected_move(-nudge, 0); // no shift
745 }
746 ret = TRUE;
747 }
748 break;
749 case GDK_Up: // move selection up
750 case GDK_KP_Up:
751 case GDK_KP_8:
752 if (!MOD__CTRL) { // not ctrl
753 if (MOD__ALT) { // alt
754 if (MOD__SHIFT) sp_node_selected_move_screen(0, 10); // shift
755 else sp_node_selected_move_screen(0, 1); // no shift
756 }
757 else { // no alt
758 if (MOD__SHIFT) sp_node_selected_move(0, 10*nudge); // shift
759 else sp_node_selected_move(0, nudge); // no shift
760 }
761 ret = TRUE;
762 }
763 break;
764 case GDK_Right: // move selection right
765 case GDK_KP_Right:
766 case GDK_KP_6:
767 if (!MOD__CTRL) { // not ctrl
768 if (MOD__ALT) { // alt
769 if (MOD__SHIFT) sp_node_selected_move_screen(10, 0); // shift
770 else sp_node_selected_move_screen(1, 0); // no shift
771 }
772 else { // no alt
773 if (MOD__SHIFT) sp_node_selected_move(10*nudge, 0); // shift
774 else sp_node_selected_move(nudge, 0); // no shift
775 }
776 ret = TRUE;
777 }
778 break;
779 case GDK_Down: // move selection down
780 case GDK_KP_Down:
781 case GDK_KP_2:
782 if (!MOD__CTRL) { // not ctrl
783 if (MOD__ALT) { // alt
784 if (MOD__SHIFT) sp_node_selected_move_screen(0, -10); // shift
785 else sp_node_selected_move_screen(0, -1); // no shift
786 }
787 else { // no alt
788 if (MOD__SHIFT) sp_node_selected_move(0, -10*nudge); // shift
789 else sp_node_selected_move(0, -nudge); // no shift
790 }
791 ret = TRUE;
792 }
793 break;
794 case GDK_Tab: // Tab - cycle selection forward
795 if (!(MOD__CTRL_ONLY || (MOD__CTRL && MOD__SHIFT))) {
796 sp_nodepath_select_next(nc->nodepath);
797 ret = TRUE;
798 }
799 break;
800 case GDK_ISO_Left_Tab: // Shift Tab - cycle selection backward
801 if (!(MOD__CTRL_ONLY || (MOD__CTRL && MOD__SHIFT))) {
802 sp_nodepath_select_prev(nc->nodepath);
803 ret = TRUE;
804 }
805 break;
806 case GDK_Escape:
807 {
808 NR::Maybe<NR::Rect> const b = Inkscape::Rubberband::get()->getRectangle();
809 if (b != NR::Nothing()) {
810 Inkscape::Rubberband::get()->stop();
811 nc->rb_escaped = true;
812 } else {
813 if (nc->nodepath && nc->nodepath->selected) {
814 sp_nodepath_deselect(nc->nodepath);
815 } else {
816 sp_desktop_selection(desktop)->clear();
817 }
818 }
819 ret = TRUE;
820 break;
821 }
823 case GDK_bracketleft:
824 if ( MOD__CTRL && !MOD__ALT && ( snaps != 0 ) ) {
825 if (nc->leftctrl)
826 sp_nodepath_selected_nodes_rotate (nc->nodepath, M_PI/snaps, -1, false);
827 if (nc->rightctrl)
828 sp_nodepath_selected_nodes_rotate (nc->nodepath, M_PI/snaps, 1, false);
829 } else if ( MOD__ALT && !MOD__CTRL ) {
830 if (nc->leftalt && nc->rightalt)
831 sp_nodepath_selected_nodes_rotate (nc->nodepath, 1, 0, true);
832 else {
833 if (nc->leftalt)
834 sp_nodepath_selected_nodes_rotate (nc->nodepath, 1, -1, true);
835 if (nc->rightalt)
836 sp_nodepath_selected_nodes_rotate (nc->nodepath, 1, 1, true);
837 }
838 } else if ( snaps != 0 ) {
839 sp_nodepath_selected_nodes_rotate (nc->nodepath, M_PI/snaps, 0, false);
840 }
841 ret = TRUE;
842 break;
843 case GDK_bracketright:
844 if ( MOD__CTRL && !MOD__ALT && ( snaps != 0 ) ) {
845 if (nc->leftctrl)
846 sp_nodepath_selected_nodes_rotate (nc->nodepath, -M_PI/snaps, -1, false);
847 if (nc->rightctrl)
848 sp_nodepath_selected_nodes_rotate (nc->nodepath, -M_PI/snaps, 1, false);
849 } else if ( MOD__ALT && !MOD__CTRL ) {
850 if (nc->leftalt && nc->rightalt)
851 sp_nodepath_selected_nodes_rotate (nc->nodepath, -1, 0, true);
852 else {
853 if (nc->leftalt)
854 sp_nodepath_selected_nodes_rotate (nc->nodepath, -1, -1, true);
855 if (nc->rightalt)
856 sp_nodepath_selected_nodes_rotate (nc->nodepath, -1, 1, true);
857 }
858 } else if ( snaps != 0 ) {
859 sp_nodepath_selected_nodes_rotate (nc->nodepath, -M_PI/snaps, 0, false);
860 }
861 ret = TRUE;
862 break;
863 case GDK_less:
864 case GDK_comma:
865 if (MOD__CTRL) {
866 if (nc->leftctrl)
867 sp_nodepath_selected_nodes_scale(nc->nodepath, -offset, -1);
868 if (nc->rightctrl)
869 sp_nodepath_selected_nodes_scale(nc->nodepath, -offset, 1);
870 } else if (MOD__ALT) {
871 if (nc->leftalt && nc->rightalt)
872 sp_nodepath_selected_nodes_scale_screen(nc->nodepath, -1, 0);
873 else {
874 if (nc->leftalt)
875 sp_nodepath_selected_nodes_scale_screen(nc->nodepath, -1, -1);
876 if (nc->rightalt)
877 sp_nodepath_selected_nodes_scale_screen(nc->nodepath, -1, 1);
878 }
879 } else {
880 sp_nodepath_selected_nodes_scale(nc->nodepath, -offset, 0);
881 }
882 ret = TRUE;
883 break;
884 case GDK_greater:
885 case GDK_period:
886 if (MOD__CTRL) {
887 if (nc->leftctrl)
888 sp_nodepath_selected_nodes_scale(nc->nodepath, offset, -1);
889 if (nc->rightctrl)
890 sp_nodepath_selected_nodes_scale(nc->nodepath, offset, 1);
891 } else if (MOD__ALT) {
892 if (nc->leftalt && nc->rightalt)
893 sp_nodepath_selected_nodes_scale_screen(nc->nodepath, 1, 0);
894 else {
895 if (nc->leftalt)
896 sp_nodepath_selected_nodes_scale_screen(nc->nodepath, 1, -1);
897 if (nc->rightalt)
898 sp_nodepath_selected_nodes_scale_screen(nc->nodepath, 1, 1);
899 }
900 } else {
901 sp_nodepath_selected_nodes_scale(nc->nodepath, offset, 0);
902 }
903 ret = TRUE;
904 break;
906 case GDK_Alt_L:
907 nc->leftalt = TRUE;
908 sp_node_context_show_modifier_tip(event_context, event);
909 break;
910 case GDK_Alt_R:
911 nc->rightalt = TRUE;
912 sp_node_context_show_modifier_tip(event_context, event);
913 break;
914 case GDK_Control_L:
915 nc->leftctrl = TRUE;
916 sp_node_context_show_modifier_tip(event_context, event);
917 break;
918 case GDK_Control_R:
919 nc->rightctrl = TRUE;
920 sp_node_context_show_modifier_tip(event_context, event);
921 break;
922 case GDK_Shift_L:
923 case GDK_Shift_R:
924 case GDK_Meta_L:
925 case GDK_Meta_R:
926 sp_node_context_show_modifier_tip(event_context, event);
927 break;
928 default:
929 ret = node_key(event);
930 break;
931 }
932 break;
933 case GDK_KEY_RELEASE:
934 switch (get_group0_keyval(&event->key)) {
935 case GDK_Alt_L:
936 nc->leftalt = FALSE;
937 event_context->defaultMessageContext()->clear();
938 break;
939 case GDK_Alt_R:
940 nc->rightalt = FALSE;
941 event_context->defaultMessageContext()->clear();
942 break;
943 case GDK_Control_L:
944 nc->leftctrl = FALSE;
945 event_context->defaultMessageContext()->clear();
946 break;
947 case GDK_Control_R:
948 nc->rightctrl = FALSE;
949 event_context->defaultMessageContext()->clear();
950 break;
951 case GDK_Shift_L:
952 case GDK_Shift_R:
953 case GDK_Meta_L:
954 case GDK_Meta_R:
955 event_context->defaultMessageContext()->clear();
956 break;
957 }
958 break;
959 default:
960 break;
961 }
963 if (!ret) {
964 if (((SPEventContextClass *) parent_class)->root_handler)
965 ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event);
966 }
968 return ret;
969 }
972 /*
973 Local Variables:
974 mode:c++
975 c-file-style:"stroustrup"
976 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
977 indent-tabs-mode:nil
978 fill-column:99
979 End:
980 */
981 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :