From: dvlierop2 Date: Tue, 28 Aug 2007 20:07:38 +0000 (+0000) Subject: Implement snapping of guides while dragging them, and snap TO item centers (we only... X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=5d34a7de8fd25b5ff22b5ca5ae2a631611a18f6f;p=inkscape.git Implement snapping of guides while dragging them, and snap TO item centers (we only had snapping FROM item centers so far) --- diff --git a/src/arc-context.cpp b/src/arc-context.cpp index d5a25b61b..ab6b3a01d 100644 --- a/src/arc-context.cpp +++ b/src/arc-context.cpp @@ -291,7 +291,7 @@ static gint sp_arc_context_root_handler(SPEventContext *event_context, GdkEvent SnapManager const &m = desktop->namedview->snap_manager; motion_dt = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, motion_dt, ac->item).getPoint(); - + sp_arc_drag(ac, motion_dt, event->motion.state); gobble_motion_events(GDK_BUTTON1_MASK); diff --git a/src/attributes.cpp b/src/attributes.cpp index 8ac3c8c74..977ab3483 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -85,6 +85,7 @@ static SPStyleProp const props[] = { {SP_ATTR_INKSCAPE_WINDOW_Y, "inkscape:window-y"}, {SP_ATTR_INKSCAPE_SNAP_BBOX, "inkscape:snap-bbox"}, {SP_ATTR_INKSCAPE_SNAP_NODES, "inkscape:snap-nodes"}, + {SP_ATTR_INKSCAPE_SNAP_GUIDE, "inkscape:snap-guide"}, {SP_ATTR_INKSCAPE_SNAP_CENTER, "inkscape:snap-center"}, {SP_ATTR_INKSCAPE_OBJECT_PATHS, "inkscape:object-paths"}, {SP_ATTR_INKSCAPE_OBJECT_NODES, "inkscape:object-nodes"}, diff --git a/src/attributes.h b/src/attributes.h index 73ecf0252..4a2970830 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -86,6 +86,7 @@ enum SPAttributeEnum { SP_ATTR_INKSCAPE_WINDOW_Y, SP_ATTR_INKSCAPE_SNAP_BBOX, SP_ATTR_INKSCAPE_SNAP_NODES, + SP_ATTR_INKSCAPE_SNAP_GUIDE, SP_ATTR_INKSCAPE_SNAP_CENTER, SP_ATTR_INKSCAPE_OBJECT_PATHS, SP_ATTR_INKSCAPE_OBJECT_NODES, diff --git a/src/desktop-events.cpp b/src/desktop-events.cpp index a10264811..7f9ce0b04 100644 --- a/src/desktop-events.cpp +++ b/src/desktop-events.cpp @@ -30,6 +30,7 @@ #include "message-context.h" #include "xml/repr.h" #include "dialogs/guidelinedialog.h" +#include "snap.h" /* Root item handler */ @@ -165,7 +166,11 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) if (dragging) { NR::Point const motion_w(event->motion.x, event->motion.y); - NR::Point const motion_dt(desktop->w2d(motion_w)); + NR::Point motion_dt(desktop->w2d(motion_w)); + + SnapManager const &m = desktop->namedview->snap_manager; + motion_dt = m.guideSnap(motion_dt, *guide).getPoint(); + sp_guide_moveto(*guide, sp_guide_position_from_pt(guide, motion_dt), false); moved = true; desktop->set_coordinate_status(motion_dt); @@ -178,7 +183,14 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) if (moved) { NR::Point const event_w(event->button.x, event->button.y); - NR::Point const event_dt(desktop->w2d(event_w)); + NR::Point event_dt(desktop->w2d(event_w)); + + //If we don't snap here again, it will end up at the current mouse position + //whereas it might have been at a snapped position a millisecond before. + //See GDK_MOTION_NOTIFY above. Why's that???? + SnapManager const &m = desktop->namedview->snap_manager; + event_dt = m.guideSnap(event_dt, *guide).getPoint(); + if (sp_canvas_world_pt_inside_window(item->canvas, event_w)) { sp_guide_moveto(*guide, sp_guide_position_from_pt(guide, event_dt), true); sp_document_done(sp_desktop_document(desktop), SP_VERB_NONE, diff --git a/src/object-snapper.cpp b/src/object-snapper.cpp index 427b7c507..57937145a 100644 --- a/src/object-snapper.cpp +++ b/src/object-snapper.cpp @@ -28,7 +28,8 @@ Inkscape::ObjectSnapper::ObjectSnapper(SPNamedView const *nv, NR::Coord const d) : Snapper(nv, d), _snap_to_itemnode(true), _snap_to_itempath(true), - _snap_to_bboxnode(true), _snap_to_bboxpath(true), _strict_snapping(true) + _snap_to_bboxnode(true), _snap_to_bboxpath(true), _strict_snapping(true), + _include_item_center(false) { } @@ -41,7 +42,8 @@ Inkscape::ObjectSnapper::ObjectSnapper(SPNamedView const *nv, NR::Coord const d) void Inkscape::ObjectSnapper::_findCandidates(std::list& c, SPObject* r, std::list const &it, - NR::Point const &p) const + NR::Point const &p, + DimensionToSnap const snap_dim) const { if (ThisSnapperMightSnap()) { SPDesktop const *desktop = SP_ACTIVE_DESKTOP; @@ -57,10 +59,15 @@ void Inkscape::ObjectSnapper::_findCandidates(std::list& c, if (i == it.end()) { /* See if the item is within range */ if (SP_IS_GROUP(o)) { - _findCandidates(c, o, it, p); + _findCandidates(c, o, it, p, snap_dim); } else { NR::Maybe b = sp_item_bbox_desktop(SP_ITEM(o)); - if ( b && NR::expand(*b, -getDistance()).contains(p) ) { + NR::Point b_min = b->min(); + NR::Point b_max = b->max(); + double d = getDistance(); + bool withinX = (p[NR::X] >= b_min[NR::X] - d) && (p[NR::X] <= b_max[NR::X] + d); + bool withinY = (p[NR::Y] >= b_min[NR::Y] - d) && (p[NR::Y] <= b_max[NR::Y] + d); + if (snap_dim == SNAP_X && withinX || snap_dim == SNAP_Y && withinY || snap_dim == SNAP_XY && withinX && withinY) { c.push_back(SP_ITEM(o)); } } @@ -75,6 +82,7 @@ void Inkscape::ObjectSnapper::_findCandidates(std::list& c, void Inkscape::ObjectSnapper::_snapNodes(Inkscape::Snapper::PointType const &t, Inkscape::SnappedPoint &s, NR::Point const &p, + DimensionToSnap const snap_dim, std::list const &cand) const { /* FIXME: this seems like a hack. Perhaps Snappers should be @@ -90,6 +98,11 @@ void Inkscape::ObjectSnapper::_snapNodes(Inkscape::Snapper::PointType const &t, } bool p_is_a_node = t & Inkscape::Snapper::SNAPPOINT_NODE; + bool p_is_a_bbox = t & Inkscape::Snapper::SNAPPOINT_BBOX; + bool p_is_a_guide = t & Inkscape::Snapper::SNAPPOINT_GUIDE; + + // A point considered for snapping should be either a node, a bbox corner or a guide. Pick only ONE! + g_assert(!(p_is_a_node && p_is_a_bbox || p_is_a_bbox && p_is_a_guide || p_is_a_node && p_is_a_guide)); for (std::list::const_iterator i = cand.begin(); i != cand.end(); i++) { @@ -117,7 +130,7 @@ void Inkscape::ObjectSnapper::_snapNodes(Inkscape::Snapper::PointType const &t, //Collect all nodes so we can snap to them if (_snap_to_itemnode) { - if (!(_strict_snapping && !p_is_a_node)) { + if (!(_strict_snapping && !p_is_a_node) || p_is_a_guide) { if (curve) { int j = 0; while (SP_CURVE_BPATH(curve)[j].code != NR_END) { @@ -126,13 +139,16 @@ void Inkscape::ObjectSnapper::_snapNodes(Inkscape::Snapper::PointType const &t, points_to_snap_to.push_back(desktop->doc2dt(bp.c(3) * i2doc)); j++; } + if (_include_item_center) { + points_to_snap_to.push_back(root_item->getCenter()); + } } } } //Collect the bounding box's corners so we can snap to them if (_snap_to_bboxnode) { - if (!(_strict_snapping && p_is_a_node)) { + if (!(_strict_snapping && !p_is_a_bbox) || p_is_a_guide) { NR::Maybe b = sp_item_bbox_desktop(root_item, bbox_type); if (b) { for ( unsigned k = 0 ; k < 4 ; k++ ) { @@ -145,9 +161,24 @@ void Inkscape::ObjectSnapper::_snapNodes(Inkscape::Snapper::PointType const &t, //Do the snapping, using all the nodes and corners collected above for (std::list::const_iterator k = points_to_snap_to.begin(); k != points_to_snap_to.end(); k++) { /* Try to snap to this node of the path */ - NR::Coord const dist = NR::L2(*k - p); + NR::Coord dist = NR_HUGE; + NR::Point snapped_point; + switch (snap_dim) { + case SNAP_X: + dist = fabs((*k)[NR::X] - p[NR::X]); + snapped_point = NR::Point((*k)[NR::X], p[NR::Y]); + break; + case SNAP_Y: + dist = fabs((*k)[NR::Y] - p[NR::Y]); + snapped_point = NR::Point(p[NR::X], (*k)[NR::Y]); + break; + case SNAP_XY: + dist = NR::L2(*k - p); + snapped_point = *k; + break; + } if (dist < getDistance() && dist < s.getDistance()) { - s = SnappedPoint(*k, dist); + s = SnappedPoint(snapped_point, dist); } } } @@ -251,12 +282,12 @@ Inkscape::SnappedPoint Inkscape::ObjectSnapper::_doFreeSnap(Inkscape::Snapper::P /* Get a list of all the SPItems that we will try to snap to */ std::list cand; - _findCandidates(cand, sp_document_root(_named_view->document), it, p); + _findCandidates(cand, sp_document_root(_named_view->document), it, p, SNAP_XY); SnappedPoint s(p, NR_HUGE); if (_snap_to_itemnode || _snap_to_bboxnode) { - _snapNodes(t, s, p, cand); + _snapNodes(t, s, p, SNAP_XY, cand); } if (_snap_to_itempath || _snap_to_bboxpath) { _snapPaths(t, s, p, cand); @@ -278,6 +309,26 @@ Inkscape::SnappedPoint Inkscape::ObjectSnapper::_doConstrainedSnap(Inkscape::Sna return _doFreeSnap(t, p, it); } + + +Inkscape::SnappedPoint Inkscape::ObjectSnapper::guideSnap(NR::Point const &p, + DimensionToSnap const snap_dim) const +{ + if ( NULL == _named_view ) { + return SnappedPoint(p, NR_HUGE); + } + + /* Get a list of all the SPItems that we will try to snap to */ + std::list cand; + std::list const it; //just an empty list + _findCandidates(cand, sp_document_root(_named_view->document), it, p, snap_dim); + + SnappedPoint s(p, NR_HUGE); + _snapNodes(Inkscape::Snapper::SNAPPOINT_GUIDE, s, p, snap_dim, cand); + + return s; +} + /** * \return true if this Snapper will snap at least one kind of point. */ diff --git a/src/object-snapper.h b/src/object-snapper.h index a0d5f0b65..ad4cfc649 100644 --- a/src/object-snapper.h +++ b/src/object-snapper.h @@ -24,9 +24,12 @@ namespace Inkscape class ObjectSnapper : public Snapper { + public: ObjectSnapper(SPNamedView const *nv, NR::Coord const d); + enum DimensionToSnap {SNAP_X, SNAP_Y, SNAP_XY}; + void setSnapToItemNode(bool s) { _snap_to_itemnode = s; } @@ -59,10 +62,21 @@ public: return _snap_to_bboxpath; } + void setIncludeItemCenter(bool s) { + _include_item_center = s; + } + + bool getIncludeItemCenter() const { + return _include_item_center; + } + void setStrictSnapping(bool enabled) { _strict_snapping = enabled; } + SnappedPoint guideSnap(NR::Point const &p, + DimensionToSnap const snap_dim) const; + bool ThisSnapperMightSnap() const; private: @@ -78,11 +92,13 @@ private: void _findCandidates(std::list& c, SPObject* r, std::list const &it, - NR::Point const &p) const; + NR::Point const &p, + DimensionToSnap const snap_dim) const; void _snapNodes(Inkscape::Snapper::PointType const &t, Inkscape::SnappedPoint &s, NR::Point const &p, + DimensionToSnap const snap_dim, std::list const &cand) const; void _snapPaths(Inkscape::Snapper::PointType const &t, @@ -100,6 +116,7 @@ private: //snap bbox corners to nodes, or nodes to bboxes. //(snapping to grids and guides is not affected by this) bool _strict_snapping; + bool _include_item_center; }; } diff --git a/src/snap.cpp b/src/snap.cpp index c585a9790..07e76362d 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -26,6 +26,8 @@ #include "inkscape.h" #include "desktop.h" +#include "sp-guide.h" +using std::vector; /** * Construct a SnapManager for a SPNamedView. @@ -145,6 +147,16 @@ bool SnapManager::getSnapModeNode() const return guide.getSnapFrom(Inkscape::Snapper::SNAPPOINT_NODE); } +void SnapManager::setSnapModeGuide(bool enabled) +{ + object.setSnapFrom(Inkscape::Snapper::SNAPPOINT_GUIDE, enabled); +} + +bool SnapManager::getSnapModeGuide() const +{ + return object.getSnapFrom(Inkscape::Snapper::SNAPPOINT_GUIDE); +} + /** * Try to snap a point to any interested snappers. * @@ -317,6 +329,21 @@ Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType return r; } +Inkscape::SnappedPoint SnapManager::guideSnap(NR::Point const &p, + SPGuide const &guide) const +{ + Inkscape::ObjectSnapper::DimensionToSnap snap_dim; + if (guide.normal == component_vectors[NR::Y]) { + snap_dim = Inkscape::ObjectSnapper::SNAP_Y; + } else if (guide.normal == component_vectors[NR::X]) { + snap_dim = Inkscape::ObjectSnapper::SNAP_X; + } else { + g_warning("WARNING: snapping of angled guides is not supported yet!"); + snap_dim = Inkscape::ObjectSnapper::SNAP_XY; + } + + return object.guideSnap(p, snap_dim); +} /** diff --git a/src/snap.h b/src/snap.h index c60d866ad..276069fe6 100644 --- a/src/snap.h +++ b/src/snap.h @@ -76,6 +76,9 @@ public: NR::Point const &p, Inkscape::Snapper::ConstraintLine const &c, std::list const &it) const; + + Inkscape::SnappedPoint guideSnap(NR::Point const &p, + SPGuide const &guide) const; std::pair freeSnapTranslation(Inkscape::Snapper::PointType t, std::vector const &p, @@ -124,11 +127,15 @@ public: void setSnapModeBBox(bool enabled); void setSnapModeNode(bool enabled); + void setSnapModeGuide(bool enabled); bool getSnapModeBBox() const; bool getSnapModeNode() const; + bool getSnapModeGuide() const; void setIncludeItemCenter(bool enabled) { _include_item_center = enabled; + object.setIncludeItemCenter(enabled); //store a local copy in the object-snapper + //instead of passing it through many functions } bool getIncludeItemCenter() const { diff --git a/src/snapper.cpp b/src/snapper.cpp index 7a82fe1d4..5d39c6b02 100644 --- a/src/snapper.cpp +++ b/src/snapper.cpp @@ -15,6 +15,7 @@ Inkscape::Snapper::PointType const Inkscape::Snapper::SNAPPOINT_BBOX = 0x1; Inkscape::Snapper::PointType const Inkscape::Snapper::SNAPPOINT_NODE = 0x2; +Inkscape::Snapper::PointType const Inkscape::Snapper::SNAPPOINT_GUIDE = 0x4; /** * Construct new Snapper for named view. diff --git a/src/snapper.h b/src/snapper.h index 1440f64c7..085eb156e 100644 --- a/src/snapper.h +++ b/src/snapper.h @@ -35,6 +35,7 @@ public: typedef int PointType; static const PointType SNAPPOINT_NODE; static const PointType SNAPPOINT_BBOX; + static const PointType SNAPPOINT_GUIDE; void setSnapFrom(PointType t, bool s); void setDistance(::NR::Coord d); diff --git a/src/sp-namedview.cpp b/src/sp-namedview.cpp index 319e0ba39..fa41060c2 100644 --- a/src/sp-namedview.cpp +++ b/src/sp-namedview.cpp @@ -155,6 +155,7 @@ static void sp_namedview_build(SPObject *object, SPDocument *document, Inkscape: sp_object_read_attr(object, "inkscape:window-y"); sp_object_read_attr(object, "inkscape:snap-bbox"); sp_object_read_attr(object, "inkscape:snap-nodes"); + sp_object_read_attr(object, "inkscape:snap-guide"); sp_object_read_attr(object, "inkscape:snap-center"); sp_object_read_attr(object, "inkscape:object-paths"); sp_object_read_attr(object, "inkscape:object-nodes"); @@ -355,6 +356,10 @@ static void sp_namedview_set(SPObject *object, unsigned int key, const gchar *va nv->snap_manager.setIncludeItemCenter(value ? sp_str_to_bool(value) : FALSE); object->requestModified(SP_OBJECT_MODIFIED_FLAG); break; + case SP_ATTR_INKSCAPE_SNAP_GUIDE: + nv->snap_manager.setSnapModeGuide(value ? sp_str_to_bool(value) : FALSE); + object->requestModified(SP_OBJECT_MODIFIED_FLAG); + break; case SP_ATTR_INKSCAPE_OBJECT_PATHS: nv->snap_manager.object.setSnapToItemPath(value ? sp_str_to_bool(value) : FALSE); object->requestModified(SP_OBJECT_MODIFIED_FLAG); diff --git a/src/ui/dialog/document-properties.cpp b/src/ui/dialog/document-properties.cpp index f301629e4..b62248cc3 100644 --- a/src/ui/dialog/document-properties.cpp +++ b/src/ui/dialog/document-properties.cpp @@ -272,16 +272,16 @@ DocumentProperties::build_snap() _rcbsnn.init (_("_Nodes"), _("Snap nodes to grid lines, to guides, to paths, and to other nodes"), "inkscape:snap-nodes", _wr); - _rcbic.init (_("Include the object's center"), - _("Also snap the rotation center of an object when snapping nodes"), - "inkscape:snap-center", _wr); + _rcbsng.init (_("_Guides"), + _("While dragging a guide, snap to object nodes. (In 'Snapping to objects', 'Snap to nodes' must also be enabled)"), + "inkscape:snap-guide", _wr); //Options for snapping to objects - _rcbsnop.init (_("Snap to _paths"), + _rcbsnop.init (_("Snap to p_aths"), _("Snap nodes to object paths"), "inkscape:object-paths", _wr); _rcbsnon.init (_("Snap to n_odes"), - _("Snap nodes to object nodes"), + _("Snap nodes or guides to object nodes"), "inkscape:object-nodes", _wr); _rsu_sno.init (_("Snap _distance"), _("Snap at any dist_ance"), _("Snapping distance, in screen pixels, for snapping to objects"), @@ -300,28 +300,31 @@ DocumentProperties::build_snap() _("If set, objects snap to the nearest guide, regardless of distance"), "guidetolerance", _wr); - std::list list; - list.push_back(_rcbic._button); - list.push_back(_rcbsnop._button); - list.push_back(_rcbsnon._button); - _rcbsnn.setSlaveButton(list); - + //Some other options + _rcbic.init (_("_Include the object's center"), + _("Also snap the rotation center of an object when snapping nodes or guides"), + "inkscape:snap-center", _wr); + //Applies to both nodes and guides, but not to bboxes, that's why its located here + //Other options to locate here: e.g. visual snapping indicators on/off Gtk::Label *label_g = manage (new Gtk::Label); - label_g->set_markup (_("Snapping from")); + label_g->set_markup (_("Snapping of")); Gtk::Label *label_o = manage (new Gtk::Label); label_o->set_markup (_("Snapping to objects")); Gtk::Label *label_gr = manage (new Gtk::Label); label_gr->set_markup (_("Snapping to grids")); Gtk::Label *label_gu = manage (new Gtk::Label); label_gu->set_markup (_("Snapping to guides")); + Gtk::Label *label_m = manage (new Gtk::Label); + label_m->set_markup (_("Miscellaneous")); Gtk::Widget *const array[] = { label_g, 0, 0, _rcbsnn._button, - 0, 0, //_rcbic._button will be inserted here + //0, 0, //_rcbic._button will be inserted here 0, _rcbsnbb._button, + 0, _rcbsng._button, 0, 0, 0, 0, 0, 0, @@ -335,12 +338,14 @@ DocumentProperties::build_snap() 0, 0, label_gu, 0, 0, _rsu_gusn._vbox, + 0, 0, + 0, 0, + 0, 0, + label_m, 0, + 0, _rcbic._button }; attach_all(_page_snap.table(), array, G_N_ELEMENTS(array)); - - // add _rcbic manually to get some additional indenting - _page_snap.table().attach(*_rcbic._button, 1, 3, 2, 3, Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0, 18, 0); } /** @@ -451,6 +456,7 @@ DocumentProperties::update() _rcbsnbb.setActive (nv->snap_manager.getSnapModeBBox()); _rcbsnn.setActive (nv->snap_manager.getSnapModeNode()); + _rcbsng.setActive (nv->snap_manager.getSnapModeGuide()); _rcbic.setActive (nv->snap_manager.getIncludeItemCenter()); _rcbsnop.setActive(nv->snap_manager.object.getSnapToItemPath()); _rcbsnon.setActive(nv->snap_manager.object.getSnapToItemNode()); diff --git a/src/ui/dialog/document-properties.h b/src/ui/dialog/document-properties.h index 5db1c734f..846d95341 100644 --- a/src/ui/dialog/document-properties.h +++ b/src/ui/dialog/document-properties.h @@ -73,7 +73,7 @@ protected: RegisteredUnitMenu _rum_gusn; RegisteredColorPicker _rcp_gui, _rcp_hgui; //--------------------------------------------------------------- - RegisteredCheckButton _rcbsnbb, _rcbsnn, _rcbic, _rcbsnop, _rcbsnon; + RegisteredCheckButton _rcbsnbb, _rcbsng, _rcbsnn, _rcbic, _rcbsnop, _rcbsnon; RegisteredUnitMenu _rumso; ToleranceSlider _rsu_sno, _rsu_sn, _rsu_gusn; RegisteredRadioButtonPair _rrb_pix;