diff options
| author | Diederik van Lierop <mail@diedenrezi.nl> | 2011-06-30 20:46:15 +0000 |
|---|---|---|
| committer | Diederik van Lierop <mail@diedenrezi.nl> | 2011-06-30 20:46:15 +0000 |
| commit | 8911d9a8ca0c7f4ef1476b2f056adf2afa4e99cd (patch) | |
| tree | bc14be1ac41f82196fc5a07ac99ff3613eeb4cd7 /src | |
| parent | Minimise GTK version changes in text-edit dialog (diff) | |
| download | inkscape-8911d9a8ca0c7f4ef1476b2f056adf2afa4e99cd.tar.gz inkscape-8911d9a8ca0c7f4ef1476b2f056adf2afa4e99cd.zip | |
Implement decent snapping to text (baseline & anchor), and provide a toggle button for this (as requested in LP bug #727281 )
(bzr r10392)
Diffstat (limited to 'src')
| -rw-r--r-- | src/attributes-test.h | 1 | ||||
| -rw-r--r-- | src/attributes.cpp | 1 | ||||
| -rw-r--r-- | src/attributes.h | 1 | ||||
| -rw-r--r-- | src/display/snap-indicator.cpp | 7 | ||||
| -rw-r--r-- | src/libnrtype/Layout-TNG-OutIter.cpp | 21 | ||||
| -rw-r--r-- | src/libnrtype/Layout-TNG.h | 2 | ||||
| -rw-r--r-- | src/object-snapper.cpp | 230 | ||||
| -rw-r--r-- | src/object-snapper.h | 1 | ||||
| -rw-r--r-- | src/snap-enums.h | 69 | ||||
| -rw-r--r-- | src/snap-preferences.cpp | 7 | ||||
| -rw-r--r-- | src/snap-preferences.h | 4 | ||||
| -rw-r--r-- | src/sp-flowtext.cpp | 18 | ||||
| -rw-r--r-- | src/sp-namedview.cpp | 5 | ||||
| -rw-r--r-- | src/sp-text.cpp | 18 | ||||
| -rw-r--r-- | src/ui/icon-names.h | 2 | ||||
| -rw-r--r-- | src/widgets/toolbox.cpp | 18 |
16 files changed, 253 insertions, 152 deletions
diff --git a/src/attributes-test.h b/src/attributes-test.h index 6a9570c37..dee29975e 100644 --- a/src/attributes-test.h +++ b/src/attributes-test.h @@ -355,6 +355,7 @@ struct {char const *attr; bool supported;} const all_attrs[] = { {"inkscape:snap-smooth-nodes", true}, {"inkscape:snap-midpoints", true}, {"inkscape:snap-object-midpoints", true}, + {"inkscape:snap-text-baseline", true}, {"inkscape:snap-bbox-edge-midpoints", true}, {"inkscape:snap-bbox-midpoints", true}, //{"inkscape:snap-intersection-grid-guide", true}, diff --git a/src/attributes.cpp b/src/attributes.cpp index 334c3447c..47b261038 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -102,6 +102,7 @@ static SPStyleProp const props[] = { {SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES, "inkscape:snap-smooth-nodes"}, {SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS, "inkscape:snap-midpoints"}, {SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS, "inkscape:snap-object-midpoints"}, + {SP_ATTR_INKSCAPE_SNAP_TEXT_BASELINE, "inkscape:snap-text-baseline"}, {SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS, "inkscape:snap-bbox-edge-midpoints"}, {SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS, "inkscape:snap-bbox-midpoints"}, {SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS, "inkscape:snap-intersection-paths"}, diff --git a/src/attributes.h b/src/attributes.h index afa396507..2dec8b351 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -102,6 +102,7 @@ enum SPAttributeEnum { SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS, + SP_ATTR_INKSCAPE_SNAP_TEXT_BASELINE, SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS, //SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE, diff --git a/src/display/snap-indicator.cpp b/src/display/snap-indicator.cpp index ec4b7e28f..e351a1145 100644 --- a/src/display/snap-indicator.cpp +++ b/src/display/snap-indicator.cpp @@ -141,6 +141,9 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const &p, bool pre_snap case SNAPTARGET_CORNER: target_name = _("corner"); break; + case SNAPTARGET_TEXT_ANCHOR: + target_name = _("text anchor"); + break; case SNAPTARGET_TEXT_BASELINE: target_name = _("text baseline"); break; @@ -206,8 +209,8 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const &p, bool pre_snap case SNAPSOURCE_CORNER: source_name = _("Corner"); break; - case SNAPSOURCE_TEXT_BASELINE: - source_name = _("Text baseline"); + case SNAPSOURCE_TEXT_ANCHOR: + source_name = _("Text anchor"); break; case SNAPSOURCE_GRID_PITCH: source_name = _("Multiple of grid spacing"); diff --git a/src/libnrtype/Layout-TNG-OutIter.cpp b/src/libnrtype/Layout-TNG-OutIter.cpp index 4d461a486..1d300b210 100644 --- a/src/libnrtype/Layout-TNG-OutIter.cpp +++ b/src/libnrtype/Layout-TNG-OutIter.cpp @@ -13,6 +13,7 @@ #include "font-instance.h" #include "svg/svg-length.h" #include <2geom/transforms.h> +#include <2geom/line.h> #include "style.h" namespace Inkscape { @@ -250,6 +251,26 @@ boost::optional<Geom::Point> Layout::baselineAnchorPoint() const } } +Geom::Path Layout::baseline() const +{ + iterator pos = this->begin(); + Geom::Point left_pt = this->characterAnchorPoint(pos); + pos.thisEndOfLine(); + Geom::Point right_pt = this->characterAnchorPoint(pos); + + if (this->_blockProgression() == LEFT_TO_RIGHT || this->_blockProgression() == RIGHT_TO_LEFT) { + left_pt = Geom::Point(left_pt[Geom::Y], left_pt[Geom::X]); + right_pt = Geom::Point(right_pt[Geom::Y], right_pt[Geom::X]); + } + + Geom::Path baseline; + baseline.start(left_pt); + baseline.appendNew<Geom::LineSegment>(right_pt); + + return baseline; +} + + Geom::Point Layout::chunkAnchorPoint(iterator const &it) const { unsigned chunk_index; diff --git a/src/libnrtype/Layout-TNG.h b/src/libnrtype/Layout-TNG.h index 7d0c58c3e..6ab02c0e3 100644 --- a/src/libnrtype/Layout-TNG.h +++ b/src/libnrtype/Layout-TNG.h @@ -486,6 +486,8 @@ public: For rightmost text, the rightmost... you probably got it by now ;-)*/ boost::optional<Geom::Point> baselineAnchorPoint() const; + Geom::Path baseline() const; + /** This is that value to apply to the x,y attributes of tspan role=line elements, and hence it takes alignment into account. */ Geom::Point chunkAnchorPoint(iterator const &it) const; diff --git a/src/object-snapper.cpp b/src/object-snapper.cpp index 3088accd2..1944f7ffa 100644 --- a/src/object-snapper.cpp +++ b/src/object-snapper.cpp @@ -183,7 +183,7 @@ void Inkscape::ObjectSnapper::_collectNodes(Inkscape::SnapSourceType const &t, bool p_is_a_bbox = t & Inkscape::SNAPSOURCE_BBOX_CATEGORY; bool p_is_other = t & Inkscape::SNAPSOURCE_OTHERS_CATEGORY; - // A point considered for snapping should be either a node, a bbox corner or a guide. Pick only ONE! + // A point considered for snapping should be either a node, a bbox corner or a guide/other. Pick only ONE! g_assert(!((p_is_a_node && p_is_a_bbox) || (p_is_a_bbox && p_is_other) || (p_is_a_node && p_is_other))); if (_snapmanager->snapprefs.getSnapToBBoxNode() || _snapmanager->snapprefs.getSnapBBoxEdgeMidpoints() || _snapmanager->snapprefs.getSnapBBoxMidpoints()) { @@ -282,27 +282,30 @@ void Inkscape::ObjectSnapper::_snapNodes(SnappedConstraints &sc, SnappedPoint s; bool success = false; + bool strict_snapping = _snapmanager->snapprefs.getStrictSnapping(); for (std::vector<SnapCandidatePoint>::const_iterator k = _points_to_snap_to->begin(); k != _points_to_snap_to->end(); k++) { - Geom::Point target_pt = (*k).getPoint(); - Geom::Coord dist = NR_HUGE; - if (!c.isUndefined()) { - // We're snapping to nodes along a constraint only, so find out if this node - // is at the constraint, while allowing for a small margin - if (Geom::L2(target_pt - c.projection(target_pt)) > 1e-9) { - // The distance from the target point to its projection on the constraint - // is too large, so this point is not on the constraint. Skip it! - continue; + if (_allowSourceToSnapToTarget(p.getSourceType(), (*k).getTargetType(), strict_snapping)) { + Geom::Point target_pt = (*k).getPoint(); + Geom::Coord dist = NR_HUGE; + if (!c.isUndefined()) { + // We're snapping to nodes along a constraint only, so find out if this node + // is at the constraint, while allowing for a small margin + if (Geom::L2(target_pt - c.projection(target_pt)) > 1e-9) { + // The distance from the target point to its projection on the constraint + // is too large, so this point is not on the constraint. Skip it! + continue; + } + dist = Geom::L2(target_pt - p_proj_on_constraint); + } else { + // Free (unconstrained) snapping + dist = Geom::L2(target_pt - p.getPoint()); } - dist = Geom::L2(target_pt - p_proj_on_constraint); - } else { - // Free (unconstrained) snapping - dist = Geom::L2(target_pt - p.getPoint()); - } - if (dist < getSnapperTolerance() && dist < s.getSnapDistance()) { - s = SnappedPoint(target_pt, p.getSourceType(), p.getSourceNum(), (*k).getTargetType(), dist, getSnapperTolerance(), getSnapperAlwaysSnap(), false, true, (*k).getTargetBBox()); - success = true; + if (dist < getSnapperTolerance() && dist < s.getSnapDistance()) { + s = SnappedPoint(target_pt, p.getSourceType(), p.getSourceNum(), (*k).getTargetType(), dist, getSnapperTolerance(), getSnapperAlwaysSnap(), false, true, (*k).getTargetBBox()); + success = true; + } } } @@ -394,44 +397,46 @@ void Inkscape::ObjectSnapper::_collectPaths(Geom::Point /*p*/, //Build a list of all paths considered for snapping to //Add the item's path to snap to - if (_snapmanager->snapprefs.getSnapToItemPath() && (_snapmanager->snapprefs.getSnapModeNode() || _snapmanager->snapprefs.getSnapModeOthers())) { + if ((_snapmanager->snapprefs.getSnapToItemPath() && _snapmanager->snapprefs.getSnapModeNode()) || + (_snapmanager->snapprefs.getSnapTextBaseline() && (_snapmanager->snapprefs.getSnapModeNode() || _snapmanager->snapprefs.getSnapToItemPath())) ) { if (p_is_other || !(_snapmanager->snapprefs.getStrictSnapping() && !p_is_a_node)) { - // Snapping to the path of characters is very cool, but for a large - // chunk of text this will take ages! So limit snapping to text paths - // containing max. 240 characters. Snapping the bbox will not be affected - bool very_lenghty_prose = false; if (SP_IS_TEXT(root_item) || SP_IS_FLOWTEXT(root_item)) { - very_lenghty_prose = sp_text_get_length(SP_TEXT(root_item)) > 240; - } - // On my AMD 3000+, the snapping lag becomes annoying at approx. 240 chars - // which corresponds to a lag of 500 msec. This is for snapping a rect - // to a single line of text. - - // Snapping for example to a traced bitmap is also very stressing for - // the CPU, so we'll only snap to paths having no more than 500 nodes - // This also leads to a lag of approx. 500 msec (in my lousy test set-up). - bool very_complex_path = false; - if (SP_IS_PATH(root_item)) { - very_complex_path = sp_nodes_in_path(SP_PATH(root_item)) > 500; - } - - if (!very_lenghty_prose && !very_complex_path && root_item) { - SPCurve *curve = NULL; - if (SP_IS_SHAPE(root_item)) { - curve = SP_SHAPE(root_item)->getCurve(); - } else if (SP_IS_TEXT(root_item) || SP_IS_FLOWTEXT(root_item)) { - curve = te_get_layout(root_item)->convertToCurves(); + if (_snapmanager->snapprefs.getSnapTextBaseline()) { + // Snap to the text baseline + Inkscape::Text::Layout const *layout = te_get_layout((SPItem *) root_item); + if (layout != NULL && layout->outputExists()) { + Geom::PathVector *pv = new Geom::PathVector(); + pv->push_back(layout->baseline() * root_item->i2d_affine() * (*i).additional_affine * _snapmanager->getDesktop()->doc2dt()); + _paths_to_snap_to->push_back(Inkscape::SnapCandidatePath(pv, SNAPTARGET_TEXT_BASELINE, Geom::OptRect())); + } + } + } else { + // Snapping for example to a traced bitmap is very stressing for + // the CPU, so we'll only snap to paths having no more than 500 nodes + // This also leads to a lag of approx. 500 msec (in my lousy test set-up). + bool very_complex_path = false; + if (SP_IS_PATH(root_item)) { + very_complex_path = sp_nodes_in_path(SP_PATH(root_item)) > 500; } - if (curve) { - // We will get our own copy of the pathvector, which must be freed at some point - // Geom::PathVector *pv = pathvector_for_curve(root_item, curve, true, true, Geom::identity(), (*i).additional_affine); + if (!very_complex_path && root_item && (_snapmanager->snapprefs.getSnapToItemPath() && _snapmanager->snapprefs.getSnapModeNode())) { + SPCurve *curve = NULL; + if (SP_IS_SHAPE(root_item)) { + curve = SP_SHAPE(root_item)->getCurve(); + }/* else if (SP_IS_TEXT(root_item) || SP_IS_FLOWTEXT(root_item)) { + curve = te_get_layout(root_item)->convertToCurves(); + }*/ + if (curve) { + // We will get our own copy of the pathvector, which must be freed at some point + + // Geom::PathVector *pv = pathvector_for_curve(root_item, curve, true, true, Geom::identity(), (*i).additional_affine); - Geom::PathVector *pv = new Geom::PathVector(curve->get_pathvector()); - (*pv) *= root_item->i2d_affine() * (*i).additional_affine * _snapmanager->getDesktop()->doc2dt(); // (_edit_transform * _i2d_transform); + Geom::PathVector *pv = new Geom::PathVector(curve->get_pathvector()); + (*pv) *= root_item->i2d_affine() * (*i).additional_affine * _snapmanager->getDesktop()->doc2dt(); // (_edit_transform * _i2d_transform); - _paths_to_snap_to->push_back(Inkscape::SnapCandidatePath(pv, SNAPTARGET_PATH, Geom::OptRect())); // Perhaps for speed, get a reference to the Geom::pathvector, and store the transformation besides it. - curve->unref(); + _paths_to_snap_to->push_back(Inkscape::SnapCandidatePath(pv, SNAPTARGET_PATH, Geom::OptRect())); // Perhaps for speed, get a reference to the Geom::pathvector, and store the transformation besides it. + curve->unref(); + } } } } @@ -490,54 +495,58 @@ void Inkscape::ObjectSnapper::_snapPaths(SnappedConstraints &sc, int num_path = 0; int num_segm = 0; - for (std::vector<Inkscape::SnapCandidatePath >::const_iterator it_p = _paths_to_snap_to->begin(); it_p != _paths_to_snap_to->end(); it_p++) { - bool const being_edited = node_tool_active && (*it_p).currently_being_edited; - //if true then this pathvector it_pv is currently being edited in the node tool - - for(Geom::PathVector::iterator it_pv = (it_p->path_vector)->begin(); it_pv != (it_p->path_vector)->end(); ++it_pv) { - // Find a nearest point for each curve within this path - // n curves will return n time values with 0 <= t <= 1 - std::vector<double> anp = (*it_pv).nearestPointPerCurve(p_doc); + bool strict_snapping = _snapmanager->snapprefs.getStrictSnapping(); - std::vector<double>::const_iterator np = anp.begin(); - unsigned int index = 0; - for (; np != anp.end(); np++, index++) { - Geom::Curve const *curve = &((*it_pv).at_index(index)); - Geom::Point const sp_doc = curve->pointAt(*np); - - bool c1 = true; - bool c2 = true; - if (being_edited) { - /* If the path is being edited, then we should only snap though to stationary pieces of the path - * and not to the pieces that are being dragged around. This way we avoid - * self-snapping. For this we check whether the nodes at both ends of the current - * piece are unselected; if they are then this piece must be stationary - */ - g_assert(unselected_nodes != NULL); - Geom::Point start_pt = _snapmanager->getDesktop()->doc2dt(curve->pointAt(0)); - Geom::Point end_pt = _snapmanager->getDesktop()->doc2dt(curve->pointAt(1)); - c1 = isUnselectedNode(start_pt, unselected_nodes); - c2 = isUnselectedNode(end_pt, unselected_nodes); - /* Unfortunately, this might yield false positives for coincident nodes. Inkscape might therefore mistakenly - * snap to path segments that are not stationary. There are at least two possible ways to overcome this: - * - Linking the individual nodes of the SPPath we have here, to the nodes of the NodePath::SubPath class as being - * used in sp_nodepath_selected_nodes_move. This class has a member variable called "selected". For this the nodes - * should be in the exact same order for both classes, so we can index them - * - Replacing the SPPath being used here by the the NodePath::SubPath class; but how? - */ - } + for (std::vector<Inkscape::SnapCandidatePath >::const_iterator it_p = _paths_to_snap_to->begin(); it_p != _paths_to_snap_to->end(); it_p++) { + if (_allowSourceToSnapToTarget(p.getSourceType(), (*it_p).target_type, strict_snapping)) { + bool const being_edited = node_tool_active && (*it_p).currently_being_edited; + //if true then this pathvector it_pv is currently being edited in the node tool + + for(Geom::PathVector::iterator it_pv = (it_p->path_vector)->begin(); it_pv != (it_p->path_vector)->end(); ++it_pv) { + // Find a nearest point for each curve within this path + // n curves will return n time values with 0 <= t <= 1 + std::vector<double> anp = (*it_pv).nearestPointPerCurve(p_doc); + + std::vector<double>::const_iterator np = anp.begin(); + unsigned int index = 0; + for (; np != anp.end(); np++, index++) { + Geom::Curve const *curve = &((*it_pv).at_index(index)); + Geom::Point const sp_doc = curve->pointAt(*np); + + bool c1 = true; + bool c2 = true; + if (being_edited) { + /* If the path is being edited, then we should only snap though to stationary pieces of the path + * and not to the pieces that are being dragged around. This way we avoid + * self-snapping. For this we check whether the nodes at both ends of the current + * piece are unselected; if they are then this piece must be stationary + */ + g_assert(unselected_nodes != NULL); + Geom::Point start_pt = _snapmanager->getDesktop()->doc2dt(curve->pointAt(0)); + Geom::Point end_pt = _snapmanager->getDesktop()->doc2dt(curve->pointAt(1)); + c1 = isUnselectedNode(start_pt, unselected_nodes); + c2 = isUnselectedNode(end_pt, unselected_nodes); + /* Unfortunately, this might yield false positives for coincident nodes. Inkscape might therefore mistakenly + * snap to path segments that are not stationary. There are at least two possible ways to overcome this: + * - Linking the individual nodes of the SPPath we have here, to the nodes of the NodePath::SubPath class as being + * used in sp_nodepath_selected_nodes_move. This class has a member variable called "selected". For this the nodes + * should be in the exact same order for both classes, so we can index them + * - Replacing the SPPath being used here by the the NodePath::SubPath class; but how? + */ + } - Geom::Point const sp_dt = _snapmanager->getDesktop()->doc2dt(sp_doc); - if (!being_edited || (c1 && c2)) { - Geom::Coord const dist = Geom::distance(sp_doc, p_doc); - if (dist < getSnapperTolerance()) { - sc.curves.push_back(Inkscape::SnappedCurve(sp_dt, num_path, num_segm, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), false, curve, p.getSourceType(), p.getSourceNum(), it_p->target_type, it_p->target_bbox)); + Geom::Point const sp_dt = _snapmanager->getDesktop()->doc2dt(sp_doc); + if (!being_edited || (c1 && c2)) { + Geom::Coord const dist = Geom::distance(sp_doc, p_doc); + if (dist < getSnapperTolerance()) { + sc.curves.push_back(Inkscape::SnappedCurve(sp_dt, num_path, num_segm, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), false, curve, p.getSourceType(), p.getSourceNum(), it_p->target_type, it_p->target_bbox)); + } } } - } - num_segm++; - } // End of: for (Geom::PathVector::iterator ....) - num_path++; + num_segm++; + } // End of: for (Geom::PathVector::iterator ....) + num_path++; + } } } @@ -599,10 +608,12 @@ void Inkscape::ObjectSnapper::_snapPathsConstrained(SnappedConstraints &sc, } // Length of constraint_path will always be one + bool strict_snapping = _snapmanager->snapprefs.getStrictSnapping(); + // Find all intersections of the constrained path with the snap target candidates std::vector<Geom::Point> intersections; for (std::vector<Inkscape::SnapCandidatePath >::const_iterator k = _paths_to_snap_to->begin(); k != _paths_to_snap_to->end(); k++) { - if (k->path_vector) { + if (k->path_vector && _allowSourceToSnapToTarget(p.getSourceType(), (*k).target_type, strict_snapping)) { // Do the intersection math Geom::CrossingSet cs = Geom::crossings(constraint_path, *(k->path_vector)); // Store the results as intersection points @@ -681,7 +692,8 @@ void Inkscape::ObjectSnapper::freeSnap(SnappedConstraints &sc, )) || (_snapmanager->snapprefs.getSnapModeAny() && ( _snapmanager->snapprefs.getIncludeItemCenter() || _snapmanager->snapprefs.getSnapToPageBorder() || - _snapmanager->snapprefs.getSnapObjectMidpoints() + _snapmanager->snapprefs.getSnapObjectMidpoints() || + _snapmanager->snapprefs.getSnapTextBaseline() )) ; if (snap_nodes) { @@ -690,7 +702,7 @@ void Inkscape::ObjectSnapper::freeSnap(SnappedConstraints &sc, if ((_snapmanager->snapprefs.getSnapModeNode() && _snapmanager->snapprefs.getSnapToItemPath()) || (_snapmanager->snapprefs.getSnapModeBBox() && _snapmanager->snapprefs.getSnapToBBoxPath()) || - (_snapmanager->snapprefs.getSnapModeAny() && _snapmanager->snapprefs.getSnapToPageBorder())) { + _snapmanager->snapprefs.getSnapModeAny()) { unsigned n = (unselected_nodes == NULL) ? 0 : unselected_nodes->size(); if (n > 0) { /* While editing a path in the node tool, findCandidates must ignore that path because @@ -748,7 +760,8 @@ void Inkscape::ObjectSnapper::constrainedSnap( SnappedConstraints &sc, )) || (_snapmanager->snapprefs.getSnapModeAny() && ( _snapmanager->snapprefs.getIncludeItemCenter() || _snapmanager->snapprefs.getSnapObjectMidpoints() || - _snapmanager->snapprefs.getSnapToPageBorder() + _snapmanager->snapprefs.getSnapToPageBorder() || + _snapmanager->snapprefs.getSnapTextBaseline() )); if (snap_nodes) { @@ -808,7 +821,8 @@ bool Inkscape::ObjectSnapper::ThisSnapperMightSnap() const )) || (_snapmanager->snapprefs.getSnapModeAny() && ( _snapmanager->snapprefs.getSnapToPageBorder() || _snapmanager->snapprefs.getIncludeItemCenter() || - _snapmanager->snapprefs.getSnapObjectMidpoints() + _snapmanager->snapprefs.getSnapObjectMidpoints() || + _snapmanager->snapprefs.getSnapTextBaseline() )); return (_snap_enabled && snap_to_something); @@ -873,6 +887,24 @@ void Inkscape::getBBoxPoints(Geom::OptRect const bbox, } } +bool Inkscape::ObjectSnapper::_allowSourceToSnapToTarget(SnapSourceType source, SnapTargetType target, bool strict_snapping) const +{ + bool allow_this_pair_to_snap = false; + + if (strict_snapping) { // bounding boxes will not snap to nodes/paths and vice versa + int source_cat = source & (SNAPSOURCE_BBOX_CATEGORY | SNAPSOURCE_NODE_CATEGORY | SNAPSOURCE_OTHERS_CATEGORY); + int target_cat = target & (SNAPTARGET_BBOX_CATEGORY | SNAPTARGET_NODE_CATEGORY | SNAPTARGET_OTHERS_CATEGORY); + if (source_cat == target_cat || source_cat == SNAPSOURCE_OTHERS_CATEGORY || target_cat == SNAPTARGET_OTHERS_CATEGORY) { + allow_this_pair_to_snap = true; + } + } else { // anything will snap to anything + allow_this_pair_to_snap = true; + } + + return allow_this_pair_to_snap; +} + + /* Local Variables: mode:c++ diff --git a/src/object-snapper.h b/src/object-snapper.h index 6e3cc620f..00fb18923 100644 --- a/src/object-snapper.h +++ b/src/object-snapper.h @@ -106,6 +106,7 @@ private: Geom::PathVector* _getBorderPathv() const; Geom::PathVector* _getPathvFromRect(Geom::Rect const rect) const; void _getBorderNodes(std::vector<SnapCandidatePoint> *points) const; + bool _allowSourceToSnapToTarget(SnapSourceType source, SnapTargetType target, bool strict_snapping) const; }; // end of ObjectSnapper class diff --git a/src/snap-enums.h b/src/snap-enums.h index 8988589a1..6ef021fc0 100644 --- a/src/snap-enums.h +++ b/src/snap-enums.h @@ -15,36 +15,6 @@ namespace Inkscape { -enum SnapTargetType { - SNAPTARGET_UNDEFINED = 0, - SNAPTARGET_GRID, - SNAPTARGET_GRID_INTERSECTION, - SNAPTARGET_GUIDE, - SNAPTARGET_GUIDE_INTERSECTION, - SNAPTARGET_GUIDE_ORIGIN, - SNAPTARGET_GRID_GUIDE_INTERSECTION, - SNAPTARGET_NODE_SMOOTH, - SNAPTARGET_NODE_CUSP, - SNAPTARGET_LINE_MIDPOINT, - SNAPTARGET_OBJECT_MIDPOINT, - SNAPTARGET_ROTATION_CENTER, - SNAPTARGET_HANDLE, - SNAPTARGET_PATH, - SNAPTARGET_PATH_INTERSECTION, - SNAPTARGET_BBOX_CORNER, - SNAPTARGET_BBOX_EDGE, - SNAPTARGET_BBOX_EDGE_MIDPOINT, - SNAPTARGET_BBOX_MIDPOINT, - SNAPTARGET_PAGE_BORDER, - SNAPTARGET_PAGE_CORNER, - SNAPTARGET_CONVEX_HULL_CORNER, - SNAPTARGET_ELLIPSE_QUADRANT_POINT, - SNAPTARGET_CORNER, // of image or of rectangle - SNAPTARGET_TEXT_BASELINE, - SNAPTARGET_CONSTRAINED_ANGLE, - SNAPTARGET_CONSTRAINT -}; - enum SnapSourceType { SNAPSOURCE_UNDEFINED = 0, //------------------------------------------------------------------- @@ -72,10 +42,47 @@ enum SnapSourceType { SNAPSOURCE_OBJECT_MIDPOINT, // midpoint of rectangles, ellipses, polygon, etc. SNAPSOURCE_GUIDE, SNAPSOURCE_GUIDE_ORIGIN, - SNAPSOURCE_TEXT_BASELINE, + SNAPSOURCE_TEXT_ANCHOR, SNAPSOURCE_OTHER_HANDLE, // eg. the handle of a gradient or of a connector (ie not being tied to a stroke) SNAPSOURCE_GRID_PITCH, // eg. when pasting or alt-dragging in the selector tool; not realy a snap source }; +enum SnapTargetType { + SNAPTARGET_UNDEFINED = 0, + //------------------------------------------------------------------- + SNAPTARGET_BBOX_CATEGORY = 256, // will be used as a flag and must therefore be a power of two + SNAPTARGET_BBOX_CORNER, + SNAPTARGET_BBOX_EDGE, + SNAPTARGET_BBOX_EDGE_MIDPOINT, + SNAPTARGET_BBOX_MIDPOINT, + //------------------------------------------------------------------- + SNAPTARGET_NODE_CATEGORY = 512, // will be used as a flag and must therefore be a power of two + SNAPTARGET_NODE_SMOOTH, + SNAPTARGET_NODE_CUSP, + SNAPTARGET_LINE_MIDPOINT, + SNAPTARGET_PATH, + SNAPTARGET_PATH_INTERSECTION, + SNAPTARGET_ELLIPSE_QUADRANT_POINT, + SNAPTARGET_CORNER, // of image or of rectangle + //------------------------------------------------------------------- + SNAPTARGET_OTHERS_CATEGORY = 1024, // will be used as a flag and must therefore be a power of two + SNAPTARGET_GRID, + SNAPTARGET_GRID_INTERSECTION, + SNAPTARGET_GUIDE, + SNAPTARGET_GUIDE_INTERSECTION, + SNAPTARGET_GUIDE_ORIGIN, + SNAPTARGET_GRID_GUIDE_INTERSECTION, + SNAPTARGET_OBJECT_MIDPOINT, + SNAPTARGET_ROTATION_CENTER, + SNAPTARGET_HANDLE, + SNAPTARGET_PAGE_BORDER, + SNAPTARGET_PAGE_CORNER, + SNAPTARGET_CONVEX_HULL_CORNER, + SNAPTARGET_TEXT_ANCHOR, + SNAPTARGET_TEXT_BASELINE, + SNAPTARGET_CONSTRAINED_ANGLE, + SNAPTARGET_CONSTRAINT +}; + } #endif /* SNAPENUMS_H_ */ diff --git a/src/snap-preferences.cpp b/src/snap-preferences.cpp index 816320145..fba9beb43 100644 --- a/src/snap-preferences.cpp +++ b/src/snap-preferences.cpp @@ -32,10 +32,11 @@ Inkscape::SnapPreferences::SnapPreferences() : /* * The snappers have too many parameters to adjust individually. Therefore only - * two snapping modes are presented to the user: snapping bounding box corners (to + * three snapping modes are presented to the user: snapping bounding box corners (to * other bounding boxes, grids or guides), and/or snapping nodes (to other nodes, - * paths, grids or guides). To select either of these modes (or both), use the - * methods defined below: setSnapModeBBox() and setSnapModeNode(). + * paths, grids or guides), and or snapping to/from others (e.g. grids, guide, text, etc) + * To select either of these three modes (or all), use the + * methods defined below: setSnapModeBBox(), setSnapModeNode(), or setSnapModeOthers() * * */ diff --git a/src/snap-preferences.h b/src/snap-preferences.h index 4f3ad6ce6..8e8ebc9cf 100644 --- a/src/snap-preferences.h +++ b/src/snap-preferences.h @@ -40,6 +40,7 @@ public: void setSnapSmoothNodes(bool enabled) {_smoothNodes = enabled;} void setSnapLineMidpoints(bool enabled) {_line_midpoints = enabled;} void setSnapObjectMidpoints(bool enabled) {_object_midpoints = enabled;} + void setSnapTextBaseline(bool enabled) {_text_baseline = enabled;} void setSnapBBoxEdgeMidpoints(bool enabled) {_bbox_edge_midpoints = enabled;} void setSnapBBoxMidpoints(bool enabled) {_bbox_midpoints = enabled;} bool getSnapIntersectionGG() const {return _intersectionGG;} @@ -47,6 +48,7 @@ public: bool getSnapSmoothNodes() const {return _smoothNodes;} bool getSnapLineMidpoints() const {return _line_midpoints;} bool getSnapObjectMidpoints() const {return _object_midpoints;} + bool getSnapTextBaseline() const {return _text_baseline;} bool getSnapBBoxEdgeMidpoints() const {return _bbox_edge_midpoints;} bool getSnapBBoxMidpoints() const {return _bbox_midpoints;} @@ -89,7 +91,6 @@ public: void setGuideTolerance(gdouble val) {_guide_tolerance = val;} void setObjectTolerance(gdouble val) {_object_tolerance = val;} - private: bool _include_item_center; //If true, snapping nodes will also snap the item's center bool _intersectionGG; //Consider snapping to intersections of grid and guides @@ -97,6 +98,7 @@ private: bool _smoothNodes; bool _line_midpoints; bool _object_midpoints; // the midpoint of shapes (e.g. a circle, rect, polygon) or of any other shape (at [h/2, w/2]) + bool _text_baseline; // both anchor point and baseline of the text bool _bbox_edge_midpoints; bool _bbox_midpoints; bool _snap_to_grids; diff --git a/src/sp-flowtext.cpp b/src/sp-flowtext.cpp index d7bc0053f..3413999a9 100644 --- a/src/sp-flowtext.cpp +++ b/src/sp-flowtext.cpp @@ -388,15 +388,17 @@ static gchar *sp_flowtext_description(SPItem *item) } } -static void sp_flowtext_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const */*snapprefs*/) +static void sp_flowtext_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &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<Geom::Point> pt = layout->baselineAnchorPoint(); - if (pt) { - p.push_back(Inkscape::SnapCandidatePoint((*pt) * item->i2d_affine(), Inkscape::SNAPSOURCE_TEXT_BASELINE, Inkscape::SNAPTARGET_TEXT_BASELINE)); + if (snapprefs->getSnapTextBaseline()) { + // 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<Geom::Point> pt = layout->baselineAnchorPoint(); + if (pt) { + p.push_back(Inkscape::SnapCandidatePoint((*pt) * item->i2d_affine(), Inkscape::SNAPSOURCE_TEXT_ANCHOR, Inkscape::SNAPTARGET_TEXT_ANCHOR)); + } } } } diff --git a/src/sp-namedview.cpp b/src/sp-namedview.cpp index 515658d0b..97fd97570 100644 --- a/src/sp-namedview.cpp +++ b/src/sp-namedview.cpp @@ -257,6 +257,7 @@ static void sp_namedview_build(SPObject *object, SPDocument *document, Inkscape: object->readAttr( "inkscape:snap-smooth-nodes" ); object->readAttr( "inkscape:snap-midpoints" ); object->readAttr( "inkscape:snap-object-midpoints" ); + object->readAttr( "inkscape:snap-text-baseline" ); object->readAttr( "inkscape:snap-bbox-edge-midpoints" ); object->readAttr( "inkscape:snap-bbox-midpoints" ); object->readAttr( "inkscape:snap-to-guides" ); @@ -493,6 +494,10 @@ static void sp_namedview_set(SPObject *object, unsigned int key, const gchar *va nv->snap_manager.snapprefs.setSnapObjectMidpoints(value ? sp_str_to_bool(value) : FALSE); object->requestModified(SP_OBJECT_MODIFIED_FLAG); break; + case SP_ATTR_INKSCAPE_SNAP_TEXT_BASELINE: + nv->snap_manager.snapprefs.setSnapTextBaseline(value ? sp_str_to_bool(value) : FALSE); + object->requestModified(SP_OBJECT_MODIFIED_FLAG); + break; case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS: nv->snap_manager.snapprefs.setSnapBBoxEdgeMidpoints(value ? sp_str_to_bool(value) : FALSE); object->requestModified(SP_OBJECT_MODIFIED_FLAG); diff --git a/src/sp-text.cpp b/src/sp-text.cpp index 3f30c2422..f7ba7592b 100644 --- a/src/sp-text.cpp +++ b/src/sp-text.cpp @@ -441,15 +441,17 @@ static char * sp_text_description(SPItem *item) return ret; } -static void sp_text_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const */*snapprefs*/) +static void sp_text_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &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<Geom::Point> pt = layout->baselineAnchorPoint(); - if (pt) { - p.push_back(Inkscape::SnapCandidatePoint((*pt) * item->i2d_affine(), Inkscape::SNAPSOURCE_TEXT_BASELINE, Inkscape::SNAPTARGET_TEXT_BASELINE)); + if (snapprefs->getSnapTextBaseline()) { + // 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<Geom::Point> pt = layout->baselineAnchorPoint(); + if (pt) { + p.push_back(Inkscape::SnapCandidatePoint((*pt) * item->i2d_affine(), Inkscape::SNAPSOURCE_TEXT_ANCHOR, Inkscape::SNAPTARGET_TEXT_ANCHOR)); + } } } } diff --git a/src/ui/icon-names.h b/src/ui/icon-names.h index cf459b563..8935b1def 100644 --- a/src/ui/icon-names.h +++ b/src/ui/icon-names.h @@ -470,6 +470,8 @@ "snap-nodes-path" #define INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER \ "snap-nodes-rotation-center" +#define INKSCAPE_ICON_SNAP_TEXT_BASELINE \ + "snap-text-baseline" #define INKSCAPE_ICON_SNAP_NODES_SMOOTH \ "snap-nodes-smooth" #define INKSCAPE_ICON_SNAP_PAGE \ diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp index 7789484fd..5d065c3e9 100644 --- a/src/widgets/toolbox.cpp +++ b/src/widgets/toolbox.cpp @@ -2208,6 +2208,10 @@ static void toggle_snap_callback(GtkToggleAction *act, gpointer data) //data poi v = nv->snap_manager.snapprefs.getSnapObjectMidpoints(); sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v); break; + case SP_ATTR_INKSCAPE_SNAP_TEXT_BASELINE: + v = nv->snap_manager.snapprefs.getSnapTextBaseline(); + sp_repr_set_boolean(repr, "inkscape:snap-text-baseline", !v); + break; case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS: v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints(); sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v); @@ -2252,6 +2256,7 @@ void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop) " <toolitem action='ToggleSnapFromOthers' />" " <toolitem action='ToggleSnapToFromObjectCenters' />" " <toolitem action='ToggleSnapToFromRotationCenter' />" + " <toolitem action='ToggleSnapToFromTextBaseline' />" " <separator />" " <toolitem action='ToggleSnapToPageBorder' />" " <toolitem action='ToggleSnapToGrids' />" @@ -2402,6 +2407,16 @@ void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop) } { + InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromTextBaseline", + _("Text baseline"), _("Snap from and to text anchors and baselines"), + INKSCAPE_ICON_SNAP_TEXT_BASELINE, secondarySize, SP_ATTR_INKSCAPE_SNAP_TEXT_BASELINE); + + gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); + } + + + { InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder", _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE, secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE); @@ -2493,6 +2508,7 @@ void ToolboxFactory::updateSnapToolbox(SPDesktop *desktop, SPEventContext * /*ev Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapFromOthers"); Glib::RefPtr<Gtk::Action> act10b = mainActions->get_action("ToggleSnapToFromObjectCenters"); Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter"); + Glib::RefPtr<Gtk::Action> act11b = mainActions->get_action("ToggleSnapToFromTextBaseline"); Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder"); //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections"); Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids"); @@ -2547,6 +2563,8 @@ void ToolboxFactory::updateSnapToolbox(SPDesktop *desktop, SPEventContext * /*ev gtk_action_set_sensitive(GTK_ACTION(act10b->gobj()), c1 && c5); gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter()); gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c5); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11b->gobj()), nv->snap_manager.snapprefs.getSnapTextBaseline()); + gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c5); gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder()); gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1); |
