select-context.cpp revision 08ff610afd206b66a72d448ab6738d398ea74d80
/*
* Selection and transformation context
*
* Authors:
* Lauris Kaplinski <lauris@kaplinski.com>
* bulia byak <buliabyak@users.sf.net>
* Abhishek Sharma
* Jon A. Cruz <jon@joncruz.org>
*
* Copyright (C) 2010 authors
* Copyright (C) 2006 Johan Engelen <johan@shouraizou.nl>
* Copyright (C) 1999-2005 Authors
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <cstring>
#include <string>
#include <gdk/gdkkeysyms.h>
#include "macros.h"
#include "rubberband.h"
#include "document.h"
#include "document-undo.h"
#include "selection.h"
#include "sp-cursor.h"
#include "style.h"
#include "pixmaps/cursor-select-m.xpm"
#include "pixmaps/cursor-select-d.xpm"
#include "pixmaps/handles.xpm"
#include "select-context.h"
#include "selection-chemistry.h"
#ifdef WITH_DBUS
#include "extension/dbus/document-interface.h"
#endif
#include "desktop.h"
#include "desktop-handles.h"
#include "sp-root.h"
#include "preferences.h"
#include "tools-switch.h"
#include "message-stack.h"
#include "selection-describer.h"
#include "seltrans.h"
#include "box3d.h"
#include "display/sp-canvas.h"
#include "display/sp-canvas-item.h"
#include "display/drawing-item.h"
using Inkscape::DocumentUndo;
static gint sp_select_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event);
static gint rb_escaped = 0; // if non-zero, rubberband was canceled by esc, so the next button release should not deselect
static bool within_tolerance = false;
static bool is_cycling = false;
static bool moved_while_cycling = false;
static void
{
}
//Creates rotated variations for handles
static void
// We use either the original at *start or previous loop item to rotate
}
}
static void
{
sc->button_press_shift = false;
sc->button_press_ctrl = false;
sc->button_press_alt = false;
sc->cycling_wrap = true;
// cursors in select context
// selection handles
}
static void
{
ec->enableGrDrag(false);
}
delete sc->_describer;
if (CursorSelectDragging) {
#if GTK_CHECK_VERSION(3,0,0)
#else
#endif
}
if (CursorSelectMouseover) {
#if GTK_CHECK_VERSION(3,0,0)
#else
#endif
}
}
static void
{
}
desktop->messageStack(),
_("No objects selected. Click, Shift+click, Alt+scroll mouse on top of objects, or drag around objects to select.")
);
ec->enableGrDrag();
}
}
static void
{
if (path == "show") {
} else {
}
}
}
static bool
{
drag_escaped = 1;
// only undo if the item is still valid
}
} else if (sc->button_press_ctrl) {
// NOTE: This is a workaround to a bug.
// When the ctrl key is held, sc->item is not defined
// so in this case (only), we skip the object doc check
}
SP_EVENT_CONTEXT(sc)->desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Move canceled."));
return true;
}
} else {
rb_escaped = 1;
SP_EVENT_CONTEXT(sc)->desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Selection canceled."));
return true;
}
}
return false;
}
static bool
return (key == GDK_KEY_Alt_L ||
key == GDK_KEY_Alt_R ||
key == GDK_KEY_Control_L ||
key == GDK_KEY_Control_R ||
key == GDK_KEY_Shift_L ||
key == GDK_KEY_Shift_R ||
key == GDK_KEY_Meta_R);
}
static void
{
/* Click in empty place, go up one level -- but don't leave a layer to root.
*
* (Rationale: we don't usually allow users to go to the root, since that
* detracts from the layer metaphor: objects at the root level can in front
* of or behind layers. Whereas it's fine to go to the root if editing
* a document that has no layers (e.g. a non-Inkscape document).)
*
* Once we support editing SVG "islands" (e.g. <svg> embedded in an xhtml
* document), we might consider further restricting the below to disallow
* leaving a layer to go to a non-layer.
*/
if (current_layer) {
if ( parent
|| !( SP_IS_GROUP(current_layer)
{
}
}
}
static gint
{
// make sure we still have valid objects to move around
}
case GDK_BUTTON_PRESS:
/* Left mousebutton */
// save drag origin
within_tolerance = true;
// remember what modifiers were on before button press
// if shift or ctrl was pressed, do not move objects;
// pass the event to root handler which will perform rubberband, shift-click, ctrl-click, ctrl-drag
} else {
// remember the clicked item in sc->item:
}
rb_escaped = drag_escaped = 0;
}
}
// right click; do not eat it so that right-click menu can appear, but cancel dragging & rubberband
}
break;
case GDK_ENTER_NOTIFY:
{
}
break;
}
case GDK_LEAVE_NOTIFY:
}
break;
case GDK_KEY_PRESS:
/* stamping mode: show content mode moving */
}
seltrans->getNextClosestPoint(false);
}
seltrans->getNextClosestPoint(true);
}
}
break;
default:
break;
}
if (!ret) {
ret = (SP_EVENT_CONTEXT_CLASS(sp_select_context_parent_class))->item_handler(event_context, item, event);
}
return ret;
}
static void
sp_select_context_cycle_through_items(SPSelectContext *sc, Inkscape::Selection *selection, GdkEventScroll *scroll_event, bool shift_pressed) {
if (!sc->cycling_cur_item)
return;
// Deactivate current item
// Find next item and activate it
} else {
}
if (next) {
}
if (shift_pressed)
else
}
static gint
{
// make sure we still have valid objects to move around
}
case GDK_2BUTTON_PRESS:
} else { // switch tool
}
} else {
}
}
break;
case GDK_BUTTON_PRESS:
// save drag origin
within_tolerance = true;
}
GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK,
// remember what modifiers were on before button press
rb_escaped = drag_escaped = 0;
// right click; do not eat it so that right-click menu can appear, but cancel dragging & rubberband
}
break;
case GDK_MOTION_NOTIFY:
{
if (is_cycling)
{
moved_while_cycling = true;
}
if ( within_tolerance
break; // do not drag if we're within tolerance from origin
}
// Once the user has moved farther than tolerance from the original location
// (indicating they intend to move the object, not click), then always process the
// motion notify coordinates as given (no snapping back to origin)
within_tolerance = false;
if (sc->button_press_ctrl || (sc->button_press_alt && !sc->button_press_shift && !selection->isEmpty())) {
// if it's not click and ctrl or alt was pressed (the latter with some selection
// but not with shift) we want to drag rather than rubberband
}
/* User has dragged fast, so we get events on root (lauris)*/
// not only that; we will end up here when ctrl-dragging as well
// and also when we started within tolerance, but trespassed tolerance outside of item
SPItem *item_at_point = desktop->getItemAtPoint(Geom::Point(event->button.x, event->button.y), FALSE);
if (!item_at_point) // if no item at this point, try at the click point (bug 1012200)
// drag only if starting from an item, or if something is already grabbed, or if alt-dragging
SPItem *item_in_group = desktop->getItemAtPoint(Geom::Point(event->button.x, event->button.y), TRUE);
// group-at-point is meant to be topmost item if it's a group,
// not topmost group of all items at point
if (group_at_point != item_in_group &&
!(group_at_point && item_at_point &&
// if neither a group nor an item (possibly in a group) at point are selected, set selection to the item at point
&& !sc->button_press_alt) {
// select what is under cursor
seltrans->resetState();
}
// when simply ctrl-dragging, we don't want to go into groups
} // otherwise, do not change selection so that dragging selected-within-group items, as well as alt-dragging, is possible
}
desktop->scroll_to_point(p);
} else {
}
} else {
event_context->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("<b>Draw over</b> objects to select them; release <b>Alt</b> to switch to rubberband selection"));
} else {
event_context->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("<b>Drag around</b> objects to select them; press <b>Alt</b> to switch to touch selection"));
}
}
}
}
break;
}
case GDK_BUTTON_RELEASE:
// item has been moved
#ifdef WITH_DBUS
#endif
// item has not been moved -> simply a click, do selecting
// with shift, toggle selection
seltrans->resetState();
} else {
} else {
seltrans->resetState();
}
}
} else { // simple or shift click, no previous selection
seltrans->resetState();
}
}
}
} else {
if (r->is_started() && !within_tolerance) {
// this was a rubberband drag
if (r->getMode() == RUBBERBAND_MODE_RECT) {
} else if (r->getMode() == RUBBERBAND_MODE_TOUCHPATH) {
}
seltrans->resetState();
r->stop();
// with shift, add to selection
} else {
// without shift, simply select anew
}
} else { // it was just a click, or a too small rubberband
r->stop();
// this was a shift+click or alt+shift+click, select what was clicked upon
sc->button_press_shift = false;
if (sc->button_press_ctrl) {
// go into groups, honoring Alt
} else {
// don't go into groups, honoring Alt
}
if (item) {
}
} else if ((sc->button_press_ctrl || sc->button_press_alt) && !rb_escaped && !drag_escaped) { // ctrl+click, alt+click
if (item) {
} else {
seltrans->resetState();
}
}
} else { // click without shift, simply deselect, unless with Alt or something was cancelled
rb_escaped = 0;
}
}
}
}
}
}
}
sc->button_press_shift = false;
sc->button_press_ctrl = false;
sc->button_press_alt = false;
break;
case GDK_SCROLL:
{
if (moved_while_cycling)
{
moved_while_cycling = false;
}
is_cycling = true;
/* Rebuild list of items underneath the mouse pointer */
// Save pointer to current cycle-item so that we can find it again later, in the freshly built list
}
/* Compare current item list with item list during previous scroll ... */
bool item_lists_differ = false;
// Note that we can do an 'or' comparison in the loop because it is safe to call g_list_next with a NULL pointer.
for (l1 = sc->cycling_items, l2 = sc->cycling_items_cmp; l1 != NULL || l2 != NULL; l1 = g_list_next(l1), l2 = g_list_next(l2)) {
item_lists_differ = true;
break;
}
}
/* If list of items under mouse pointer hasn't changed ... */
if (!item_lists_differ) {
// ... find current item in the freshly built list and continue cycling ...
// TODO: This wouldn't be necessary if cycling_cur_item pointed to an element of cycling_items_cmp instead
} else {
// ... otherwise reset opacities for outdated items ...
//if (!shift_pressed && !g_list_find(sc->cycling_items_selected_before, SP_ITEM(l->data)) && selection->includes(SP_ITEM(l->data)))
if (!g_list_find(sc->cycling_items_selected_before, SP_ITEM(l->data)) && selection->includes(SP_ITEM(l->data)))
}
// ... clear the lists ...
// ... and rebuild them with the new items.
// already selected items are stored separately, too
}
}
// set the current item to the bottommost one so that the cycling step below re-starts at the top
}
// Cycle through the items underneath the mouse pointer, one-by-one
if (w)
{
}
}
break;
}
case GDK_KEY_PRESS: // keybindings for select context
{
{
|| (keyval == GDK_KEY_Alt_L)
|| (keyval == GDK_KEY_Alt_R)
|| (keyval == GDK_KEY_Meta_L)
|| (keyval == GDK_KEY_Meta_R));
if (!key_is_a_modifier (keyval)) {
// if Alt then change cursor to moving cursor:
if (alt) {
}
} else {
// do not change the statusbar text when mousekey is down to move or transform the object,
// because the statusbar text is already updated somewhere else.
break;
}
} else {
_("<b>Shift</b>: click to toggle select; drag for rubberband selection"),
_("<b>Alt</b>: click to select under; scroll mouse-wheel to cycle-select; drag to move selected or select by touch"));
// if Alt and nonempty selection, show moving cursor ("move selected"):
}
//*/
break;
}
}
gdouble const nudge = prefs->getDoubleLimited("/options/nudgedistance/value", 2, 0, 1000, "px"); // in px
case GDK_KEY_Left: // move selection left
case GDK_KEY_KP_Left:
if (MOD__SHIFT(event)) sp_selection_move_screen(sp_desktop_selection(desktop), mul*-10, 0); // shift
}
else { // no alt
}
}
break;
case GDK_KEY_Up: // move selection up
case GDK_KEY_KP_Up:
}
else { // no alt
}
}
break;
case GDK_KEY_Right: // move selection right
case GDK_KEY_KP_Right:
}
else { // no alt
}
}
break;
case GDK_KEY_Down: // move selection down
case GDK_KEY_KP_Down:
if (MOD__SHIFT(event)) sp_selection_move_screen(sp_desktop_selection(desktop), 0, mul*-10); // shift
}
else { // no alt
}
}
break;
case GDK_KEY_Escape:
break;
case GDK_KEY_a:
case GDK_KEY_A:
if (MOD__CTRL_ONLY(event)) {
}
break;
case GDK_KEY_space:
/* stamping mode: show outline mode moving */
/* FIXME: Is next condition ok? (lauris) */
}
break;
case GDK_KEY_x:
case GDK_KEY_X:
if (MOD__ALT_ONLY(event)) {
}
break;
case GDK_KEY_bracketleft:
} else if (snaps) {
}
break;
case GDK_KEY_bracketright:
} else if (snaps) {
}
break;
case GDK_KEY_less:
case GDK_KEY_comma:
} else {
}
break;
case GDK_KEY_greater:
case GDK_KEY_period:
} else {
}
break;
case GDK_KEY_Return:
if (MOD__CTRL_ONLY(event)) {
if (selection->singleItem()) {
if ( SP_IS_GROUP(clicked_item) ||
} else {
SP_EVENT_CONTEXT(sc)->desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Selected object is not a group. Cannot enter."));
}
}
}
break;
case GDK_KEY_BackSpace:
if (MOD__CTRL_ONLY(event)) {
}
break;
case GDK_KEY_s:
case GDK_KEY_S:
if (MOD__SHIFT_ONLY(event)) {
}
}
break;
case GDK_KEY_g:
case GDK_KEY_G:
if (MOD__SHIFT_ONLY(event)) {
ret = true;
}
break;
default:
break;
}
break;
}
case GDK_KEY_RELEASE:
{
if (key_is_a_modifier (keyval))
|| (keyval == GDK_KEY_Alt_L)
|| (keyval == GDK_KEY_Alt_R)
|| (keyval == GDK_KEY_Meta_L)
|| (keyval == GDK_KEY_Meta_R));
// if Alt then change cursor to moving cursor:
if (alt) {
}
} else {
if (alt) { // TODO: Should we have a variable like is_cycling or is it harmless to run this piece of code each time?
// quit cycle-selection and reset opacities
if (is_cycling)
{
is_cycling = false;
}
}
}
}
// set cursor to default.
if (!desktop->isWaitingCursor()) {
// Do we need to reset the cursor here on key release ?
//GdkWindow* window = gtk_widget_get_window (GTK_WIDGET (sp_desktop_canvas(desktop)));
//gdk_window_set_cursor(window, event_context->cursor);
}
break;
default:
break;
}
if (!ret) {
}
return ret;
}
static void
{
// SPDesktop *desktop = event_context->desktop;
}
}
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :