From 31bb8269c26a781036448ed8f8cd93cc84fb2118 Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Sun, 29 Nov 2009 16:33:18 +0100 Subject: First GSoC node tool commit to Bazaar (bzr r8846.1.1) --- src/ui/tool/control-point.cpp | 619 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 619 insertions(+) create mode 100644 src/ui/tool/control-point.cpp (limited to 'src/ui/tool/control-point.cpp') diff --git a/src/ui/tool/control-point.cpp b/src/ui/tool/control-point.cpp new file mode 100644 index 000000000..74dd6e31c --- /dev/null +++ b/src/ui/tool/control-point.cpp @@ -0,0 +1,619 @@ +/** @file + * Desktop-bound visual control object - implementation + */ +/* Authors: + * Krzysztof KosiƄski + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#include +#include +#include <2geom/point.h> +#include "ui/tool/control-point.h" +#include "ui/tool/event-utils.h" +#include "preferences.h" +#include "desktop.h" +#include "desktop-handles.h" +#include "event-context.h" +#include "message-context.h" + +namespace Inkscape { +namespace UI { + +// class and member documentation goes here... + +/** + * @class ControlPoint + * @brief Draggable point, the workhorse of on-canvas editing. + * + * Control points (formerly known as knots) are graphical representations of some significant + * point in the drawing. The drawing can be changed by dragging the point and the things that are + * attached to it with the mouse. Example things that could be edited with draggable points + * are gradient stops, the place where text is attached to a path, text kerns, nodes and handles + * in a path, and many more. Control points use signals heavily - read the libsigc++ + * tutorial on the wiki before using this class. + * + * @par Control point signals + * @par + * The control point has several signals which allow you to react to things that happen to it. + * The most important singals are the grabbed, dragged, ungrabbed and moved signals. + * When a drag happens, the order of emission is as follows: + * - signal_grabbed + * - signal_dragged + * - signal_dragged + * - signal_dragged + * - ... + * - signal_dragged + * - signal_ungrabbed + * + * The control point can also respond to clicks and double clicks. On a double click, + * signal_clicked is emitted, followed by signal_doubleclicked. + * + * A few signal usage hints if you can't be bothered to read the tutorial: + * - If you want some other object or a global function to react to signals of a control point + * from some other object, and you want to access the control point that emitted the signal + * in the handler, use sigc::bind like this: + * @code + * void handle_clicked_signal(ControlPoint *point, int button); + * point->signal_clicked.connect( + * sigc::bind<0>( sigc::ptr_fun(handle_clicked_signal), + * point )); + * @endcode + * - You can ignore unneeded parameters using sigc::hide. + * - If you want to get rid of the handlers added by constructors in superclasses, + * use the clear() method: @code signal_clicked.clear(); @endcode + * - To connect at the front of the slot list instead of at the end, use: + * @code + * signal_clicked.slots().push_front( + * sigc::mem_fun(*this, &FunkyPoint::_clickedHandler)); + * @endcode + * - Note that calling slots() does not copy anything. You can disconnect + * and reorder slots by manipulating the elements of the slot list. The returned object is + * of type @verbatim (signal type)::slot_list @endverbatim. + * + * @par Which method to override? + * @par + * You might wonder which hook to use when you want to do things when the point is relocated. + * Here are some tips: + * - If the point is used to edit an object, override the move() method. + * - If the point can usually be dragged wherever you like but can optionally be constrained + * to axes or the like, add a handler for signal_dragged that modifies its new + * position argument. + * - If the point has additional canvas items tied to it (like handle lines), override + * the setPosition() method. + */ + +/** + * @var ControlPoint::signal_dragged + * Emitted while dragging, but before moving the knot to new position. + * Old position will always be the same as position() - there are two parameters + * only for convenience. + * - First parameter: old position, always equal to position() + * - Second parameter: new position (after drag). This is passed as a non-const reference, + * so you can change it from the handler - that's how constrained dragging is implemented. + * - Third parameter: motion event + */ + +/** + * @var ControlPoint::signal_clicked + * Emitted when the control point is clicked, at mouse button release. The parameter contains + * the event that caused the signal to be emitted. Your signal handler should return true + * if the click had some effect. If it did nothing, return false. Improperly handling this signal + * can cause the context menu not to appear when a control point is right-clicked. + */ + +/** + * @var ControlPoint::signal_doubleclicked + * Emitted when the control point is doubleclicked, at mouse button release. The parameter + * contains the event that caused the signal to be emitted. Your signal handler should return true + * if the double click had some effect. If it did nothing, return false. + */ + +/** + * @var ControlPoint::signal_grabbed + * Emitted when the control point is grabbed and a drag starts. The parameter contains + * the causing event. Return true to prevent further processing. Because all control points + * handle drag tolerance, signal_dragged will be emitted immediately after this signal + * to move the point to its new position. + */ + +/** + * @var ControlPoint::signal_ungrabbed + * Emitted when the control point finishes a drag. The parameter contains the event which + * caused the signal, but it can be NULL if the grab was broken. + */ + +/** + * @enum ControlPoint::State + * Enumeration representing the possible states of the control point, used to determine + * its appearance. + * @var ControlPoint::STATE_NORMAL + * Normal state + * @var ControlPoint::STATE_MOUSEOVER + * Mouse is hovering over the control point + * @var ControlPoint::STATE_CLICKED + * First mouse button pressed over the control point + */ + +// Default colors for control points +static ControlPoint::ColorSet default_color_set = { + {0xffffff00, 0x01000000}, // normal fill, stroke + {0xff0000ff, 0x01000000}, // mouseover fill, stroke + {0x0000ffff, 0x01000000} // clicked fill, stroke +}; + +/** Holds the currently mouseovered control point. */ +ControlPoint *ControlPoint::mouseovered_point = 0; + +/** Emitted when the mouseovered point changes. The parameter is the new mouseovered point. + * When a point ceases to be mouseovered, the parameter will be NULL. */ +sigc::signal ControlPoint::signal_mouseover_change; + +/** Stores the window point over which the cursor was during the last mouse button press */ +Geom::Point ControlPoint::_drag_event_origin(Geom::infinity(), Geom::infinity()); + +/** Stores the desktop point from which the last drag was initiated */ +Geom::Point ControlPoint::_drag_origin(Geom::infinity(), Geom::infinity()); + +/** Events which should be captured when a handle is being dragged. */ +int const ControlPoint::_grab_event_mask = (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_KEY_PRESS_MASK | + GDK_KEY_RELEASE_MASK); + +bool ControlPoint::_drag_initiated = false; +bool ControlPoint::_event_grab = false; + +/** A color set which you can use to create an invisible control that can still receive events. + * @relates ControlPoint */ +ControlPoint::ColorSet invisible_cset = { + {0x00000000, 0x00000000}, + {0x00000000, 0x00000000}, + {0x00000000, 0x00000000} +}; + +/** + * Create a regular control point. + * Derive to have constructors with a reasonable number of parameters. + * + * @param d Desktop for this control + * @param initial_pos Initial position of the control point in desktop coordinates + * @param anchor Where is the control point rendered relative to its desktop coordinates + * @param shape Shape of the control point: square, diamond, circle... + * @param size Pixel size of the visual representation + * @param cset Colors of the point + * @param group The canvas group the point's canvas item should be created in + */ +ControlPoint::ControlPoint(SPDesktop *d, Geom::Point const &initial_pos, + Gtk::AnchorType anchor, SPCtrlShapeType shape, + unsigned int size, ColorSet *cset, SPCanvasGroup *group) + : _desktop (d) + , _canvas_item (NULL) + , _cset (cset ? cset : &default_color_set) + , _state (STATE_NORMAL) + , _position (initial_pos) +{ + _canvas_item = sp_canvas_item_new( + group ? group : sp_desktop_controls (_desktop), SP_TYPE_CTRL, + "anchor", (GtkAnchorType) anchor, "size", (gdouble) size, "shape", shape, + "filled", TRUE, "fill_color", _cset->normal.fill, + "stroked", TRUE, "stroke_color", _cset->normal.stroke, + "mode", SP_CTRL_MODE_XOR, NULL); + _commonInit(); +} + +/** + * Create a control point with a pixbuf-based visual representation. + * + * @param d Desktop for this control + * @param initial_pos Initial position of the control point in desktop coordinates + * @param anchor Where is the control point rendered relative to its desktop coordinates + * @param pixbuf Pixbuf to be used as the visual representation + * @param cset Colors of the point + * @param group The canvas group the point's canvas item should be created in + */ +ControlPoint::ControlPoint(SPDesktop *d, Geom::Point const &initial_pos, + Gtk::AnchorType anchor, Glib::RefPtr pixbuf, + ColorSet *cset, SPCanvasGroup *group) + : _desktop (d) + , _canvas_item (NULL) + , _cset(cset ? cset : &default_color_set) + , _position (initial_pos) +{ + _canvas_item = sp_canvas_item_new( + group ? group : sp_desktop_controls(_desktop), SP_TYPE_CTRL, + "anchor", (GtkAnchorType) anchor, "size", (gdouble) pixbuf->get_width(), + "shape", SP_CTRL_SHAPE_BITMAP, "pixbuf", pixbuf->gobj(), + "filled", TRUE, "fill_color", _cset->normal.fill, + "stroked", TRUE, "stroke_color", _cset->normal.stroke, + "mode", SP_CTRL_MODE_XOR, NULL); + _commonInit(); +} + +ControlPoint::~ControlPoint() +{ + // avoid storing invalid points in mouseovered_point + if (this == mouseovered_point) { + _clearMouseover(); + } + + g_signal_handler_disconnect(G_OBJECT(_canvas_item), _event_handler_connection); + //sp_canvas_item_hide(_canvas_item); + gtk_object_destroy(_canvas_item); +} + +void ControlPoint::_commonInit() +{ + _event_handler_connection = g_signal_connect(G_OBJECT(_canvas_item), "event", + G_CALLBACK(_event_handler), this); + SP_CTRL(_canvas_item)->moveto(_position); +} + +/** Relocate the control point without side effects. + * Overload this method only if there is an additional graphical representation + * that must be updated (like the lines that connect handles to nodes). If you override it, + * you must also call the superclass implementation of the method. + * @todo Investigate whether this method should be protected */ +void ControlPoint::setPosition(Geom::Point const &pos) +{ + _position = pos; + SP_CTRL(_canvas_item)->moveto(pos); +} + +/** Move the control point to new position with side effects. + * This is called after each drag. Override this method if only some positions make sense + * for a control point (like a point that must always be on a path and can't modify it), + * or when moving a control point changes the positions of other points. */ +void ControlPoint::move(Geom::Point const &pos) +{ + setPosition(pos); +} + +/** Apply an arbitrary affine transformation to a control point. This is used + * by ControlPointSelection, and is important for things like nodes with handles. + * The default implementation simply moves the point according to the transform. */ +void ControlPoint::transform(Geom::Matrix const &m) { + move(position() * m); +} + +bool ControlPoint::visible() const +{ + return sp_canvas_item_is_visible(_canvas_item); +} + +/** Set the visibility of the control point. An invisible point is not drawn on the canvas + * and cannot receive any events. If you want to have an invisible point that can respond + * to events, use invisible_cset as its color set. */ +void ControlPoint::setVisible(bool v) +{ + if (v) sp_canvas_item_show(_canvas_item); + else sp_canvas_item_hide(_canvas_item); +} + +Glib::ustring ControlPoint::format_tip(char const *format, ...) +{ + va_list args; + va_start(args, format); + char *dyntip = g_strdup_vprintf(format, args); + va_end(args); + Glib::ustring ret = dyntip; + g_free(dyntip); + return ret; +} + +unsigned int ControlPoint::_size() const +{ + double ret; + g_object_get(_canvas_item, "size", &ret, NULL); + return static_cast(ret); +} + +SPCtrlShapeType ControlPoint::_shape() const +{ + SPCtrlShapeType ret; + g_object_get(_canvas_item, "shape", &ret, NULL); + return ret; +} + +GtkAnchorType ControlPoint::_anchor() const +{ + GtkAnchorType ret; + g_object_get(_canvas_item, "anchor", &ret, NULL); + return ret; +} + +Glib::RefPtr ControlPoint::_pixbuf() +{ + GdkPixbuf *ret; + g_object_get(_canvas_item, "pixbuf", &ret, NULL); + return Glib::wrap(ret); +} + +// Same for setters. + +void ControlPoint::_setSize(unsigned int size) +{ + g_object_set(_canvas_item, "size", (gdouble) size, NULL); +} + +void ControlPoint::_setShape(SPCtrlShapeType shape) +{ + g_object_set(_canvas_item, "shape", shape, NULL); +} + +void ControlPoint::_setAnchor(GtkAnchorType anchor) +{ + g_object_set(_canvas_item, "anchor", anchor, NULL); +} + +void ControlPoint::_setPixbuf(Glib::RefPtr p) +{ + g_object_set(_canvas_item, "pixbuf", Glib::unwrap(p), NULL); +} + +// re-routes events into the virtual function +int ControlPoint::_event_handler(SPCanvasItem *item, GdkEvent *event, ControlPoint *point) +{ + return point->_eventHandler(event) ? TRUE : FALSE; +} + +// main event callback, which emits all other callbacks. +bool ControlPoint::_eventHandler(GdkEvent *event) +{ + // NOTE the static variables below are shared for all points! + + // offset from the pointer hotspot to the center of the grabbed knot in desktop coords + static Geom::Point pointer_offset; + // number of last doubleclicked button, to be + static unsigned next_release_doubleclick = 0; + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int drag_tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); + + switch(event->type) + { + case GDK_2BUTTON_PRESS: + // store the button number for next release + next_release_doubleclick = event->button.button; + return true; + + case GDK_BUTTON_PRESS: + next_release_doubleclick = 0; + if (event->button.button == 1) { + // mouse click. internally, start dragging, but do not emit signals + // or change position until drag tolerance is exceeded. + _drag_event_origin[Geom::X] = event->button.x; + _drag_event_origin[Geom::Y] = event->button.y; + pointer_offset = _position - _desktop->w2d(_drag_event_origin); + _drag_initiated = false; + // route all events to this handler + sp_canvas_item_grab(_canvas_item, _grab_event_mask, NULL, event->button.time); + _event_grab = true; + _setState(STATE_CLICKED); + } + return true; + + case GDK_MOTION_NOTIFY: + if (held_button<1>(event->motion) && !_desktop->event_context->space_panning) { + bool transferred = false; + if (!_drag_initiated) { + bool t = fabs(event->motion.x - _drag_event_origin[Geom::X]) <= drag_tolerance && + fabs(event->motion.y - _drag_event_origin[Geom::Y]) <= drag_tolerance; + if (t) return true; + + // if we are here, it means the tolerance was just exceeded. + next_release_doubleclick = 0; + _drag_origin = _position; + transferred = signal_grabbed.emit(&event->motion); + // _drag_initiated might change during the above signal emission + if (!_drag_initiated) { + // this guarantees smooth redraws while dragging + sp_canvas_force_full_redraw_after_interruptions(_desktop->canvas, 5); + _drag_initiated = true; + } + } + if (transferred) return true; + // the point was moved beyond the drag tolerance + Geom::Point new_pos = _desktop->w2d(event_point(event->motion)) + pointer_offset; + + // the new position is passed by reference and can be changed in the handlers. + signal_dragged.emit(_position, new_pos, &event->motion); + move(new_pos); + _updateDragTip(&event->motion); // update dragging tip after moving to new position + + _desktop->scroll_to_point(new_pos); + _desktop->set_coordinate_status(_position); + return true; + } + break; + + case GDK_BUTTON_RELEASE: + if (_event_grab) { + sp_canvas_item_ungrab(_canvas_item, event->button.time); + _setMouseover(this, event->button.state); + _event_grab = false; + + if (_drag_initiated) { + sp_canvas_end_forced_full_redraws(_desktop->canvas); + } + + if (event->button.button == next_release_doubleclick) { + _drag_initiated = false; + return signal_doubleclicked.emit(&event->button); + } + if (event->button.button == 1) { + if (_drag_initiated) { + // it is the end of a drag + signal_ungrabbed.emit(&event->button); + _drag_initiated = false; + return true; + } else { + // it is the end of a click + return signal_clicked.emit(&event->button); + } + } + _drag_initiated = false; + } + break; + + case GDK_ENTER_NOTIFY: + _setMouseover(this, event->crossing.state); + return true; + case GDK_LEAVE_NOTIFY: + _clearMouseover(); + return true; + + case GDK_GRAB_BROKEN: + if (!event->grab_broken.keyboard && _event_grab) { + { + signal_ungrabbed.emit(0); + if (_drag_initiated) + sp_canvas_end_forced_full_redraws(_desktop->canvas); + } + _setState(STATE_NORMAL); + _event_grab = false; + _drag_initiated = false; + return true; + } + break; + + // update tips on modifier state change + case GDK_KEY_PRESS: + case GDK_KEY_RELEASE: + if (mouseovered_point != this) return false; + if (_drag_initiated) { + return true; // this prevents the tool from overwriting the drag tip + } else { + unsigned state = state_after_event(event); + if (state != event->key.state) { + // we need to return true if there was a tip available, otherwise the tool's + // handler will process this event and set the tool's message, overwriting + // the point's message + return _updateTip(state); + } + } + break; + + default: break; + } + + return false; +} + +void ControlPoint::_setMouseover(ControlPoint *p, unsigned state) +{ + bool visible = p->visible(); + if (visible) { // invisible points shouldn't get mouseovered + p->_setState(STATE_MOUSEOVER); + } + p->_updateTip(state); + + if (visible && mouseovered_point != p) { + mouseovered_point = p; + signal_mouseover_change.emit(mouseovered_point); + } +} + +bool ControlPoint::_updateTip(unsigned state) +{ + Glib::ustring tip = _getTip(state); + if (!tip.empty()) { + _desktop->event_context->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, + tip.data()); + return true; + } else { + _desktop->event_context->defaultMessageContext()->clear(); + return false; + } +} + +bool ControlPoint::_updateDragTip(GdkEventMotion *event) +{ + if (!_hasDragTips()) return false; + Glib::ustring tip = _getDragTip(event); + if (!tip.empty()) { + _desktop->event_context->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, + tip.data()); + return true; + } else { + _desktop->event_context->defaultMessageContext()->clear(); + return false; + } +} + +void ControlPoint::_clearMouseover() +{ + if (mouseovered_point) { + mouseovered_point->_desktop->event_context->defaultMessageContext()->clear(); + mouseovered_point->_setState(STATE_NORMAL); + mouseovered_point = 0; + signal_mouseover_change.emit(mouseovered_point); + } +} + +/** Transfer the grab to another point. This method allows one to create a draggable point + * that should be dragged instead of the one that received the grabbed signal. + * This is used to implement dragging out handles in the new node tool, for example. + * + * This method will NOT emit the ungrab signal of @c prev_point, because this would complicate + * using it with selectable control points. If you use this method while dragging, you must emit + * the ungrab signal yourself. + * + * Note that this will break horribly if you try to transfer grab between points in different + * desktops, which doesn't make much sense anyway. */ +void ControlPoint::transferGrab(ControlPoint *prev_point, GdkEventMotion *event) +{ + if (!_event_grab) return; + + signal_grabbed.emit(event); + sp_canvas_item_ungrab(prev_point->_canvas_item, event->time); + sp_canvas_item_grab(_canvas_item, _grab_event_mask, NULL, event->time); + + if (!_drag_initiated) { + sp_canvas_force_full_redraw_after_interruptions(_desktop->canvas, 5); + _drag_initiated = true; + } + + prev_point->_setState(STATE_NORMAL); + _setMouseover(this, event->state); +} + +/** + * @brief Change the state of the knot + * Alters the appearance of the knot to match one of the states: normal, mouseover + * or clicked. + */ +void ControlPoint::_setState(State state) +{ + ColorEntry current = {0, 0}; + switch(state) { + case STATE_NORMAL: + current = _cset->normal; break; + case STATE_MOUSEOVER: + current = _cset->mouseover; break; + case STATE_CLICKED: + current = _cset->clicked; break; + }; + _setColors(current); + _state = state; +} +void ControlPoint::_setColors(ColorEntry colors) +{ + g_object_set(_canvas_item, "fill_color", colors.fill, "stroke_color", colors.stroke, NULL); +} + +} // namespace UI +} // namespace Inkscape + +/* + 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:encoding=utf-8:textwidth=99 : -- cgit v1.2.3 From b52865a71a9f83da9719a3ec5f50a4a2cd7cdace Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Sun, 10 Jan 2010 01:46:28 +0100 Subject: * Implement node snapping. * Fix minor bug in linear grow. * Add --fixes. * Move some node selection-related functions to ControlPointSelection. Fixed bugs: - https://launchpad.net/bugs/170561 - https://launchpad.net/bugs/171893 - https://launchpad.net/bugs/182585 - https://launchpad.net/bugs/446773 (bzr r8846.2.9) --- src/ui/tool/control-point.cpp | 81 ++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 35 deletions(-) (limited to 'src/ui/tool/control-point.cpp') diff --git a/src/ui/tool/control-point.cpp b/src/ui/tool/control-point.cpp index 74dd6e31c..0d076a5ab 100644 --- a/src/ui/tool/control-point.cpp +++ b/src/ui/tool/control-point.cpp @@ -12,13 +12,14 @@ #include #include #include <2geom/point.h> -#include "ui/tool/control-point.h" -#include "ui/tool/event-utils.h" -#include "preferences.h" #include "desktop.h" #include "desktop-handles.h" +#include "display/snap-indicator.h" #include "event-context.h" #include "message-context.h" +#include "preferences.h" +#include "ui/tool/control-point.h" +#include "ui/tool/event-utils.h" namespace Inkscape { namespace UI { @@ -397,6 +398,7 @@ bool ControlPoint::_eventHandler(GdkEvent *event) case GDK_MOTION_NOTIFY: if (held_button<1>(event->motion) && !_desktop->event_context->space_panning) { + _desktop->snapindicator->remove_snaptarget(); bool transferred = false; if (!_drag_initiated) { bool t = fabs(event->motion.x - _drag_event_origin[Geom::X]) <= drag_tolerance && @@ -414,48 +416,57 @@ bool ControlPoint::_eventHandler(GdkEvent *event) _drag_initiated = true; } } - if (transferred) return true; - // the point was moved beyond the drag tolerance - Geom::Point new_pos = _desktop->w2d(event_point(event->motion)) + pointer_offset; - - // the new position is passed by reference and can be changed in the handlers. - signal_dragged.emit(_position, new_pos, &event->motion); - move(new_pos); - _updateDragTip(&event->motion); // update dragging tip after moving to new position - - _desktop->scroll_to_point(new_pos); - _desktop->set_coordinate_status(_position); + if (!transferred) { + // dragging in progress + Geom::Point new_pos = _desktop->w2d(event_point(event->motion)) + pointer_offset; + + // the new position is passed by reference and can be changed in the handlers. + signal_dragged.emit(_position, new_pos, &event->motion); + move(new_pos); + _updateDragTip(&event->motion); // update dragging tip after moving to new position + + _desktop->scroll_to_point(new_pos); + _desktop->set_coordinate_status(_position); + sp_event_context_snap_delay_handler(_desktop->event_context, NULL, + reinterpret_cast(this), &event->motion, + DelayedSnapEvent::CONTROL_POINT_HANDLER); + } return true; } break; case GDK_BUTTON_RELEASE: - if (_event_grab) { - sp_canvas_item_ungrab(_canvas_item, event->button.time); - _setMouseover(this, event->button.state); - _event_grab = false; + if (!_event_grab) break; - if (_drag_initiated) { - sp_canvas_end_forced_full_redraws(_desktop->canvas); - } + // TODO I think this "feature" is wrong. + // sp_event_context_snap_watchdog_callback(_desktop->event_context->_delayed_snap_event); + sp_event_context_discard_delayed_snap_event(_desktop->event_context); + _desktop->snapindicator->remove_snaptarget(); + + sp_canvas_item_ungrab(_canvas_item, event->button.time); + _setMouseover(this, event->button.state); + _event_grab = false; - if (event->button.button == next_release_doubleclick) { + if (_drag_initiated) { + sp_canvas_end_forced_full_redraws(_desktop->canvas); + } + + if (event->button.button == next_release_doubleclick) { + _drag_initiated = false; + return signal_doubleclicked.emit(&event->button); + } + if (event->button.button == 1) { + if (_drag_initiated) { + // it is the end of a drag + signal_ungrabbed.emit(&event->button); _drag_initiated = false; - return signal_doubleclicked.emit(&event->button); + return true; + } else { + // it is the end of a click + return signal_clicked.emit(&event->button); } - if (event->button.button == 1) { - if (_drag_initiated) { - // it is the end of a drag - signal_ungrabbed.emit(&event->button); - _drag_initiated = false; - return true; - } else { - // it is the end of a click - return signal_clicked.emit(&event->button); - } - } - _drag_initiated = false; } + _drag_initiated = false; break; case GDK_ENTER_NOTIFY: -- cgit v1.2.3 From c3333fcf1c1d9c820e4bf1bb0d08d4a2941c1644 Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Thu, 14 Jan 2010 16:44:32 +0100 Subject: Re-enable snapping on release, for now. (bzr r8846.2.16) --- src/ui/tool/control-point.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/ui/tool/control-point.cpp') diff --git a/src/ui/tool/control-point.cpp b/src/ui/tool/control-point.cpp index 0d076a5ab..73dd0d19e 100644 --- a/src/ui/tool/control-point.cpp +++ b/src/ui/tool/control-point.cpp @@ -438,8 +438,8 @@ bool ControlPoint::_eventHandler(GdkEvent *event) case GDK_BUTTON_RELEASE: if (!_event_grab) break; - // TODO I think this "feature" is wrong. - // sp_event_context_snap_watchdog_callback(_desktop->event_context->_delayed_snap_event); + // TODO I think snapping on release is wrong, or at least counter-intuitive. + sp_event_context_snap_watchdog_callback(_desktop->event_context->_delayed_snap_event); sp_event_context_discard_delayed_snap_event(_desktop->event_context); _desktop->snapindicator->remove_snaptarget(); -- cgit v1.2.3