summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDiederik van Lierop <mail@diedenrezi.nl>2011-06-30 20:46:15 +0000
committerDiederik van Lierop <mail@diedenrezi.nl>2011-06-30 20:46:15 +0000
commit8911d9a8ca0c7f4ef1476b2f056adf2afa4e99cd (patch)
treebc14be1ac41f82196fc5a07ac99ff3613eeb4cd7 /src
parentMinimise GTK version changes in text-edit dialog (diff)
downloadinkscape-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.h1
-rw-r--r--src/attributes.cpp1
-rw-r--r--src/attributes.h1
-rw-r--r--src/display/snap-indicator.cpp7
-rw-r--r--src/libnrtype/Layout-TNG-OutIter.cpp21
-rw-r--r--src/libnrtype/Layout-TNG.h2
-rw-r--r--src/object-snapper.cpp230
-rw-r--r--src/object-snapper.h1
-rw-r--r--src/snap-enums.h69
-rw-r--r--src/snap-preferences.cpp7
-rw-r--r--src/snap-preferences.h4
-rw-r--r--src/sp-flowtext.cpp18
-rw-r--r--src/sp-namedview.cpp5
-rw-r--r--src/sp-text.cpp18
-rw-r--r--src/ui/icon-names.h2
-rw-r--r--src/widgets/toolbox.cpp18
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);