select-tool.cpp revision 9ba77856a8823f85b53c0a861d220cd0347f2754
/*
* 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 "ui/tools/select-tool.h"
#include "selection-chemistry.h"
#ifdef WITH_DBUS
#include "extension/dbus/document-interface.h"
#endif
#include "desktop.h"
#include "sp-root.h"
#include "preferences.h"
#include "ui/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"
#include "ui/tool-factory.h"
using Inkscape::DocumentUndo;
namespace Inkscape {
namespace UI {
namespace Tools {
static gint rb_escaped = 0; // if non-zero, rubberband was canceled by esc, so the next button release should not deselect
namespace {
return new SelectTool();
}
bool selectContextRegistered = ToolFactory::instance().registerObject("/tools/select", createSelectContext);
}
return SelectTool::prefsPath;
}
//Creates rotated variations for handles
static void
// We use either the original at *start or previous loop item to rotate
}
}
// Don't load a default cursor
, dragging(false)
, moved(false)
, button_press_shift(false)
, button_press_ctrl(false)
, button_press_alt(false)
, cycling_wrap(true)
, _describer(NULL)
{
// cursors in select context
// selection handles
}
//static gint xp = 0, yp = 0; // where drag started
//static gint tolerance = 0;
//static bool within_tolerance = false;
static bool is_cycling = false;
static bool moved_while_cycling = false;
SelectTool::~SelectTool() {
this->enableGrDrag(false);
if (this->grabbed) {
}
delete this->_seltrans;
delete this->_describer;
this->_describer = NULL;
if (CursorSelectDragging) {
#if GTK_CHECK_VERSION(3,0,0)
#else
#endif
}
if (CursorSelectMouseover) {
#if GTK_CHECK_VERSION(3,0,0)
#else
#endif
}
}
void SelectTool::setup() {
desktop->messageStack(),
_("No objects selected. Click, Shift+click, Alt+scroll mouse on top of objects, or drag around objects to select.")
);
sp_event_context_read(this, "show");
sp_event_context_read(this, "transform");
this->enableGrDrag();
}
}
if (path == "show") {
} else {
}
}
}
bool SelectTool::sp_select_context_abort() {
if (this->dragging) {
if (this->moved) { // cancel dragging an object
drag_escaped = 1;
if (this->item) {
// only undo if the item is still valid
}
} else if (this->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(this)->desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Move canceled."));
return true;
}
} else {
rb_escaped = 1;
SP_EVENT_CONTEXT(this)->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
|| !( current_group
{
}
}
}
}
// make sure we still have valid objects to move around
this->sp_select_context_abort();
}
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 this->item:
if (this->item) {
}
rb_escaped = drag_escaped = 0;
if (this->grabbed) {
}
}
// right click; do not eat it so that right-click menu can appear, but cancel dragging & rubberband
this->sp_select_context_abort();
}
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) {
}
return ret;
}
void SelectTool::sp_select_context_cycle_through_items(Inkscape::Selection *selection, GdkEventScroll *scroll_event, bool shift_pressed) {
if (!this->cycling_cur_item) {
return;
}
// Deactivate current item
}
// Find next item and activate it
next = this->cycling_items;
} else {
}
if (next) {
this->cycling_cur_item = next;
}
if (shift_pressed) {
} else {
}
}
void SelectTool::sp_select_context_reset_opacities() {
if (item) {
} else {
}
}
g_list_free(this->cycling_items);
g_list_free(this->cycling_items_cmp);
this->cycling_items = NULL;
this->cycling_items_selected_before = NULL;
this->cycling_cur_item = NULL;
this->cycling_items_cmp = NULL;
}
// make sure we still have valid objects to move around
this->sp_select_context_abort();
}
case GDK_2BUTTON_PRESS:
if (dynamic_cast<SPGroup *>(clicked_item) && !dynamic_cast<SPBox3D *>(clicked_item)) { // enter group if it's not a 3D box
this->dragging = false;
} else { // switch tool
}
} else {
}
}
break;
case GDK_BUTTON_PRESS:
// save drag origin
within_tolerance = true;
}
if (this->grabbed) {
}
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
this->sp_select_context_abort();
}
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 (this->button_press_ctrl || (this->button_press_alt && !this->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
}
if (this->dragging) {
/* 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
this->defaultMessageContext()->clear();
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
if (!this->moved) {
{
}
}
// 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
&& !this->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 {
this->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("<b>Draw over</b> objects to select them; release <b>Alt</b> to switch to rubberband selection"));
} else {
this->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:
if (this->dragging) {
if (this->moved) {
// item has been moved
#ifdef WITH_DBUS
#endif
} else if (this->item && !drag_escaped) {
// item has not been moved -> simply a click, do selecting
// with shift, toggle selection
_seltrans->resetState();
} else {
} else if (singleGroup && (singleGroup->layerMode() == SPGroup::LAYER) && single->isAncestorOf(this->item)) {
} else {
_seltrans->resetState();
}
}
} else { // simple or shift click, no previous selection
_seltrans->resetState();
}
}
if (this->item) {
}
} 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();
this->defaultMessageContext()->clear();
// 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
this->button_press_shift = false;
if (this->button_press_ctrl) {
// go into groups, honoring Alt
this->button_press_ctrl = FALSE;
} else {
// don't go into groups, honoring Alt
}
if (item) {
}
} else if ((this->button_press_ctrl || this->button_press_alt) && !rb_escaped && !drag_escaped) { // ctrl+click, alt+click
this->button_press_ctrl = FALSE;
this->button_press_alt = FALSE;
if (item) {
} else {
_seltrans->resetState();
}
}
} else { // click without shift, simply deselect, unless with Alt or something was cancelled
}
rb_escaped = 0;
}
}
}
}
if (this->grabbed) {
}
}
}
this->button_press_shift = false;
this->button_press_ctrl = false;
this->button_press_alt = false;
break;
case GDK_SCROLL: {
if (moved_while_cycling) {
moved_while_cycling = false;
this->sp_select_context_reset_opacities();
}
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
SPItem *tmp_cur_item = this->cycling_cur_item ? dynamic_cast<SPItem *>(static_cast<SPObject *>(this->cycling_cur_item->data)) : NULL;
g_list_free(this->cycling_items);
this->cycling_items = NULL;
this->cycling_cur_item = NULL;
}
/* 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 = this->cycling_items, l2 = this->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 (item) {
//if (!shift_pressed && !g_list_find(this->cycling_items_selected_before, item) && selection->includes(item))
}
}
}
// ... clear the lists ...
g_list_free(this->cycling_items_cmp);
this->cycling_items_cmp = NULL;
this->cycling_items_selected_before = NULL;
this->cycling_cur_item = NULL;
// ... and rebuild them with the new items.
if (item) {
// already selected items are stored separately, too
}
} else {
}
}
// 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)) {
this->defaultMessageContext()->clear();
// 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)) {
} else {
}
} else { // no alt
if (MOD__SHIFT(event)) {
} else {
}
}
}
break;
case GDK_KEY_Up: // move selection up
case GDK_KEY_KP_Up:
if (MOD__SHIFT(event)) {
} else {
}
} else { // no alt
if (MOD__SHIFT(event)) {
} else {
}
}
}
break;
case GDK_KEY_Right: // move selection right
case GDK_KEY_KP_Right:
if (MOD__SHIFT(event)) {
} else {
}
} else { // no alt
if (MOD__SHIFT(event)) {
} else {
}
}
}
break;
case GDK_KEY_Down: // move selection down
case GDK_KEY_KP_Down:
if (MOD__SHIFT(event)) {
} else {
}
} else { // no alt
if (MOD__SHIFT(event)) {
} else {
}
}
}
break;
case GDK_KEY_Escape:
if (!this->sp_select_context_abort()) {
}
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 ( (clickedGroup && (clickedGroup->layerMode() != SPGroup::LAYER)) || dynamic_cast<SPBox3D *>(clicked_item)) { // enter group or a 3D box
} else {
this->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)) {
this->defaultMessageContext()->clear();
}
|| (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) {
// quit cycle-selection and reset opacities
if (is_cycling) {
this->sp_select_context_reset_opacities();
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 (desktop->getCanvas()));
//gdk_window_set_cursor(window, event_context->cursor);
}
break;
}
default:
break;
}
if (!ret) {
}
return ret;
}
}
}
}
/*
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 :