From: Diederik van Lierop Date: Wed, 30 Dec 2009 14:34:51 +0000 (+0100) Subject: Small snap bug has been eliminated, flowed text snapping now uses baseline, replacing... X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=41e6b2681e6a61f7e0b79f06787ad76d397bf0bd;p=inkscape.git Small snap bug has been eliminated, flowed text snapping now uses baseline, replacing tabs by spaces, removing some commented lines --- diff --git a/src/connector-context.cpp b/src/connector-context.cpp index c5c23a734..0fc9de9d0 100644 --- a/src/connector-context.cpp +++ b/src/connector-context.cpp @@ -625,7 +625,6 @@ sp_connector_context_item_handler(SPEventContext *event_context, SPItem *item, G { spcc_reset_colors(cc); cc->state = SP_CONNECTOR_CONTEXT_IDLE; -// sp_event_context_snap_window_closed(event_context); } if (cc->state != SP_CONNECTOR_CONTEXT_IDLE) { // Doing something else like rerouting. @@ -757,7 +756,7 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const switch (cc->state) { case SP_CONNECTOR_CONTEXT_STOP: - /* This is allowed, if we just cancelled curve */ + /* This is allowed, if we just canceled curve */ case SP_CONNECTOR_CONTEXT_IDLE: { if ( cc->npoints == 0 ) { @@ -772,8 +771,6 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const // Test whether we clicked on a connection point cc->sid = conn_pt_handle_test(cc, p); -// sp_event_context_snap_window_open(event_context); - if (!cc->sid) { // This is the first point, so just snap it to the grid // as there's no other points to go off. @@ -800,7 +797,6 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const } cc_set_active_conn(cc, cc->newconn); cc->state = SP_CONNECTOR_CONTEXT_IDLE; -// sp_event_context_snap_window_closed(event_context); ret = TRUE; break; } @@ -819,7 +815,6 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const cc_connector_rerouting_finish(cc, &p); cc->state = SP_CONNECTOR_CONTEXT_IDLE; -// sp_event_context_snap_window_closed(event_context); // Don't set ret to TRUE, so we drop through to the // parent handler which will open the context menu. @@ -827,7 +822,6 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const else if (cc->npoints != 0) { spcc_connector_finish(cc); cc->state = SP_CONNECTOR_CONTEXT_IDLE; -// sp_event_context_snap_window_closed(event_context); ret = TRUE; } } @@ -879,7 +873,6 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const if ( cc->selected_handle ) { -// sp_event_context_snap_window_open(event_context); cc->state = SP_CONNECTOR_CONTEXT_DRAGGING; cc->selection->set( SP_OBJECT( cc->active_shape ) ); } @@ -1047,7 +1040,6 @@ connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton con } cc_set_active_conn(cc, cc->newconn); cc->state = SP_CONNECTOR_CONTEXT_IDLE; -// sp_event_context_snap_window_closed(event_context); break; } case SP_CONNECTOR_CONTEXT_REROUTING: @@ -1057,7 +1049,6 @@ connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton con sp_document_ensure_up_to_date(doc); cc->state = SP_CONNECTOR_CONTEXT_IDLE; -// sp_event_context_snap_window_closed(event_context); return TRUE; break; } @@ -1077,10 +1068,8 @@ connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton con if (!cc->within_tolerance) { -// sp_event_context_snap_window_open(event_context); m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); sp_knot_set_position(cc->selected_handle, p, 0); -// sp_event_context_snap_window_closed(event_context); ConnectionPoint& cp = cc->connpthandles[cc->selected_handle]; cp.pos = p * sp_item_dt2i_affine(cc->active_shape); cc->active_shape->avoidRef->updateConnectionPoint(cp); @@ -1092,11 +1081,9 @@ connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton con case SP_CONNECTOR_CONTEXT_NEWCONNPOINT: -// sp_event_context_snap_window_open( event_context ); m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); sp_knot_set_position(cc->selected_handle, p, 0); -// sp_event_context_snap_window_closed(event_context); ConnectionPoint cp; cp.type = ConnPointUserDefined; @@ -1137,7 +1124,6 @@ connector_handle_key_press(SPConnectorContext *const cc, guint const keyval) if (cc->npoints != 0) { spcc_connector_finish(cc); cc->state = SP_CONNECTOR_CONTEXT_IDLE; -// sp_event_context_snap_window_closed(SP_EVENT_CONTEXT(cc)); ret = TRUE; } break; @@ -1152,7 +1138,6 @@ connector_handle_key_press(SPConnectorContext *const cc, guint const keyval) sp_document_undo(doc); cc->state = SP_CONNECTOR_CONTEXT_IDLE; -// sp_event_context_snap_window_closed(SP_EVENT_CONTEXT(cc)); desktop->messageStack()->flash( Inkscape::NORMAL_MESSAGE, _("Connector endpoint drag cancelled.")); ret = TRUE; @@ -1160,7 +1145,6 @@ connector_handle_key_press(SPConnectorContext *const cc, guint const keyval) else if (cc->npoints != 0) { // if drawing, cancel, otherwise pass it up for deselecting cc->state = SP_CONNECTOR_CONTEXT_STOP; -// sp_event_context_snap_window_closed(SP_EVENT_CONTEXT(cc)); spcc_reset_colors(cc); ret = TRUE; } @@ -1200,10 +1184,8 @@ connector_handle_key_press(SPConnectorContext *const cc, guint const keyval) if (!cc->within_tolerance) { -// sp_event_context_snap_window_open(event_context); m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); sp_knot_set_position(cc->selected_handle, p, 0); -// sp_event_context_snap_window_closed(event_context); ConnectionPoint& cp = cc->connpthandles[cc->selected_handle]; cp.pos = p * sp_item_dt2i_affine(cc->active_shape); cc->active_shape->avoidRef->updateConnectionPoint(cp); @@ -1229,13 +1211,10 @@ connector_handle_key_press(SPConnectorContext *const cc, guint const keyval) SnapManager &m = desktop->namedview->snap_manager; m.setup(desktop); Geom::Point p = cc->selected_handle->pos; -// SPEventContext* event_context = SP_EVENT_CONTEXT( cc ); -// sp_event_context_snap_window_open( event_context ); m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); sp_knot_set_position(cc->selected_handle, p, 0); -// sp_event_context_snap_window_closed(event_context); ConnectionPoint cp; cp.type = ConnPointUserDefined; @@ -1571,7 +1550,6 @@ endpt_handler(SPKnot */*knot*/, GdkEvent *event, SPConnectorContext *cc) cc->clickedhandle = cc->active_handle; cc_clear_active_conn(cc); cc->state = SP_CONNECTOR_CONTEXT_REROUTING; -// sp_event_context_snap_window_open(SP_EVENT_CONTEXT(cc)); // Disconnect from attached shape unsigned ind = (cc->active_handle == cc->endpt_handle[0]) ? 0 : 1; diff --git a/src/display/snap-indicator.cpp b/src/display/snap-indicator.cpp index 20ea7d58c..fdea9cbbf 100644 --- a/src/display/snap-indicator.cpp +++ b/src/display/snap-indicator.cpp @@ -46,11 +46,10 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const p) g_assert(_desktop != NULL); - /* Commented out for now, because this might hide any snapping bug! if (!p.getSnapped()) { - return; // If we haven't snapped, then it is of no use to draw a snapindicator + g_warning("No snapping took place, so no snap target will be displayed"); + return; // If we haven't snapped, then it is of no use to draw a snapindicator } - */ Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool value = prefs->getBool("/options/snapindicator/value", true); diff --git a/src/event-context.cpp b/src/event-context.cpp index 9b846f2b7..918e3d419 100644 --- a/src/event-context.cpp +++ b/src/event-context.cpp @@ -174,7 +174,7 @@ sp_event_context_dispose(GObject *object) } if (ec->_delayed_snap_event) { - delete ec->_delayed_snap_event; + delete ec->_delayed_snap_event; } G_OBJECT_CLASS(parent_class)->dispose(object); @@ -374,9 +374,9 @@ static gint sp_event_context_private_root_handler(SPEventContext *event_context, switch (event->button.button) { case 1: if (event_context->space_panning) { - // When starting panning, make sure there are no snap events pending because these might disable the panning again - sp_event_context_discard_delayed_snap_event(event_context); - panning = 1; + // When starting panning, make sure there are no snap events pending because these might disable the panning again + sp_event_context_discard_delayed_snap_event(event_context); + panning = 1; sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), GDK_KEY_RELEASE_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK, NULL, event->button.time-1); @@ -387,9 +387,9 @@ static gint sp_event_context_private_root_handler(SPEventContext *event_context, if (event->button.state & GDK_SHIFT_MASK) { zoom_rb = 2; } else { - // When starting panning, make sure there are no snap events pending because these might disable the panning again - sp_event_context_discard_delayed_snap_event(event_context); - panning = 2; + // When starting panning, make sure there are no snap events pending because these might disable the panning again + sp_event_context_discard_delayed_snap_event(event_context); + panning = 2; sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK, NULL, event->button.time-1); @@ -399,9 +399,9 @@ static gint sp_event_context_private_root_handler(SPEventContext *event_context, case 3: if (event->button.state & GDK_SHIFT_MASK || event->button.state & GDK_CONTROL_MASK) { - // When starting panning, make sure there are no snap events pending because these might disable the panning again - sp_event_context_discard_delayed_snap_event(event_context); - panning = 3; + // When starting panning, make sure there are no snap events pending because these might disable the panning again + sp_event_context_discard_delayed_snap_event(event_context); + panning = 3; sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK, NULL, event->button.time); @@ -421,7 +421,7 @@ static gint sp_event_context_private_root_handler(SPEventContext *event_context, || (panning == 3 && !(event->motion.state & GDK_BUTTON3_MASK)) ) { /* Gdk seems to lose button release for us sometimes :-( */ - panning = 0; + panning = 0; sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time); ret = TRUE; @@ -514,11 +514,11 @@ static gint sp_event_context_private_root_handler(SPEventContext *event_context, } break; case GDK_KEY_PRESS: - { - double const acceleration = prefs->getDoubleLimited("/options/scrollingacceleration/value", 0, 0, 6); - int const key_scroll = prefs->getIntLimited("/options/keyscroll/value", 10, 0, 1000); + { + double const acceleration = prefs->getDoubleLimited("/options/scrollingacceleration/value", 0, 0, 6); + int const key_scroll = prefs->getIntLimited("/options/keyscroll/value", 10, 0, 1000); - switch (get_group0_keyval(&event->key)) { + switch (get_group0_keyval(&event->key)) { // GDK insists on stealing these keys (F1 for no idea what, tab for cycling widgets // in the editing window). So we resteal them back and run our regular shortcut // invoker on them. @@ -545,11 +545,11 @@ static gint sp_event_context_private_root_handler(SPEventContext *event_context, break; case GDK_Q: case GDK_q: - if (desktop->quick_zoomed()) { - ret = TRUE; - } + if (desktop->quick_zoomed()) { + ret = TRUE; + } if (!MOD__SHIFT && !MOD__CTRL && !MOD__ALT) { - desktop->zoom_quick(true); + desktop->zoom_quick(true); ret = TRUE; } break; @@ -632,7 +632,7 @@ static gint sp_event_context_private_root_handler(SPEventContext *event_context, default: break; } - } + } break; case GDK_KEY_RELEASE: switch (get_group0_keyval(&event->key)) { @@ -651,8 +651,8 @@ static gint sp_event_context_private_root_handler(SPEventContext *event_context, break; case GDK_Q: case GDK_q: - if (desktop->quick_zoomed()) { - desktop->zoom_quick(false); + if (desktop->quick_zoomed()) { + desktop->zoom_quick(false); ret = TRUE; } break; @@ -907,24 +907,24 @@ gint sp_event_context_root_handler(SPEventContext * event_context, GdkEvent * event) { switch (event->type) { - case GDK_MOTION_NOTIFY: - sp_event_context_snap_delay_handler(event_context, NULL, NULL, (GdkEventMotion *)event, DelayedSnapEvent::EVENTCONTEXT_ROOT_HANDLER); - break; - case GDK_BUTTON_RELEASE: - if (event_context->_delayed_snap_event) { - // If we have any pending snapping action, then invoke it now - sp_event_context_snap_watchdog_callback(event_context->_delayed_snap_event); - } - break; - case GDK_BUTTON_PRESS: + case GDK_MOTION_NOTIFY: + sp_event_context_snap_delay_handler(event_context, NULL, NULL, (GdkEventMotion *)event, DelayedSnapEvent::EVENTCONTEXT_ROOT_HANDLER); + break; + case GDK_BUTTON_RELEASE: + if (event_context->_delayed_snap_event) { + // If we have any pending snapping action, then invoke it now + sp_event_context_snap_watchdog_callback(event_context->_delayed_snap_event); + } + break; + case GDK_BUTTON_PRESS: case GDK_2BUTTON_PRESS: case GDK_3BUTTON_PRESS: - // Snapping will be on hold if we're moving the mouse at high speeds. When starting - // drawing a new shape we really should snap though. - event_context->desktop->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false); - break; + // Snapping will be on hold if we're moving the mouse at high speeds. When starting + // drawing a new shape we really should snap though. + event_context->desktop->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false); + break; default: - break; + break; } return sp_event_context_virtual_root_handler(event_context, event); @@ -933,9 +933,9 @@ sp_event_context_root_handler(SPEventContext * event_context, GdkEvent * 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; + gint ret = ((SPEventContextClass *) G_OBJECT_GET_CLASS(event_context))->root_handler(event_context, event); + set_event_location(event_context->desktop, event); + return ret; } /** @@ -944,27 +944,27 @@ sp_event_context_virtual_root_handler(SPEventContext * event_context, GdkEvent * 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); - break; - case GDK_BUTTON_RELEASE: - if (event_context->_delayed_snap_event) { - // If we have any pending snapping action, then invoke it now - sp_event_context_snap_watchdog_callback(event_context->_delayed_snap_event); - } - break; - /*case GDK_BUTTON_PRESS: - case GDK_2BUTTON_PRESS: - case GDK_3BUTTON_PRESS: - // Snapping will be on hold if we're moving the mouse at high speeds. When starting - // drawing a new shape we really should snap though. - event_context->desktop->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false); - break; - */ - default: - break; - } + switch (event->type) { + case GDK_MOTION_NOTIFY: + sp_event_context_snap_delay_handler(event_context, item, NULL, (GdkEventMotion *)event, DelayedSnapEvent::EVENTCONTEXT_ITEM_HANDLER); + break; + case GDK_BUTTON_RELEASE: + if (event_context->_delayed_snap_event) { + // If we have any pending snapping action, then invoke it now + sp_event_context_snap_watchdog_callback(event_context->_delayed_snap_event); + } + break; + /*case GDK_BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + case GDK_3BUTTON_PRESS: + // Snapping will be on hold if we're moving the mouse at high speeds. When starting + // drawing a new shape we really should snap though. + event_context->desktop->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false); + break; + */ + default: + break; + } return sp_event_context_virtual_item_handler(event_context, item, event); } @@ -972,15 +972,15 @@ sp_event_context_item_handler(SPEventContext * event_context, SPItem * item, Gdk 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 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); - } else { - set_event_location(event_context->desktop, event); - } + if (! ret) { + ret = sp_event_context_virtual_root_handler(event_context, event); + } else { + set_event_location(event_context->desktop, event); + } - return ret; + return ret; } /** @@ -1174,124 +1174,124 @@ event_context_print_event_info(GdkEvent *event, bool print_return) { void sp_event_context_snap_delay_handler(SPEventContext *ec, SPItem* const item, SPKnot* const knot, GdkEventMotion *event, DelayedSnapEvent::DelayedSnapEventOrigin origin) { - static guint32 prev_time; - static boost::optional prev_pos; + static guint32 prev_time; + static boost::optional prev_pos; - // Snapping occurs when dragging with the left mouse button down, or when hovering e.g. in the pen tool with left mouse button up + // 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 - // Inkscape will get stuck in an unresponsive state + // Inkscape will get stuck in an unresponsive state bool const c3 = tools_isactive(ec->desktop, TOOLS_CALLIGRAPHIC); // The snap delay will repeat the last motion event, which will lead to // erroneous points in the calligraphy context. And because we don't snap // in this context, we might just as well disable the snap delay all together if (c1 || c2 || c3) { - // Make sure that we don't send any pending snap events to a context if we know in advance - // that we're not going to snap any way (e.g. while scrolling with middle mouse button) - // Any motion event might affect the state of the context, leading to unexpected behavior - sp_event_context_discard_delayed_snap_event(ec); + // Make sure that we don't send any pending snap events to a context if we know in advance + // that we're not going to snap any way (e.g. while scrolling with middle mouse button) + // Any motion event might affect the state of the context, leading to unexpected behavior + sp_event_context_discard_delayed_snap_event(ec); } else if (ec->desktop && ec->desktop->namedview->snap_manager.snapprefs.getSnapEnabledGlobally()) { - // Snap when speed drops below e.g. 0.02 px/msec, or when no motion events have occurred for some period. - // i.e. snap when we're at stand still. A speed threshold enforces snapping for tablets, which might never - // be fully at stand still and might keep spitting out motion events. - ec->desktop->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(true); // put snapping on hold - - Geom::Point event_pos(event->x, event->y); - guint32 event_t = gdk_event_get_time ( (GdkEvent *) event ); - - if (prev_pos) { - Geom::Coord dist = Geom::L2(event_pos - *prev_pos); - guint32 delta_t = event_t - prev_time; - gdouble speed = delta_t > 0 ? dist/delta_t : 1000; - //std::cout << "Mouse speed = " << speed << " px/msec " << std::endl; - if (speed > 0.02) { // Jitter threshold, might be needed for tablets - // We're moving fast, so postpone any snapping until the next GDK_MOTION_NOTIFY event. We - // will keep on postponing the snapping as long as the speed is high. - // We must snap at some point in time though, so set a watchdog timer at some time from - // 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, 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 - // speed threshold is ineffective. In the extreme case the delay is set to zero, and snapping will - // be immediate, as it used to be in the old days ;-). - } else { // Speed is very low, so we're virtually at stand still - // But if we're really standing still, then we should snap now. We could use some low-pass filtering, - // otherwise snapping occurs for each jitter movement. For this filtering we'll leave the watchdog to expire, - // 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 by this - ec->_delayed_snap_event = new DelayedSnapEvent(ec, item, knot, 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, event, origin); - } - - prev_pos = event_pos; - prev_time = event_t; + // Snap when speed drops below e.g. 0.02 px/msec, or when no motion events have occurred for some period. + // i.e. snap when we're at stand still. A speed threshold enforces snapping for tablets, which might never + // be fully at stand still and might keep spitting out motion events. + ec->desktop->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(true); // put snapping on hold + + Geom::Point event_pos(event->x, event->y); + guint32 event_t = gdk_event_get_time ( (GdkEvent *) event ); + + if (prev_pos) { + Geom::Coord dist = Geom::L2(event_pos - *prev_pos); + guint32 delta_t = event_t - prev_time; + gdouble speed = delta_t > 0 ? dist/delta_t : 1000; + //std::cout << "Mouse speed = " << speed << " px/msec " << std::endl; + if (speed > 0.02) { // Jitter threshold, might be needed for tablets + // We're moving fast, so postpone any snapping until the next GDK_MOTION_NOTIFY event. We + // will keep on postponing the snapping as long as the speed is high. + // We must snap at some point in time though, so set a watchdog timer at some time from + // 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, 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 + // speed threshold is ineffective. In the extreme case the delay is set to zero, and snapping will + // be immediate, as it used to be in the old days ;-). + } else { // Speed is very low, so we're virtually at stand still + // But if we're really standing still, then we should snap now. We could use some low-pass filtering, + // otherwise snapping occurs for each jitter movement. For this filtering we'll leave the watchdog to expire, + // 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); + } // 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, event, origin); + } + + prev_pos = event_pos; + prev_time = event_t; } } 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); - - if (dse == NULL) { - // This might occur when this method is called directly, i.e. not through the timer - // E.g. on GDK_BUTTON_RELEASE in sp_event_context_root_handler() - return FALSE; - } - - SPEventContext *ec = dse->getEventContext(); - if (ec == NULL || ec->desktop == NULL) { - return false; - } - - SPDesktop *dt = ec->desktop; - dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false); - - 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(); - 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(); - if (knot && SP_IS_KNOT(knot)) { - sp_knot_handler_request_position(dse->getEvent(), knot); - } - } - break; - default: - g_warning("Origin of snap-delay event has not been defined!;"); - break; - } - - ec->_delayed_snap_event = NULL; - delete dse; - - return FALSE; //Kills the timer and stops it from executing this callback over and over again. + // Snap NOW! For this the "postponed" flag will be reset and the last motion event will be repeated + DelayedSnapEvent *dse = reinterpret_cast(data); + + if (dse == NULL) { + // This might occur when this method is called directly, i.e. not through the timer + // E.g. on GDK_BUTTON_RELEASE in sp_event_context_root_handler() + return FALSE; + } + + SPEventContext *ec = dse->getEventContext(); + if (ec == NULL || ec->desktop == NULL) { + return false; + } + + SPDesktop *dt = ec->desktop; + dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false); + + 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(); + 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(); + if (knot && SP_IS_KNOT(knot)) { + sp_knot_handler_request_position(dse->getEvent(), knot); + } + } + break; + default: + g_warning("Origin of snap-delay event has not been defined!;"); + break; + } + + ec->_delayed_snap_event = NULL; + delete dse; + + return FALSE; //Kills the timer and stops it from executing this callback over and over again. } void sp_event_context_discard_delayed_snap_event(SPEventContext *ec) { - delete ec->_delayed_snap_event; - ec->_delayed_snap_event = NULL; + delete ec->_delayed_snap_event; + ec->_delayed_snap_event = NULL; } diff --git a/src/nodepath.cpp b/src/nodepath.cpp index 8f17ae013..1881dd72b 100644 --- a/src/nodepath.cpp +++ b/src/nodepath.cpp @@ -1367,7 +1367,7 @@ static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, * must provide that information. */ // Build a list of the unselected nodes to which the snapper should snap - std::vector > unselected_nodes; + std::vector > unselected_nodes; for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) { Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data; for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) { @@ -1388,39 +1388,39 @@ static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *closest_node = NULL; Geom::Coord closest_dist = NR_HUGE; - if (closest_only) { - for (GList *l = nodepath->selected; l != NULL; l = l->next) { - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; - Geom::Coord dist = Geom::L2(nodepath->drag_origin_mouse - n->origin); - if (dist < closest_dist) { - closest_node = n; - closest_dist = dist; - } - } + if (closest_only) { + for (GList *l = nodepath->selected; l != NULL; l = l->next) { + Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; + Geom::Coord dist = Geom::L2(nodepath->drag_origin_mouse - n->origin); + if (dist < closest_dist) { + closest_node = n; + closest_dist = dist; + } + } } - // Iterate through all selected nodes - m.setup(nodepath->desktop, false, nodepath->item, &unselected_nodes); - for (GList *l = nodepath->selected; l != NULL; l = l->next) { + // Iterate through all selected nodes + m.setup(nodepath->desktop, false, nodepath->item, &unselected_nodes); + for (GList *l = nodepath->selected; l != NULL; l = l->next) { Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; if (!closest_only || n == closest_node) { //try to snap either all selected nodes or only the closest one - Inkscape::SnappedPoint s; - Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP); - if (constrained) { - Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint; - dedicated_constraint.setPoint(n->pos); - s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type, dedicated_constraint, false); - } else { - s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type); - } - - if (s.getSnapped()) { - s.setPointerDistance(Geom::L2(nodepath->drag_origin_mouse - n->origin)); - if (!s.isOtherSnapBetter(best, true)) { - best = s; - best_pt = from_2geom(s.getPoint()) - n->pos; - } - } + Inkscape::SnappedPoint s; + Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP); + if (constrained) { + Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint; + dedicated_constraint.setPoint(n->pos); + s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type, dedicated_constraint, false); + } else { + s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type); + } + + if (s.getSnapped()) { + s.setPointerDistance(Geom::L2(nodepath->drag_origin_mouse - n->origin)); + if (!s.isOtherSnapBetter(best, true)) { + best = s; + best_pt = from_2geom(s.getPoint()) - n->pos; + } + } } } @@ -3955,9 +3955,9 @@ static gboolean node_handle_request(SPKnot *knot, Geom::Point &p, guint state, g Inkscape::SnappedPoint s; if ((state & GDK_SHIFT_MASK) != 0) { - // We will not try to snap when the shift-key is pressed - // so remove the old snap indicator and don't wait for it to time-out - desktop->snapindicator->remove_snaptarget(); + // We will not try to snap when the shift-key is pressed + // so remove the old snap indicator and don't wait for it to time-out + desktop->snapindicator->remove_snaptarget(); } Inkscape::NodePath::Node *othernode = opposite->other; diff --git a/src/selection.cpp b/src/selection.cpp index 828a96968..7b936587c 100644 --- a/src/selection.cpp +++ b/src/selection.cpp @@ -440,7 +440,7 @@ std::vector > Selection::getSnapPoints(SnapPreferenc //Include the transformation origin for snapping //For a selection or group only the overall origin is considered if (snapprefs != NULL && snapprefs->getIncludeItemCenter()) { - p.push_back(std::make_pair(this_item->getCenter(), SNAPSOURCE_ROTATION_CENTER)); + p.push_back(std::make_pair(this_item->getCenter(), SNAPSOURCE_ROTATION_CENTER)); } } @@ -452,12 +452,12 @@ std::vector > Selection::getSnapPointsConvexHull(Sna std::vector > p; for (GSList const *iter = items; iter != NULL; iter = iter->next) { - sp_item_snappoints(SP_ITEM(iter->data), false, p, snapprefs); + sp_item_snappoints(SP_ITEM(iter->data), false, p, snapprefs); } std::vector > pHull; if (!p.empty()) { - std::vector >::iterator i; + std::vector >::iterator i; Geom::RectHull cvh((p.front()).first); for (i = p.begin(); i != p.end(); i++) { // these are the points we get back diff --git a/src/sp-flowtext.cpp b/src/sp-flowtext.cpp index e4259e52c..feb79cd9a 100644 --- a/src/sp-flowtext.cpp +++ b/src/sp-flowtext.cpp @@ -27,7 +27,7 @@ #include "sp-rect.h" #include "text-tag-attributes.h" #include "text-chemistry.h" - +#include "text-editing.h" #include "livarot/Shape.h" @@ -49,6 +49,7 @@ static void sp_flowtext_set(SPObject *object, unsigned key, gchar const *value); static void sp_flowtext_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags); static void sp_flowtext_print(SPItem *item, SPPrintContext *ctx); static gchar *sp_flowtext_description(SPItem *item); +static void sp_flowtext_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs); static NRArenaItem *sp_flowtext_show(SPItem *item, NRArena *arena, unsigned key, unsigned flags); static void sp_flowtext_hide(SPItem *item, unsigned key); @@ -98,6 +99,7 @@ sp_flowtext_class_init(SPFlowtextClass *klass) item_class->bbox = sp_flowtext_bbox; item_class->print = sp_flowtext_print; item_class->description = sp_flowtext_description; + item_class->snappoints = sp_flowtext_snappoints; item_class->show = sp_flowtext_show; item_class->hide = sp_flowtext_hide; } @@ -384,6 +386,20 @@ static gchar *sp_flowtext_description(SPItem *item) return g_strdup_printf(ngettext("Linked flowed text (%d character%s)", "Linked flowed text (%d characters%s)", nChars), nChars, trunc); } +static void sp_flowtext_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const */*snapprefs*/) +{ + // Choose a point on the baseline for snapping from or to, with the horizontal position + // of this point depending on the text alignment (left vs. right) + Inkscape::Text::Layout const *layout = te_get_layout((SPItem *) item); + if (layout != NULL && layout->outputExists()) { + boost::optional pt = layout->baselineAnchorPoint(); + if (pt) { + int type = target ? int(Inkscape::SNAPTARGET_TEXT_BASELINE) : int(Inkscape::SNAPSOURCE_TEXT_BASELINE); + p.push_back(std::make_pair((*pt) * sp_item_i2d_affine(item), type)); + } + } +} + static NRArenaItem * sp_flowtext_show(SPItem *item, NRArena *arena, unsigned/* key*/, unsigned /*flags*/) { diff --git a/src/sp-item.cpp b/src/sp-item.cpp index 9f7157b99..1a5ca6f77 100644 --- a/src/sp-item.cpp +++ b/src/sp-item.cpp @@ -962,11 +962,11 @@ static void sp_item_private_snappoints(SPItem const *item, bool const target, Sn Geom::Point p1, p2; p1 = bbox->min(); p2 = bbox->max(); - int type = target ? int(Inkscape::SNAPSOURCE_CONVEX_HULL_CORNER) : int(Inkscape::SNAPSOURCE_CONVEX_HULL_CORNER); + int type = target ? int(Inkscape::SNAPTARGET_BBOX_CORNER) : int(Inkscape::SNAPSOURCE_BBOX_CORNER); p.push_back(std::make_pair(p1, type)); p.push_back(std::make_pair(Geom::Point(p1[Geom::X], p2[Geom::Y]), type)); p.push_back(std::make_pair(p2, type)); - p.push_back(std::make_pair(Geom::Point(p1[Geom::Y], p2[Geom::X]), type)); + p.push_back(std::make_pair(Geom::Point(p2[Geom::X], p1[Geom::Y]), type)); } }