From ed02d9080768df7e31df52038fb9cccee6bf3286 Mon Sep 17 00:00:00 2001 From: Diederik van Lierop Date: Sun, 14 Feb 2010 09:18:22 +0100 Subject: [PATCH] Implementation of snap delay mechanism for guides --- src/desktop-events.cpp | 29 +++++++---- src/event-context.cpp | 93 ++++++++++++++++++++++++++--------- src/event-context.h | 20 +++++--- src/knot.cpp | 2 +- src/rect-context.cpp | 40 +++++++-------- src/ui/tool/control-point.cpp | 2 +- 6 files changed, 123 insertions(+), 63 deletions(-) diff --git a/src/desktop-events.cpp b/src/desktop-events.cpp index cea478f85..7cf075f26 100644 --- a/src/desktop-events.cpp +++ b/src/desktop-events.cpp @@ -196,7 +196,7 @@ static gint sp_dt_ruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidge // GDK_BUTTON_PRESS, we should not set it back to inactive here. That must be // done by the context. } - default: + default: break; } @@ -205,11 +205,17 @@ static gint sp_dt_ruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidge int sp_dt_hruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidget *dtw) { + if (event->type == GDK_MOTION_NOTIFY) { + sp_event_context_snap_delay_handler(dtw->desktop->event_context, (gpointer) widget, (gpointer) dtw, (GdkEventMotion *)event, DelayedSnapEvent::GUIDE_HRULER); + } return sp_dt_ruler_event(widget, event, dtw, true); } int sp_dt_vruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidget *dtw) { + if (event->type == GDK_MOTION_NOTIFY) { + sp_event_context_snap_delay_handler(dtw->desktop->event_context, (gpointer) widget, (gpointer) dtw, (GdkEventMotion *)event, DelayedSnapEvent::GUIDE_VRULER); + } return sp_dt_ruler_event(widget, event, dtw, false); } @@ -230,7 +236,7 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) SPDesktop *desktop = static_cast(gtk_object_get_data(GTK_OBJECT(item->canvas), "SPDesktop")); switch (event->type) { - case GDK_2BUTTON_PRESS: + case GDK_2BUTTON_PRESS: if (event->button.button == 1) { drag_type = SP_DRAG_NONE; sp_event_context_discard_delayed_snap_event(desktop->event_context); @@ -239,7 +245,7 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) ret = TRUE; } break; - case GDK_BUTTON_PRESS: + case GDK_BUTTON_PRESS: if (event->button.button == 1) { Geom::Point const event_w(event->button.x, event->button.y); Geom::Point const event_dt(desktop->w2d(event_w)); @@ -279,6 +285,8 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) event->motion.y); Geom::Point motion_dt(desktop->w2d(motion_w)); + sp_event_context_snap_delay_handler(desktop->event_context, (gpointer) item, data, (GdkEventMotion *)event, DelayedSnapEvent::GUIDE_HANDLER); + // This is for snapping while dragging existing guidelines. New guidelines, // which are dragged off the ruler, are being snapped in sp_dt_ruler_event SnapManager &m = desktop->namedview->snap_manager; @@ -321,7 +329,7 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) case SP_DRAG_MOVE_ORIGIN: { sp_guide_moveto(*guide, motion_dt, false); - break; + break; } case SP_DRAG_NONE: g_assert_not_reached(); @@ -344,14 +352,14 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) SnapManager &m = desktop->namedview->snap_manager; m.setup(desktop, true, NULL, NULL, guide); if (drag_type == SP_DRAG_MOVE_ORIGIN) { - // If we snap in guideConstrainedSnap() below, then motion_dt will + // If we snap in guideConstrainedSnap() below, then motion_dt will // be forced to be on the guide. If we don't snap however, then // the origin should still be constrained to the guide. So let's // do that explicitly first: - Geom::Line line(guide->point_on_line, guide->angle()); + Geom::Line line(guide->point_on_line, guide->angle()); Geom::Coord t = line.nearestPoint(event_dt); event_dt = line.pointAt(t); - m.guideConstrainedSnap(event_dt, *guide); + m.guideConstrainedSnap(event_dt, *guide); } else { m.guideFreeSnap(event_dt, guide->normal_to_line, drag_type); } @@ -380,8 +388,8 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) } case SP_DRAG_MOVE_ORIGIN: { - sp_guide_moveto(*guide, event_dt, true); - break; + sp_guide_moveto(*guide, event_dt, true); + break; } case SP_DRAG_NONE: g_assert_not_reached(); @@ -397,12 +405,12 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) sp_document_done(sp_desktop_document(desktop), SP_VERB_NONE, _("Delete guide")); } + sp_event_context_discard_delayed_snap_event(desktop->event_context); moved = false; desktop->set_coordinate_status(from_2geom(event_dt)); desktop->setPosition (from_2geom(event_dt)); } drag_type = SP_DRAG_NONE; - sp_event_context_discard_delayed_snap_event(desktop->event_context); sp_canvas_item_ungrab(item, event->button.time); ret=TRUE; } @@ -444,6 +452,7 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) sp_guide_remove(guide); sp_document_done(doc, SP_VERB_NONE, _("Delete guide")); ret = TRUE; + sp_event_context_discard_delayed_snap_event(desktop->event_context); break; } case GDK_Shift_L: diff --git a/src/event-context.cpp b/src/event-context.cpp index 77c10765b..397ec6b80 100644 --- a/src/event-context.cpp +++ b/src/event-context.cpp @@ -43,6 +43,8 @@ #include "shortcuts.h" #include "desktop.h" #include "desktop-handles.h" +#include "desktop-events.h" +#include "widgets/desktop-widget.h" #include "sp-namedview.h" #include "selection.h" #include "file.h" @@ -59,6 +61,7 @@ #include "lpe-tool-context.h" #include "ui/tool/control-point.h" #include "shape-editor.h" +#include "sp-guide.h" static void sp_event_context_class_init(SPEventContextClass *klass); static void sp_event_context_init(SPEventContext *event_context); @@ -137,6 +140,7 @@ static void sp_event_context_init(SPEventContext *event_context) { event_context->space_panning = false; event_context->shape_editor = NULL; event_context->_delayed_snap_event = NULL; + event_context->_dse_callback_in_process = false; } /** @@ -902,7 +906,8 @@ void sp_event_context_deactivate(SPEventContext *ec) { * Calls virtual root_handler(), the main event handling function. */ gint sp_event_context_root_handler(SPEventContext * event_context, - GdkEvent * event) { + GdkEvent * event) +{ switch (event->type) { case GDK_MOTION_NOTIFY: sp_event_context_snap_delay_handler(event_context, NULL, NULL, @@ -931,12 +936,8 @@ gint sp_event_context_root_handler(SPEventContext * event_context, return sp_event_context_virtual_root_handler(event_context, event); } -gint sp_event_context_virtual_root_handler(SPEventContext * event_context, - GdkEvent * event) { - gint - ret = - ((SPEventContextClass *) G_OBJECT_GET_CLASS(event_context))->root_handler( - event_context, event); +gint sp_event_context_virtual_root_handler(SPEventContext * event_context, GdkEvent * event) { + gint ret = ((SPEventContextClass *) G_OBJECT_GET_CLASS(event_context))->root_handler(event_context, event); set_event_location(event_context->desktop, event); return ret; } @@ -948,7 +949,7 @@ gint sp_event_context_item_handler(SPEventContext * event_context, SPItem * item, GdkEvent * event) { switch (event->type) { case GDK_MOTION_NOTIFY: - sp_event_context_snap_delay_handler(event_context, item, NULL, (GdkEventMotion *) event, DelayedSnapEvent::EVENTCONTEXT_ITEM_HANDLER); + sp_event_context_snap_delay_handler(event_context, (gpointer) item, NULL, (GdkEventMotion *) event, DelayedSnapEvent::EVENTCONTEXT_ITEM_HANDLER); break; case GDK_BUTTON_RELEASE: if (event_context->_delayed_snap_event) { @@ -970,12 +971,8 @@ gint sp_event_context_item_handler(SPEventContext * event_context, return sp_event_context_virtual_item_handler(event_context, item, event); } -gint sp_event_context_virtual_item_handler(SPEventContext * event_context, - SPItem * item, GdkEvent * event) { - gint - ret = - ((SPEventContextClass *) G_OBJECT_GET_CLASS(event_context))->item_handler( - event_context, item, event); +gint sp_event_context_virtual_item_handler(SPEventContext * event_context, SPItem * item, GdkEvent * event) { + gint ret = ((SPEventContextClass *) G_OBJECT_GET_CLASS(event_context))->item_handler(event_context, item, event); if (!ret) { ret = sp_event_context_virtual_root_handler(event_context, event); @@ -1156,12 +1153,28 @@ void event_context_print_event_info(GdkEvent *event, bool print_return) { } } +/** + * \brief Analyses the current event, calculates the mouse speed, turns snapping off (temporarily) if the + * mouse speed is above a threshold, and stores the current event such that it can be re-triggered when needed + * (re-triggering is controlled by a watchdog timer) + * + * \param ec Pointer to the event context + * \param dse_item Pointer that store a reference to a canvas or to an item + * \param dse_item2 Another pointer, storing a reference to a knot or controlpoint + * \param event Pointer to the motion event + * \param origin Identifier (enum) specifying where the delay (and the call to this method) were initiated + */ void sp_event_context_snap_delay_handler(SPEventContext *ec, - SPItem* const item, SPKnot* const knot, GdkEventMotion *event, - DelayedSnapEvent::DelayedSnapEventOrigin origin) { + gpointer const dse_item, gpointer const dse_item2, GdkEventMotion *event, + DelayedSnapEvent::DelayedSnapEventOrigin origin) +{ static guint32 prev_time; static boost::optional prev_pos; + if (ec->_dse_callback_in_process) { + return; + } + // Snapping occurs when dragging with the left mouse button down, or when hovering e.g. in the pen tool with left mouse button up bool const c1 = event->state & GDK_BUTTON2_MASK; // We shouldn't hold back any events when other mouse buttons have been bool const c2 = event->state & GDK_BUTTON3_MASK; // pressed, e.g. when scrolling with the middle mouse button; if we do then @@ -1199,7 +1212,7 @@ void sp_event_context_snap_delay_handler(SPEventContext *ec, // now, just in case there's no future motion event that drops under the speed limit (when // stopping abruptly) delete ec->_delayed_snap_event; - ec->_delayed_snap_event = new DelayedSnapEvent(ec, item, knot, + ec->_delayed_snap_event = new DelayedSnapEvent(ec, dse_item, dse_item2, event, origin); // watchdog is reset, i.e. pushed forward in time // If the watchdog expires before a new motion event is received, we will snap (as explained // above). This means however that when the timer is too short, we will always snap and that the @@ -1211,14 +1224,14 @@ void sp_event_context_snap_delay_handler(SPEventContext *ec, // snap, and set a new watchdog again. if (ec->_delayed_snap_event == NULL) { // no watchdog has been set // it might have already expired, so we'll set a new one; the snapping frequency will be limited this way - ec->_delayed_snap_event = new DelayedSnapEvent(ec, item, - knot, event, origin); + ec->_delayed_snap_event = new DelayedSnapEvent(ec, dse_item, + dse_item2, event, origin); } // else: watchdog has been set before and we'll wait for it to expire } } else { // This is the first GDK_MOTION_NOTIFY event, so postpone snapping and set the watchdog g_assert(ec->_delayed_snap_event == NULL); - ec->_delayed_snap_event = new DelayedSnapEvent(ec, item, knot, + ec->_delayed_snap_event = new DelayedSnapEvent(ec, dse_item, dse_item2, event, origin); } @@ -1227,6 +1240,10 @@ void sp_event_context_snap_delay_handler(SPEventContext *ec, } } +/** + * \brief When the snap delay watchdog timer barks, this method will be called and will re-inject the last motion + * event in an appropriate place, with snapping being turned on again + */ gboolean sp_event_context_snap_watchdog_callback(gpointer data) { // Snap NOW! For this the "postponed" flag will be reset and the last motion event will be repeated DelayedSnapEvent *dse = reinterpret_cast (data); @@ -1241,24 +1258,27 @@ gboolean sp_event_context_snap_watchdog_callback(gpointer data) { if (ec == NULL || ec->desktop == NULL) { return false; } + ec->_dse_callback_in_process = true; SPDesktop *dt = ec->desktop; dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false); + // Depending on where the delayed snap event originated from, we will inject it back at it's origin + // The switch below takes care of that and prepares the relevant parameters switch (dse->getOrigin()) { case DelayedSnapEvent::EVENTCONTEXT_ROOT_HANDLER: sp_event_context_virtual_root_handler(ec, dse->getEvent()); break; case DelayedSnapEvent::EVENTCONTEXT_ITEM_HANDLER: { SPItem* item = NULL; - item = dse->getItem(); + item = SP_ITEM(dse->getItem()); if (item && SP_IS_ITEM(item)) { sp_event_context_virtual_item_handler(ec, item, dse->getEvent()); } } break; case DelayedSnapEvent::KNOT_HANDLER: { - SPKnot* knot = dse->getKnot(); + SPKnot* knot = SP_KNOT(dse->getItem2()); if (knot && SP_IS_KNOT(knot)) { sp_knot_handler_request_position(dse->getEvent(), knot); } @@ -1266,10 +1286,35 @@ gboolean sp_event_context_snap_watchdog_callback(gpointer data) { break; case DelayedSnapEvent::CONTROL_POINT_HANDLER: { using Inkscape::UI::ControlPoint; - ControlPoint *point = reinterpret_cast (dse->getKnot()); + ControlPoint *point = reinterpret_cast (dse->getItem2()); point->_eventHandler(dse->getEvent()); } break; + case DelayedSnapEvent::GUIDE_HANDLER: { + gpointer item = dse->getItem(); + gpointer item2 = dse->getItem2(); + if (item && item2) { + g_assert(SP_IS_CANVAS_ITEM(item)); + g_assert(SP_IS_GUIDE(item2)); + sp_dt_guide_event(SP_CANVAS_ITEM(item), dse->getEvent(), item2); + } + } + break; + case DelayedSnapEvent::GUIDE_HRULER: + case DelayedSnapEvent::GUIDE_VRULER: { + gpointer item = dse->getItem(); + gpointer item2 = dse->getItem2(); + if (item && item2) { + g_assert(GTK_IS_WIDGET(item)); + g_assert(SP_IS_DESKTOP_WIDGET(item2)); + if (dse->getOrigin() == DelayedSnapEvent::GUIDE_HRULER) { + sp_dt_hruler_event(GTK_WIDGET(item), dse->getEvent(), SP_DESKTOP_WIDGET(item2)); + } else { + sp_dt_vruler_event(GTK_WIDGET(item), dse->getEvent(), SP_DESKTOP_WIDGET(item2)); + } + } + } + break; default: g_warning("Origin of snap-delay event has not been defined!;"); break; @@ -1278,6 +1323,8 @@ gboolean sp_event_context_snap_watchdog_callback(gpointer data) { ec->_delayed_snap_event = NULL; delete dse; + ec->_dse_callback_in_process = false; + return FALSE; //Kills the timer and stops it from executing this callback over and over again. } diff --git a/src/event-context.h b/src/event-context.h index be06f0a34..76c74e26c 100644 --- a/src/event-context.h +++ b/src/event-context.h @@ -50,11 +50,14 @@ public: EVENTCONTEXT_ROOT_HANDLER, EVENTCONTEXT_ITEM_HANDLER, KNOT_HANDLER, - CONTROL_POINT_HANDLER + CONTROL_POINT_HANDLER, + GUIDE_HANDLER, + GUIDE_HRULER, + GUIDE_VRULER }; - DelayedSnapEvent(SPEventContext *event_context, SPItem* const item, SPKnot* knot, GdkEventMotion const *event, DelayedSnapEvent::DelayedSnapEventOrigin const origin) - : _timer_id(0), _event(NULL), _item(item), _knot(knot), _origin(origin), _event_context(event_context) + DelayedSnapEvent(SPEventContext *event_context, gpointer const dse_item, gpointer dse_item2, GdkEventMotion const *event, DelayedSnapEvent::DelayedSnapEventOrigin const origin) + : _timer_id(0), _event(NULL), _item(dse_item), _item2(dse_item2), _origin(origin), _event_context(event_context) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); double value = prefs->getDoubleLimited("/options/snapdelay/value", 0, 0, 1000); @@ -71,19 +74,19 @@ public: SPEventContext* getEventContext() {return _event_context;} DelayedSnapEventOrigin getOrigin() {return _origin;} GdkEvent* getEvent() {return _event;} - SPItem* getItem() {return _item;} - SPKnot* getKnot() {return _knot;} + gpointer getItem() {return _item;} + gpointer getItem2() {return _item2;} private: guint _timer_id; GdkEvent* _event; - SPItem* _item; - SPKnot* _knot; + gpointer _item; + gpointer _item2; DelayedSnapEventOrigin _origin; SPEventContext* _event_context; }; -void sp_event_context_snap_delay_handler(SPEventContext *ec, SPItem* const item, SPKnot* const knot, GdkEventMotion *event, DelayedSnapEvent::DelayedSnapEventOrigin origin); +void sp_event_context_snap_delay_handler(SPEventContext *ec, gpointer const dse_item, gpointer const dse_item2, GdkEventMotion *event, DelayedSnapEvent::DelayedSnapEventOrigin origin); /** * Base class for Event processors. @@ -124,6 +127,7 @@ struct SPEventContext : public GObject { bool space_panning; DelayedSnapEvent *_delayed_snap_event; + bool _dse_callback_in_process; }; /** diff --git a/src/knot.cpp b/src/knot.cpp index cc26653e5..04520ed22 100644 --- a/src/knot.cpp +++ b/src/knot.cpp @@ -384,7 +384,7 @@ static int sp_knot_handler(SPCanvasItem */*item*/, GdkEvent *event, SPKnot *knot SP_KNOT_DRAGGING, TRUE); } - sp_event_context_snap_delay_handler(knot->desktop->event_context, NULL, knot, (GdkEventMotion *)event, DelayedSnapEvent::KNOT_HANDLER); + sp_event_context_snap_delay_handler(knot->desktop->event_context, NULL, (gpointer) knot, (GdkEventMotion *)event, DelayedSnapEvent::KNOT_HANDLER); sp_knot_handler_request_position(event, knot); moved = TRUE; } diff --git a/src/rect-context.cpp b/src/rect-context.cpp index a3c3ab0b5..7ae27c13d 100644 --- a/src/rect-context.cpp +++ b/src/rect-context.cpp @@ -122,15 +122,15 @@ static void sp_rect_context_init(SPRectContext *rect_context) static void sp_rect_context_finish(SPEventContext *ec) { SPRectContext *rc = SP_RECT_CONTEXT(ec); - SPDesktop *desktop = ec->desktop; + SPDesktop *desktop = ec->desktop; - sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), GDK_CURRENT_TIME); - sp_rect_finish(rc); + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), GDK_CURRENT_TIME); + sp_rect_finish(rc); rc->sel_changed_connection.disconnect(); if (((SPEventContextClass *) parent_class)->finish) { - ((SPEventContextClass *) parent_class)->finish(ec); - } + ((SPEventContextClass *) parent_class)->finish(ec); + } } @@ -396,14 +396,14 @@ static gint sp_rect_context_root_handler(SPEventContext *event_context, GdkEvent break; case GDK_Escape: - if (dragging) { - dragging = false; - sp_event_context_discard_delayed_snap_event(event_context); - // if drawing, cancel, otherwise pass it up for deselecting - sp_rect_cancel(rc); - ret = TRUE; - } - break; + if (dragging) { + dragging = false; + sp_event_context_discard_delayed_snap_event(event_context); + // if drawing, cancel, otherwise pass it up for deselecting + sp_rect_cancel(rc); + ret = TRUE; + } + break; case GDK_space: if (dragging) { @@ -534,8 +534,8 @@ static void sp_rect_finish(SPRectContext *rc) if ( rc->item != NULL ) { SPRect *rect = SP_RECT(rc->item); if (rect->width.computed == 0 || rect->height.computed == 0) { - sp_rect_cancel(rc); // Don't allow the creating of zero sized rectangle, for example when the start and and point snap to the snap grid point - return; + sp_rect_cancel(rc); // Don't allow the creating of zero sized rectangle, for example when the start and and point snap to the snap grid point + return; } SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(rc); @@ -554,14 +554,14 @@ static void sp_rect_finish(SPRectContext *rc) static void sp_rect_cancel(SPRectContext *rc) { - SPDesktop *desktop = SP_EVENT_CONTEXT(rc)->desktop; + SPDesktop *desktop = SP_EVENT_CONTEXT(rc)->desktop; - sp_desktop_selection(desktop)->clear(); - sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), 0); + sp_desktop_selection(desktop)->clear(); + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), 0); if (rc->item != NULL) { - SP_OBJECT(rc->item)->deleteObject(); - rc->item = NULL; + SP_OBJECT(rc->item)->deleteObject(); + rc->item = NULL; } rc->within_tolerance = false; diff --git a/src/ui/tool/control-point.cpp b/src/ui/tool/control-point.cpp index 90d879c66..a03a8b639 100644 --- a/src/ui/tool/control-point.cpp +++ b/src/ui/tool/control-point.cpp @@ -369,7 +369,7 @@ bool ControlPoint::_eventHandler(GdkEvent *event) _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, + (gpointer) this, &event->motion, DelayedSnapEvent::CONTROL_POINT_HANDLER); } return true; -- 2.30.2