Code

While moving or transforming a selected object, the statusbartext with transformation...
[inkscape.git] / src / select-context.cpp
1 #define __SP_SELECT_CONTEXT_C__
3 /*
4  * Selection and transformation context
5  *
6  * Author:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9   *
10  * Copyright (C) 1999-2005 authors
11  *
12  * Released under GNU GPL, read the file 'COPYING' for more information
13  */
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
18 #include <gdk/gdkkeysyms.h>
19 #include "macros.h"
20 #include "rubberband.h"
21 #include "document.h"
22 #include "selection.h"
23 #include "seltrans-handles.h"
24 #include "sp-cursor.h"
25 //#include "pixmaps/cursor-select-m.xpm" // These aren't used
26 //#include "pixmaps/cursor-select-d.xpm"
27 #include "pixmaps/handles.xpm"
28 #include <glibmm/i18n.h>
30 #include "select-context.h"
31 #include "selection-chemistry.h"
32 #include "desktop.h"
33 #include "desktop-handles.h"
34 #include "sp-root.h"
35 #include "prefs-utils.h"
36 #include "tools-switch.h"
37 #include "message-stack.h"
38 #include "selection-describer.h"
39 #include "seltrans.h"
41 static void sp_select_context_class_init(SPSelectContextClass *klass);
42 static void sp_select_context_init(SPSelectContext *select_context);
43 static void sp_select_context_dispose(GObject *object);
45 static void sp_select_context_setup(SPEventContext *ec);
46 static void sp_select_context_set(SPEventContext *ec, gchar const *key, gchar const *val);
47 static gint sp_select_context_root_handler(SPEventContext *event_context, GdkEvent *event);
48 static gint sp_select_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event);
50 static SPEventContextClass *parent_class;
52 static GdkCursor *CursorSelectMouseover = NULL;
53 static GdkCursor *CursorSelectDragging = NULL;
54 GdkPixbuf *handles[13];
56 static gint rb_escaped = 0; // if non-zero, rubberband was canceled by esc, so the next button release should not deselect
57 static gint drag_escaped = 0; // if non-zero, drag was canceled by esc
59 static gint xp = 0, yp = 0; // where drag started
60 static gint tolerance = 0;
61 static bool within_tolerance = false;
63 GtkType
64 sp_select_context_get_type(void)
65 {
66     static GType type = 0;
67     if (!type) {
68         GTypeInfo info = {
69             sizeof(SPSelectContextClass),
70             NULL, NULL,
71             (GClassInitFunc) sp_select_context_class_init,
72             NULL, NULL,
73             sizeof(SPSelectContext),
74             4,
75             (GInstanceInitFunc) sp_select_context_init,
76             NULL,   /* value_table */
77         };
78         type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "SPSelectContext", &info, (GTypeFlags)0);
79     }
80     return type;
81 }
83 static void
84 sp_select_context_class_init(SPSelectContextClass *klass)
85 {
86     GObjectClass *object_class = (GObjectClass *) klass;
87     SPEventContextClass *event_context_class = (SPEventContextClass *) klass;
89     parent_class = (SPEventContextClass*)g_type_class_peek_parent(klass);
91     object_class->dispose = sp_select_context_dispose;
93     event_context_class->setup = sp_select_context_setup;
94     event_context_class->set = sp_select_context_set;
95     event_context_class->root_handler = sp_select_context_root_handler;
96     event_context_class->item_handler = sp_select_context_item_handler;
98     // cursors in select context
99 //    CursorSelectMouseover = sp_cursor_new_from_xpm(cursor_select_m_xpm , 1, 1);
100 //    CursorSelectDragging = sp_cursor_new_from_xpm(cursor_select_d_xpm , 1, 1);
101     // selection handles
102     handles[0]  = gdk_pixbuf_new_from_xpm_data((gchar const **)handle_scale_nw_xpm);
103     handles[1]  = gdk_pixbuf_new_from_xpm_data((gchar const **)handle_scale_ne_xpm);
104     handles[2]  = gdk_pixbuf_new_from_xpm_data((gchar const **)handle_scale_h_xpm);
105     handles[3]  = gdk_pixbuf_new_from_xpm_data((gchar const **)handle_scale_v_xpm);
106     handles[4]  = gdk_pixbuf_new_from_xpm_data((gchar const **)handle_rotate_nw_xpm);
107     handles[5]  = gdk_pixbuf_new_from_xpm_data((gchar const **)handle_rotate_n_xpm);
108     handles[6]  = gdk_pixbuf_new_from_xpm_data((gchar const **)handle_rotate_ne_xpm);
109     handles[7]  = gdk_pixbuf_new_from_xpm_data((gchar const **)handle_rotate_e_xpm);
110     handles[8]  = gdk_pixbuf_new_from_xpm_data((gchar const **)handle_rotate_se_xpm);
111     handles[9]  = gdk_pixbuf_new_from_xpm_data((gchar const **)handle_rotate_s_xpm);
112     handles[10] = gdk_pixbuf_new_from_xpm_data((gchar const **)handle_rotate_sw_xpm);
113     handles[11] = gdk_pixbuf_new_from_xpm_data((gchar const **)handle_rotate_w_xpm);
114     handles[12] = gdk_pixbuf_new_from_xpm_data((gchar const **)handle_center_xpm);
118 static void
119 sp_select_context_init(SPSelectContext *sc)
121     sc->dragging = FALSE;
122     sc->moved = FALSE;
123     sc->button_press_shift = false;
124     sc->button_press_ctrl = false;
125     sc->button_press_alt = false;
126     sc->_seltrans = NULL;
127     sc->_describer = NULL;
130 static void
131 sp_select_context_dispose(GObject *object)
133     SPSelectContext *sc = SP_SELECT_CONTEXT(object);
134     SPEventContext * ec = SP_EVENT_CONTEXT (object);
136     ec->enableGrDrag(false);
138     if (sc->grabbed) {
139         sp_canvas_item_ungrab(sc->grabbed, GDK_CURRENT_TIME);
140         sc->grabbed = NULL;
141     }
143     delete sc->_seltrans;
144     sc->_seltrans = NULL;
145     delete sc->_describer;
146     sc->_describer = NULL;
148     if (CursorSelectDragging) {
149         gdk_cursor_unref (CursorSelectDragging);
150         CursorSelectDragging = NULL;
151     }
152     if (CursorSelectMouseover) {
153         gdk_cursor_unref (CursorSelectMouseover);
154         CursorSelectMouseover = NULL;
155     }
157     G_OBJECT_CLASS(parent_class)->dispose(object);
160 static void
161 sp_select_context_setup(SPEventContext *ec)
163     SPSelectContext *select_context = SP_SELECT_CONTEXT(ec);
165     if (((SPEventContextClass *) parent_class)->setup) {
166         ((SPEventContextClass *) parent_class)->setup(ec);
167     }
169     SPDesktop *desktop = ec->desktop;
171     select_context->_describer = new Inkscape::SelectionDescriber(desktop->selection, desktop->messageStack());
173     select_context->_seltrans = new Inkscape::SelTrans(desktop);
175     sp_event_context_read(ec, "show");
176     sp_event_context_read(ec, "transform");
178     if (prefs_get_int_attribute("tools.select", "gradientdrag", 0) != 0) {
179         ec->enableGrDrag();
180     }
183 static void
184 sp_select_context_set(SPEventContext *ec, gchar const *key, gchar const *val)
186     SPSelectContext *sc = SP_SELECT_CONTEXT(ec);
188     if (!strcmp(key, "show")) {
189         if (val && !strcmp(val, "outline")) {
190             sc->_seltrans->setShow(Inkscape::SelTrans::SHOW_OUTLINE);
191         } else {
192             sc->_seltrans->setShow(Inkscape::SelTrans::SHOW_CONTENT);
193         }
194     }
197 static bool
198 sp_select_context_abort(SPEventContext *event_context)
200     SPDesktop *desktop = event_context->desktop;
201     SPSelectContext *sc = SP_SELECT_CONTEXT(event_context);
202     Inkscape::SelTrans *seltrans = sc->_seltrans;
204     if (sc->dragging) {
205         if (sc->moved) { // cancel dragging an object
206             seltrans->ungrab();
207             sc->moved = FALSE;
208             sc->dragging = FALSE;
209             drag_escaped = 1;
211             if (sc->item) {
212                 // only undo if the item is still valid
213                 if (SP_OBJECT_DOCUMENT( SP_OBJECT(sc->item))) {
214                     sp_document_undo(sp_desktop_document(desktop));
215                 }
217                 sp_object_unref( SP_OBJECT(sc->item), NULL);
218             } else if (sc->button_press_ctrl) {
219                 // NOTE:  This is a workaround to a bug.
220                 // When the ctrl key is held, sc->item is not defined
221                 // so in this case (only), we skip the object doc check
222                 sp_document_undo(sp_desktop_document(desktop));
223             }
224             sc->item = NULL;
226             SP_EVENT_CONTEXT(sc)->desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Move canceled."));
227             return true;
228         }
229     } else {
230         NR::Maybe<NR::Rect> const b = Inkscape::Rubberband::get()->getRectangle();
231         if (b != NR::Nothing()) {
232             Inkscape::Rubberband::get()->stop();
233             rb_escaped = 1;
234             SP_EVENT_CONTEXT(sc)->desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Selection canceled."));
235             return true;
236         }
237     }
238     return false;
241 bool
242 key_is_a_modifier (guint key) {
243     return (key == GDK_Alt_L ||
244                 key == GDK_Alt_R ||
245                 key == GDK_Control_L ||
246                 key == GDK_Control_R ||
247                 key == GDK_Shift_L ||
248                 key == GDK_Shift_R ||
249                 key == GDK_Meta_L ||  // Meta is when you press Shift+Alt (at least on my machine)
250             key == GDK_Meta_R);
253 void
254 sp_select_context_up_one_layer(SPDesktop *desktop)
256     /* Click in empty place, go up one level -- but don't leave a layer to root.
257      *
258      * (Rationale: we don't usually allow users to go to the root, since that
259      * detracts from the layer metaphor: objects at the root level can in front
260      * of or behind layers.  Whereas it's fine to go to the root if editing
261      * a document that has no layers (e.g. a non-Inkscape document).)
262      *
263      * Once we support editing SVG "islands" (e.g. <svg> embedded in an xhtml
264      * document), we might consider further restricting the below to disallow
265      * leaving a layer to go to a non-layer.
266      */
267     SPObject *const current_layer = desktop->currentLayer();
268     if (current_layer) {
269         SPObject *const parent = SP_OBJECT_PARENT(current_layer);
270         if ( parent
271              && ( SP_OBJECT_PARENT(parent)
272                   || !( SP_IS_GROUP(current_layer)
273                         && ( SPGroup::LAYER
274                              == SP_GROUP(current_layer)->layerMode() ) ) ) )
275         {
276             desktop->setCurrentLayer(parent);
277             if (SP_IS_GROUP(current_layer) && SPGroup::LAYER != SP_GROUP(current_layer)->layerMode())
278                 sp_desktop_selection(desktop)->set(current_layer);
279         }
280     }
283 static gint
284 sp_select_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event)
286     gint ret = FALSE;
288     SPDesktop *desktop = event_context->desktop;
289     SPSelectContext *sc = SP_SELECT_CONTEXT(event_context);
290     Inkscape::SelTrans *seltrans = sc->_seltrans;
292     tolerance = prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100);
294     // make sure we still have valid objects to move around
295     if (sc->item && SP_OBJECT_DOCUMENT( SP_OBJECT(sc->item))==NULL) {
296         sp_select_context_abort(event_context);
297     }
299     switch (event->type) {
300         case GDK_BUTTON_PRESS:
301             if (event->button.button == 1) {
302                 /* Left mousebutton */
304                 // save drag origin
305                 xp = (gint) event->button.x;
306                 yp = (gint) event->button.y;
307                 within_tolerance = true;
309                 // remember what modifiers were on before button press
310                 sc->button_press_shift = (event->button.state & GDK_SHIFT_MASK) ? true : false;
311                 sc->button_press_ctrl = (event->button.state & GDK_CONTROL_MASK) ? true : false;
312                 sc->button_press_alt = (event->button.state & GDK_MOD1_MASK) ? true : false;
314                 if (event->button.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) {
315                     // if shift or ctrl was pressed, do not move objects;
316                     // pass the event to root handler which will perform rubberband, shift-click, ctrl-click, ctrl-drag
317                 } else {
318                     sc->dragging = TRUE;
319                     sc->moved = FALSE;
321                     sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 5);
322                     
323                     // remember the clicked item in sc->item:
324                     sc->item = sp_event_context_find_item (desktop,
325                                               NR::Point(event->button.x, event->button.y), event->button.state & GDK_MOD1_MASK, FALSE);
326                     sp_object_ref(sc->item, NULL);
328                     rb_escaped = drag_escaped = 0;
330                     sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->drawing),
331                                         GDK_KEY_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK |
332                                         GDK_POINTER_MOTION_MASK,
333                                         NULL, event->button.time);
334                     sc->grabbed = SP_CANVAS_ITEM(desktop->drawing);
336                     sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 5);
338                     ret = TRUE;
339                 }
340             } else if (event->button.button == 3) {
341                 // right click; do not eat it so that right-click menu can appear, but cancel dragging & rubberband
342                 sp_select_context_abort(event_context);
343             }
344             break;
346         case GDK_ENTER_NOTIFY:
347         {
348             GdkCursor *cursor = gdk_cursor_new(GDK_FLEUR);
349             gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(desktop))->window, cursor);
350             gdk_cursor_destroy(cursor);
351             break;
352         }
354         case GDK_LEAVE_NOTIFY:
355             gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(desktop))->window, event_context->cursor);
356             break;
358         case GDK_KEY_PRESS:
359             if (get_group0_keyval (&event->key) == GDK_space) {
360                 if (sc->dragging && sc->grabbed) {
361                     /* stamping mode: show content mode moving */
362                     seltrans->stamp();
363                     ret = TRUE;
364                 }
365             }
366             break;
368         default:
369             break;
370     }
372     if (!ret) {
373         if (((SPEventContextClass *) parent_class)->item_handler)
374             ret = ((SPEventContextClass *) parent_class)->item_handler(event_context, item, event);
375     }
377     return ret;
380 static gint
381 sp_select_context_root_handler(SPEventContext *event_context, GdkEvent *event)
383     SPItem *item = NULL;
384     SPItem *item_at_point = NULL, *group_at_point = NULL, *item_in_group = NULL;
385     gint ret = FALSE;
387     SPDesktop *desktop = event_context->desktop;
388     SPSelectContext *sc = SP_SELECT_CONTEXT(event_context);
389     Inkscape::SelTrans *seltrans = sc->_seltrans;
390     Inkscape::Selection *selection = sp_desktop_selection(desktop);
391     gdouble const nudge = prefs_get_double_attribute_limited("options.nudgedistance", "value", 2, 0, 1000); // in px
392     gdouble const offset = prefs_get_double_attribute_limited("options.defaultscale", "value", 2, 0, 1000);
393     tolerance = prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100);
394     int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
396     // make sure we still have valid objects to move around
397     if (sc->item && SP_OBJECT_DOCUMENT( SP_OBJECT(sc->item))==NULL) {
398         sp_select_context_abort(event_context);
399     }
401     switch (event->type) {
402         case GDK_2BUTTON_PRESS:
403             if (event->button.button == 1) {
404                 if (!selection->isEmpty()) {
405                     SPItem *clicked_item = (SPItem *) selection->itemList()->data;
406                     if (SP_IS_GROUP (clicked_item)) { // enter group
407                         desktop->setCurrentLayer(reinterpret_cast<SPObject *>(clicked_item));
408                         sp_desktop_selection(desktop)->clear();
409                         sc->dragging = false;
411                         sp_canvas_end_forced_full_redraws(desktop->canvas);
412                     } else { // switch tool
413                         tools_switch_by_item (desktop, clicked_item);
414                     }
415                 } else {
416                     sp_select_context_up_one_layer(desktop);
417                 }
418                 ret = TRUE;
419             }
420             break;
421         case GDK_BUTTON_PRESS:
422             if (event->button.button == 1) {
424                 // save drag origin
425                 xp = (gint) event->button.x;
426                 yp = (gint) event->button.y;
427                 within_tolerance = true;
429                 NR::Point const button_pt(event->button.x, event->button.y);
430                 NR::Point const p(desktop->w2d(button_pt));
431                 Inkscape::Rubberband::get()->start(desktop, p);
432                 sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate),
433                                     GDK_KEY_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK,
434                                     NULL, event->button.time);
435                 sc->grabbed = SP_CANVAS_ITEM(desktop->acetate);
437                 // remember what modifiers were on before button press
438                 sc->button_press_shift = (event->button.state & GDK_SHIFT_MASK) ? true : false;
439                 sc->button_press_ctrl = (event->button.state & GDK_CONTROL_MASK) ? true : false;
440                 sc->button_press_alt = (event->button.state & GDK_MOD1_MASK) ? true : false;
442                 sc->moved = FALSE;
444                 rb_escaped = drag_escaped = 0;
446                 ret = TRUE;
447             } else if (event->button.button == 3) {
448                 // right click; do not eat it so that right-click menu can appear, but cancel dragging & rubberband
449                 sp_select_context_abort(event_context);
450             }
451             break;
453         case GDK_MOTION_NOTIFY:
454             if (event->motion.state & GDK_BUTTON1_MASK) {
455                 NR::Point const motion_pt(event->motion.x, event->motion.y);
456                 NR::Point const p(desktop->w2d(motion_pt));
458                 if ( within_tolerance
459                      && ( abs( (gint) event->motion.x - xp ) < tolerance )
460                      && ( abs( (gint) event->motion.y - yp ) < tolerance ) ) {
461                     break; // do not drag if we're within tolerance from origin
462                 }
463                 // Once the user has moved farther than tolerance from the original location
464                 // (indicating they intend to move the object, not click), then always process the
465                 // motion notify coordinates as given (no snapping back to origin)
466                 within_tolerance = false;
468                 if (sc->button_press_ctrl || sc->button_press_alt) {
469                     // if ctrl or alt was pressed and it's not click, we want to drag rather than rubberband
470                     sc->dragging = TRUE;
472                     sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 5);
473                 }
475                 if (sc->dragging) {
476                     /* User has dragged fast, so we get events on root (lauris)*/
477                     // not only that; we will end up here when ctrl-dragging as well
478                     // and also when we started within tolerance, but trespassed tolerance outside of item
479                     Inkscape::Rubberband::get()->stop();
480                     item_at_point = desktop->item_at_point(NR::Point(event->button.x, event->button.y), FALSE);
481                     if (!item_at_point) // if no item at this point, try at the click point (bug 1012200)
482                         item_at_point = desktop->item_at_point(NR::Point(xp, yp), FALSE);
483                     if (item_at_point || sc->moved || sc->button_press_alt) {
484                         // drag only if starting from an item, or if something is already grabbed, or if alt-dragging
485                         if (!sc->moved) {
486                             item_in_group = desktop->item_at_point(NR::Point(event->button.x, event->button.y), TRUE);
487                             group_at_point = desktop->group_at_point(NR::Point(event->button.x, event->button.y));
488                             // if neither a group nor an item (possibly in a group) at point are selected, set selection to the item at point
489                             if ((!item_in_group || !selection->includes(item_in_group)) &&
490                                 (!group_at_point || !selection->includes(group_at_point))
491                                 && !sc->button_press_alt) {
492                                 // select what is under cursor
493                                 if (!seltrans->isEmpty()) {
494                                     seltrans->resetState();
495                                 }
496                                 // when simply ctrl-dragging, we don't want to go into groups
497                                 if (item_at_point && !selection->includes(item_at_point))
498                                     selection->set(item_at_point);
499                             } // otherwise, do not change selection so that dragging selected-within-group items, as well as alt-dragging, is possible
500                             seltrans->grab(p, -1, -1, FALSE);
501                             sc->moved = TRUE;
502                         }
503                         if (!seltrans->isEmpty())
504                             seltrans->moveTo(p, event->button.state);
505                         if (desktop->scroll_to_point(&p))
506                             // unfortunately in complex drawings, gobbling results in losing grab of the object, for some mysterious reason
507                             ; //gobble_motion_events(GDK_BUTTON1_MASK);
508                         ret = TRUE;
509                     } else {
510                         sc->dragging = FALSE;
512                         sp_canvas_end_forced_full_redraws(desktop->canvas);
513                     }
514                 } else {
515                     Inkscape::Rubberband::get()->move(p);
516                     gobble_motion_events(GDK_BUTTON1_MASK);
517                 }
518             }
519             break;
520         case GDK_BUTTON_RELEASE:
521             xp = yp = 0;
522             if ((event->button.button == 1) && (sc->grabbed)) {
523                 if (sc->dragging) {
524                     if (sc->moved) {
525                         // item has been moved
526                         seltrans->ungrab();
527                         sc->moved = FALSE;
528                     } else if (sc->item && !drag_escaped) {
529                         // item has not been moved -> simply a click, do selecting
530                         if (!selection->isEmpty()) {
531                             if (event->button.state & GDK_SHIFT_MASK) {
532                                 // with shift, toggle selection
533                                 seltrans->resetState();
534                                 selection->toggle(sc->item);
535                             } else {
536                                 // without shift, increase state (i.e. toggle scale/rotation handles)
537                                 if (selection->includes(sc->item)) {
538                                     seltrans->increaseState();
539                                 } else {
540                                     seltrans->resetState();
541                                     selection->set(sc->item);
542                                 }
543                             }
544                         } else { // simple or shift click, no previous selection
545                             seltrans->resetState();
546                             selection->set(sc->item);
547                         }
548                     }
549                     sc->dragging = FALSE;
551                     sp_canvas_end_forced_full_redraws(desktop->canvas);
553                     if (sc->item) {
554                         sp_object_unref( SP_OBJECT(sc->item), NULL);
555                     }
556                     sc->item = NULL;
557                 } else {
558                     NR::Maybe<NR::Rect> const b = Inkscape::Rubberband::get()->getRectangle();
559                     if (b != NR::Nothing() && !within_tolerance) {
560                         // this was a rubberband drag
561                         Inkscape::Rubberband::get()->stop();
562                         seltrans->resetState();
563                         // find out affected items:
564                         GSList *items = sp_document_items_in_box(sp_desktop_document(desktop), desktop->dkey, b.assume());
565                         if (event->button.state & GDK_SHIFT_MASK) {
566                             // with shift, add to selection
567                             selection->addList (items);
568                         } else {
569                             // without shift, simply select anew
570                             selection->setList (items);
571                         }
572                         g_slist_free (items);
573                     } else { // it was just a click, or a too small rubberband
574                         Inkscape::Rubberband::get()->stop();
575                         if (sc->button_press_shift && !rb_escaped && !drag_escaped) {
576                             // this was a shift-click, select what was clicked upon
578                             sc->button_press_shift = false;
580                             if (sc->button_press_ctrl) {
581                                 // go into groups, honoring Alt
582                                 item = sp_event_context_find_item (desktop,
583                                                    NR::Point(event->button.x, event->button.y), event->button.state & GDK_MOD1_MASK, TRUE);
584                                 sc->button_press_ctrl = FALSE;
585                             } else {
586                                 // don't go into groups, honoring Alt
587                                 item = sp_event_context_find_item (desktop,
588                                                    NR::Point(event->button.x, event->button.y), event->button.state & GDK_MOD1_MASK, FALSE);
589                             }
591                             if (item) {
592                                 selection->toggle(item);
593                                 item = NULL;
594                             }
596                         } else if (sc->button_press_ctrl && !rb_escaped && !drag_escaped) { // ctrl-click
598                             sc->button_press_ctrl = FALSE;
600                             item = sp_event_context_find_item (desktop,
601                                          NR::Point(event->button.x, event->button.y), event->button.state & GDK_MOD1_MASK, TRUE);
603                             if (item) {
604                                 if (selection->includes(item)) {
605                                     seltrans->increaseState();
606                                 } else {
607                                     seltrans->resetState();
608                                     selection->set(item);
609                                 }
610                                 item = NULL;
611                             }
613                         } else { // click without shift, simply deselect, unless with Alt or something was cancelled
614                             if (!selection->isEmpty()) {
615                                 if (!(rb_escaped) && !(drag_escaped) && !(event->button.state & GDK_MOD1_MASK))
616                                     selection->clear();
617                                 rb_escaped = 0;
618                                 ret = TRUE;
619                             }
620                         }
621                     }
622                     ret = TRUE;
623                 }
624                 if (sc->grabbed) {
625                     sp_canvas_item_ungrab(sc->grabbed, event->button.time);
626                     sc->grabbed = NULL;
627                 }
628                 
629                 desktop->updateNow();
630             }
631             sc->button_press_shift = false;
632             sc->button_press_ctrl = false;
633             sc->button_press_alt = false;
634             break;
636         case GDK_KEY_PRESS: // keybindings for select context
638             if (!key_is_a_modifier (get_group0_keyval (&event->key))) {
639                     event_context->defaultMessageContext()->clear();
640             } else if (sc->grabbed || seltrans->isGrabbed()) {
641                 // do not change the statusbar text when mousekey is down to move or transform the object,
642                 // because the statusbar text is already updated somewhere else.
643                    break;
644             } else {
645                     sp_event_show_modifier_tip (event_context->defaultMessageContext(), event,
646                                                 _("<b>Ctrl</b>: select in groups, move hor/vert"),
647                                                 _("<b>Shift</b>: toggle select, force rubberband, disable snapping"),
648                                                 _("<b>Alt</b>: select under, move selected"));
649                     // if Alt then change cursor to moving cursor:
650                     guint keyval = get_group0_keyval(&event->key);
651                     bool alt = ( MOD__ALT
652                                     || (keyval == GDK_Alt_L)
653                                     || (keyval == GDK_Alt_R)
654                                     || (keyval == GDK_Meta_L)
655                                     || (keyval == GDK_Meta_R));
656                     if (alt) {
657                         GdkCursor *cursor = gdk_cursor_new(GDK_FLEUR);
658                         gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(desktop))->window, cursor);
659                         gdk_cursor_destroy(cursor);
660                     }
661                     //*/
662                     break;
663             }
665             switch (get_group0_keyval (&event->key)) {
666                 case GDK_Left: // move selection left
667                 case GDK_KP_Left:
668                 case GDK_KP_4:
669                     if (!MOD__CTRL) { // not ctrl
670                         if (MOD__ALT) { // alt
671                             if (MOD__SHIFT) sp_selection_move_screen(-10, 0); // shift
672                             else sp_selection_move_screen(-1, 0); // no shift
673                         }
674                         else { // no alt
675                             if (MOD__SHIFT) sp_selection_move(-10*nudge, 0); // shift
676                             else sp_selection_move(-nudge, 0); // no shift
677                         }
678                         ret = TRUE;
679                     }
680                     break;
681                 case GDK_Up: // move selection up
682                 case GDK_KP_Up:
683                 case GDK_KP_8:
684                     if (!MOD__CTRL) { // not ctrl
685                         if (MOD__ALT) { // alt
686                             if (MOD__SHIFT) sp_selection_move_screen(0, 10); // shift
687                             else sp_selection_move_screen(0, 1); // no shift
688                         }
689                         else { // no alt
690                             if (MOD__SHIFT) sp_selection_move(0, 10*nudge); // shift
691                             else sp_selection_move(0, nudge); // no shift
692                         }
693                         ret = TRUE;
694                     }
695                     break;
696                 case GDK_Right: // move selection right
697                 case GDK_KP_Right:
698                 case GDK_KP_6:
699                     if (!MOD__CTRL) { // not ctrl
700                         if (MOD__ALT) { // alt
701                             if (MOD__SHIFT) sp_selection_move_screen(10, 0); // shift
702                             else sp_selection_move_screen(1, 0); // no shift
703                         }
704                         else { // no alt
705                             if (MOD__SHIFT) sp_selection_move(10*nudge, 0); // shift
706                             else sp_selection_move(nudge, 0); // no shift
707                         }
708                         ret = TRUE;
709                     }
710                     break;
711                 case GDK_Down: // move selection down
712                 case GDK_KP_Down:
713                 case GDK_KP_2:
714                     if (!MOD__CTRL) { // not ctrl
715                         if (MOD__ALT) { // alt
716                             if (MOD__SHIFT) sp_selection_move_screen(0, -10); // shift
717                             else sp_selection_move_screen(0, -1); // no shift
718                         }
719                         else { // no alt
720                             if (MOD__SHIFT) sp_selection_move(0, -10*nudge); // shift
721                             else sp_selection_move(0, -nudge); // no shift
722                         }
723                         ret = TRUE;
724                     }
725                     break;
726                 case GDK_Escape:
727                     if (!sp_select_context_abort(event_context))
728                         selection->clear();
729                     ret = TRUE;
730                     break;
731                 case GDK_a:
732                 case GDK_A:
733                     if (MOD__CTRL_ONLY) {
734                         sp_edit_select_all();
735                         ret = TRUE;
736                     }
737                     break;
738                 case GDK_Tab: // Tab - cycle selection forward
739                     if (!(MOD__CTRL_ONLY || (MOD__CTRL && MOD__SHIFT))) {
740                         sp_selection_item_next();
741                         ret = TRUE;
742                     }
743                     break;
744                 case GDK_ISO_Left_Tab: // Shift Tab - cycle selection backward
745                     if (!(MOD__CTRL_ONLY || (MOD__CTRL && MOD__SHIFT))) {
746                         sp_selection_item_prev();
747                         ret = TRUE;
748                     }
749                     break;
750                 case GDK_space:
751                     /* stamping mode: show outline mode moving */
752                     /* FIXME: Is next condition ok? (lauris) */
753                     if (sc->dragging && sc->grabbed) {
754                         seltrans->stamp();
755                         ret = TRUE;
756                     }
757                     break;
758                 case GDK_x:
759                 case GDK_X:
760                     if (MOD__ALT_ONLY) {
761                         desktop->setToolboxFocusTo ("altx");
762                         ret = TRUE;
763                     }
764                     break;
765                 case GDK_bracketleft:
766                     if (MOD__ALT) {
767                         sp_selection_rotate_screen(selection, 1);
768                     } else if (MOD__CTRL) {
769                         sp_selection_rotate(selection, 90);
770                     } else if (snaps) {
771                         sp_selection_rotate(selection, 180/snaps);
772                     }
773                     ret = TRUE;
774                     break;
775                 case GDK_bracketright:
776                     if (MOD__ALT) {
777                         sp_selection_rotate_screen(selection, -1);
778                     } else if (MOD__CTRL) {
779                         sp_selection_rotate(selection, -90);
780                     } else if (snaps) {
781                         sp_selection_rotate(selection, -180/snaps);
782                     }
783                     ret = TRUE;
784                     break;
785                 case GDK_less:
786                 case GDK_comma:
787                     if (MOD__ALT) {
788                         sp_selection_scale_screen(selection, -2);
789                     } else if (MOD__CTRL) {
790                         sp_selection_scale_times(selection, 0.5);
791                     } else {
792                         sp_selection_scale(selection, -offset);
793                     }
794                     ret = TRUE;
795                     break;
796                 case GDK_greater:
797                 case GDK_period:
798                     if (MOD__ALT) {
799                         sp_selection_scale_screen(selection, 2);
800                     } else if (MOD__CTRL) {
801                         sp_selection_scale_times(selection, 2);
802                     } else {
803                         sp_selection_scale(selection, offset);
804                     }
805                     ret = TRUE;
806                     break;
807                 case GDK_Return:
808                     if (MOD__CTRL_ONLY) {
809                         if (selection->singleItem()) {
810                             SPItem *clicked_item = selection->singleItem();
811                             if (SP_IS_GROUP (clicked_item)) { // enter group
812                                 desktop->setCurrentLayer(reinterpret_cast<SPObject *>(clicked_item));
813                                 sp_desktop_selection(desktop)->clear();
814                             } else {
815                                 SP_EVENT_CONTEXT(sc)->desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Selected object is not a group. Cannot enter."));
816                             }
817                         }
818                         ret = TRUE;
819                     }
820                     break;
821                 case GDK_BackSpace:
822                     if (MOD__CTRL_ONLY) {
823                         sp_select_context_up_one_layer(desktop);
824                         ret = TRUE;
825                     }
826                     break;
827                 default:
828                     break;
829             }
830             break;
831         case GDK_KEY_RELEASE:
832             if (key_is_a_modifier (get_group0_keyval (&event->key)))
833                 event_context->defaultMessageContext()->clear();
834                 // set cursor to default.
835                 gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(desktop))->window, event_context->cursor);
836             break;
837         default:
838             break;
839     }
841     if (!ret) {
842         if (((SPEventContextClass *) parent_class)->root_handler)
843             ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event);
844     }
846     return ret;
850 /*
851   Local Variables:
852   mode:c++
853   c-file-style:"stroustrup"
854   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
855   indent-tabs-mode:nil
856   fill-column:99
857   End:
858 */
859 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :