Code

Implement selection spatial grow
authorKrzysztof Kosiński <tweenk.pl@gmail.com>
Sat, 26 Dec 2009 03:13:01 +0000 (04:13 +0100)
committerKrzysztof Kosiński <tweenk.pl@gmail.com>
Sat, 26 Dec 2009 03:13:01 +0000 (04:13 +0100)
src/ui/tool/curve-drag-point.cpp
src/ui/tool/manipulator.h
src/ui/tool/multi-path-manipulator.cpp
src/ui/tool/multi-path-manipulator.h
src/ui/tool/node.cpp
src/ui/tool/node.h
src/ui/tool/path-manipulator.cpp
src/ui/tool/path-manipulator.h
src/ui/tool/transform-handle-set.cpp

index 889e245c65570e45d707f74a7c305eaa74a4dd89..1823622596ab0e59979d91bb1d200816dcd973d9 100644 (file)
@@ -36,8 +36,9 @@ namespace UI {
 bool CurveDragPoint::_drags_stroke = false;
 
 CurveDragPoint::CurveDragPoint(PathManipulator &pm)
-    : ControlPoint(pm._path_data.node_data.desktop, Geom::Point(), Gtk::ANCHOR_CENTER,
-        SP_CTRL_SHAPE_CIRCLE, 1.0, &invisible_cset, pm._path_data.dragpoint_group)
+    : ControlPoint(pm._multi_path_manipulator._path_data.node_data.desktop, Geom::Point(),
+        Gtk::ANCHOR_CENTER, SP_CTRL_SHAPE_CIRCLE, 1.0, &invisible_cset,
+        pm._multi_path_manipulator._path_data.dragpoint_group)
     , _pm(pm)
 {
     setVisible(false);
index c441f70168b5a5046a5dbbc365bf5663e154c877..799dad0d3cb6e754390fc0129e2d4fb80c0d6e7d 100644 (file)
@@ -27,6 +27,7 @@ class ControlPointSelection;
 
 /**
  * @brief Tool component that processes events and does something in response to them.
+ * Note: this class is probably redundant.
  */
 class Manipulator {
 friend class ManipulatorGroup;
@@ -38,15 +39,13 @@ public:
     
     /// Handle input event. Returns true if handled.
     virtual bool event(GdkEvent *)=0;
-    /// Commits changes to XML (repr).
-    //virtual void commitToXML();
-    //Manipulator *_parent;
 protected:
     SPDesktop *const _desktop;
 };
 
 /**
  * @brief Tool component that edits something on the canvas using selectable control points.
+ * Note: this class is probably redundant.
  */
 class PointManipulator : public Manipulator, public sigc::trackable {
 public:
@@ -61,7 +60,9 @@ protected:
 /** Manipulator that aggregates several manipulators of the same type.
  * The order of invoking events on the member manipulators is undefined.
  * To make this class more useful, derive from it and add actions that can be performed
- * on all manipulators in the set. */
+ * on all manipulators in the set.
+ *
+ * This is not used at the moment and is probably useless. */
 template <typename T>
 class MultiManipulator : public PointManipulator {
 public:
@@ -147,26 +148,6 @@ protected:
     MapType _mmap;
 };
 
-/*
- * @brief Set of manipulators. Takes care of routing events to appropriate manipulators.
- */
-/*class ManipulatorGroup : private std::list<boost::shared_ptr<Manipulator> > {
-friend class Manipulator;
-public:
-    ManipulatorGroup(SPDesktop *d);
-    ~ManipulatorGroup();
-    void add(boost::shared_ptr<Manipulator> m);
-    void remove(boost::shared_ptr<Manipulator> m);
-    void clear();
-    bool event(GdkEvent *);
-private:
-    void _grabEvents(boost::shared_ptr<Manipulator> m);
-    void _ungrabEvents(boost::shared_ptr<Manipulator> m);
-
-    SPDesktop *_desktop;
-    boost::shared_ptr<Manipulator> _grab;
-};*/
-
 } // namespace UI
 } // namespace Inkscape
 
index aaf7e413cdfd303805a7ec4002502cbca9f6eb7d..ac0165e1a1a62882dc12059f6eed8e610def6e7d 100644 (file)
@@ -37,8 +37,7 @@ typedef std::unordered_set<NodeList::iterator> IterSet;
 typedef std::multimap<double, IterPair> DistanceMap;
 typedef std::pair<double, IterPair> DistanceMapItem;
 
-/** Find two selected endnodes.
- * @returns -1 if not enough endnodes selected, 1 if too many, 0 if OK */
+/** Find pairs of selected endnodes suitable for joining. */
 void find_join_iterators(ControlPointSelection &sel, IterPairList &pairs)
 {
     IterSet join_iters;
@@ -105,12 +104,11 @@ bool prepare_join(IterPair &join_iters)
 } // anonymous namespace
 
 
-MultiPathManipulator::MultiPathManipulator(PathSharedData const &data, sigc::connection &chg)
+MultiPathManipulator::MultiPathManipulator(PathSharedData &data, sigc::connection &chg)
     : PointManipulator(data.node_data.desktop, *data.node_data.selection)
     , _path_data(data)
     , _changed(chg)
 {
-    //
     _selection.signal_commit.connect(
         sigc::mem_fun(*this, &MultiPathManipulator::_commit));
     _selection.signal_point_changed.connect(
@@ -170,7 +168,7 @@ void MultiPathManipulator::setItems(std::set<ShapeRecord> const &s)
     for (std::set<ShapeRecord>::iterator i = shapes.begin(); i != shapes.end(); ++i) {
         ShapeRecord const &r = *i;
         if (!SP_IS_PATH(r.item) && !IS_LIVEPATHEFFECT(r.item)) continue;
-        boost::shared_ptr<PathManipulator> newpm(new PathManipulator(_path_data, (SPPath*) r.item,
+        boost::shared_ptr<PathManipulator> newpm(new PathManipulator(*this, (SPPath*) r.item,
             r.edit_transform, _getOutlineColor(r.role), r.lpe_key));
         newpm->showHandles(_show_handles);
         // always show outlines for clips and masks
@@ -203,6 +201,39 @@ void MultiPathManipulator::shiftSelection(int dir)
 {
     invokeForAll(&PathManipulator::shiftSelection, dir);
 }
+void MultiPathManipulator::spatialGrow(NodeList::iterator origin, int dir)
+{
+    double extr_dist = dir > 0 ? HUGE_VAL : -HUGE_VAL;
+    NodeList::iterator target;
+
+    do { // this substitutes for goto
+        if ((dir > 0 && !origin->selected())) {
+            target = origin;
+            break;
+        }
+
+        bool closest = dir > 0; // when growing, find closest node
+        bool selected = dir < 0; // when growing, consider only unselected nodes
+
+        for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) {
+            NodeList::iterator t = i->second->extremeNode(origin, selected, !selected, closest);
+            if (!t) continue;
+            double dist = Geom::distance(*t, *origin);
+            bool cond = closest ? (dist < extr_dist) : (dist > extr_dist);
+            if (cond) {
+                extr_dist = dist;
+                target = t;
+            }
+        }
+    } while (0);
+
+    if (!target) return;
+    if (dir > 0) {
+        _selection.insert(target.ptr());
+    } else {
+        _selection.erase(target.ptr());
+    }
+}
 void MultiPathManipulator::invertSelection()
 {
     invokeForAll(&PathManipulator::invertSelection);
@@ -343,7 +374,7 @@ void MultiPathManipulator::joinSegment()
         }
     }
 
-    _doneWithCleanup("Join segment");
+    _doneWithCleanup("Join segments");
 }
 
 void MultiPathManipulator::deleteSegments()
@@ -505,6 +536,8 @@ bool MultiPathManipulator::event(GdkEvent *event)
     return false;
 }
 
+/** Commit changes to XML and add undo stack entry based on the action that was done. Invoked
+ * by sub-manipulators, for example TransformHandleSet and ControlPointSelection. */
 void MultiPathManipulator::_commit(CommitEvent cps)
 {
     gchar const *reason = NULL;
@@ -581,6 +614,7 @@ void MultiPathManipulator::_doneWithCleanup(gchar const *reason) {
     _changed.unblock();
 }
 
+/** Get an outline color based on the shape's role (normal, mask, LPE parameter, etc.). */
 guint32 MultiPathManipulator::_getOutlineColor(ShapeRole role)
 {
     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
index b66451ad31b293ee73646d0058780eac7c924511..4fbbf1b054415dd798f60c2616e843285b7e5c2a 100644 (file)
@@ -16,6 +16,7 @@
 #include "forward.h"
 #include "ui/tool/commit-events.h"
 #include "ui/tool/manipulator.h"
+#include "ui/tool/node.h"
 #include "ui/tool/node-types.h"
 #include "ui/tool/shape-record.h"
 
@@ -30,19 +31,16 @@ struct PathSharedData;
 
 /**
  * Manipulator that manages multiple path manipulators active at the same time.
- * It functions like a boost::ptr_set - manipulators added via insert() are retained.
  */
 class MultiPathManipulator : public PointManipulator {
 public:
-    MultiPathManipulator(PathSharedData const &data, sigc::connection &chg);
+    MultiPathManipulator(PathSharedData &data, sigc::connection &chg);
     virtual ~MultiPathManipulator();
     virtual bool event(GdkEvent *event);
 
     bool empty() { return _mmap.empty(); }
     unsigned size() { return _mmap.empty(); }
-    // TODO fix this garbage!
     void setItems(std::set<ShapeRecord> const &);
-    //std::map<SPPath*, std::pair<Geom::Matrix, guint32> > const &items);
     void clear() { _mmap.clear(); }
     void cleanup();
 
@@ -50,8 +48,7 @@ public:
     void selectAll();
     void selectArea(Geom::Rect const &area, bool take);
     void shiftSelection(int dir);
-    void linearGrow(int dir);
-    void spatialGrow(int dir);
+    void spatialGrow(NodeList::iterator center, int dir);
     void invertSelection();
     void invertSelectionInSubpaths();
     void deselect();
@@ -75,7 +72,8 @@ public:
     void showPathDirection(bool show);
     void updateOutlineColors();
     
-    sigc::signal<void> signal_coords_changed;
+    sigc::signal<void> signal_coords_changed; /// Emitted whenever the coordinates
+        /// shown in the status bar need updating
 private:
     typedef std::pair<ShapeRecord, boost::shared_ptr<PathManipulator> > MapPair;
     typedef std::map<ShapeRecord, boost::shared_ptr<PathManipulator> > MapType;
@@ -111,11 +109,15 @@ private:
     guint32 _getOutlineColor(ShapeRole role);
 
     MapType _mmap;
+public:
     PathSharedData const &_path_data;
+private:
     sigc::connection &_changed;
     bool _show_handles;
     bool _show_outline;
     bool _show_path_direction;
+
+    friend class PathManipulator;
 };
 
 } // namespace UI
index 5f792cfc74bb5fc8880ad5ef6d66f91c3bb1d343..4f6d0d5d7d994eeb0db82c13eb8e110c0306d5d5 100644 (file)
@@ -15,7 +15,9 @@
 #include <glib/gi18n.h>
 #include <2geom/transforms.h>
 #include "ui/tool/event-utils.h"
+#include "ui/tool/multi-path-manipulator.h"
 #include "ui/tool/node.h"
+#include "ui/tool/path-manipulator.h"
 #include "display/sp-ctrlline.h"
 #include "display/sp-canvas.h"
 #include "display/sp-canvas-util.h"
@@ -409,37 +411,38 @@ void Node::_fixNeighbors(Geom::Point const &old_pos, Geom::Point const &new_pos)
 
     if (_type == NODE_SMOOTH && !handle->isDegenerate()) {
         handle->setDirection(other->position(), new_pos);
-        /*Geom::Point handle_delta = handle->position() - position();
-        Geom::Point new_delta = Geom::unit_vector(new_direction) * handle_delta.length();
-        handle->setPosition(position() + new_delta);*/
     }
     // also update the handle on the other end of the segment
     if (other->_type == NODE_SMOOTH && !other_handle->isDegenerate()) {
         other_handle->setDirection(new_pos, other->position());
-        /*
-        Geom::Point handle_delta2 = other_handle->position() - other->position();
-        Geom::Point new_delta2 = Geom::unit_vector(new_direction) * handle_delta2.length();
-        other_handle->setPosition(other->position() + new_delta2);*/
     }
 }
 
 void Node::_updateAutoHandles()
 {
     // Recompute the position of automatic handles.
-    if (!_prev() || !_next()) {
+    // For endnodes, retract both handles. (It's only possible to create an end auto node
+    // through the XML editor.)
+    if (isEndNode()) {
         _front.retract();
         _back.retract();
         return;
     }
-    // TODO describe in detail what the code below does
+
+    // Auto nodes automaticaly adjust their handles to give an appearance of smoothness,
+    // no matter what their surroundings are.
     Geom::Point vec_next = _next()->position() - position();
     Geom::Point vec_prev = _prev()->position() - position();
     double len_next = vec_next.length(), len_prev = vec_prev.length();
     if (len_next > 0 && len_prev > 0) {
+        // "dir" is an unit vector perpendicular to the bisector of the angle created
+        // by the previous node, this auto node and the next node.
         Geom::Point dir = Geom::unit_vector((len_prev / len_next) * vec_next - vec_prev);
+        // Handle lengths are equal to 1/3 of the distance from the adjacent node.
         _back.setRelativePos(-dir * (len_prev / 3));
         _front.setRelativePos(dir * (len_next / 3));
     } else {
+        // If any of the adjacent nodes coincides, retract both handles.
         _front.retract();
         _back.retract();
     }
@@ -618,6 +621,33 @@ NodeType Node::parse_nodetype(char x)
     }
 }
 
+bool Node::_eventHandler(GdkEvent *event)
+{
+    static NodeList::iterator origin;
+    static int dir;
+
+    switch (event->type)
+    {
+    case GDK_SCROLL:
+        if (event->scroll.direction == GDK_SCROLL_UP) {
+            dir = 1;
+        } else if (event->scroll.direction == GDK_SCROLL_DOWN) {
+            dir = -1;
+        } else break;
+        origin = NodeList::get_iterator(this);
+
+        if (held_control(event->scroll)) {
+            list()->_list._path_manipulator._multi_path_manipulator.spatialGrow(origin, dir);
+        } else {
+            list()->_list._path_manipulator.linearGrow(origin, dir);
+        }
+        return true;
+    default:
+        break;
+    }
+    return ControlPoint::_eventHandler(event);
+}
+
 void Node::_setState(State state)
 {
     // change node size to match type and selection state
@@ -673,29 +703,31 @@ bool Node::_grabbedHandler(GdkEventMotion *event)
 
 void Node::_draggedHandler(Geom::Point &new_pos, GdkEventMotion *event)
 {
-    if (!held_control(*event)) return;
-    if (held_alt(*event)) {
-        // with Ctrl+Alt, constrain to handle lines
-        // project the new position onto a handle line that is closer
-        Geom::Point origin = _last_drag_origin();
-        Geom::Line line_front(origin, origin + _front.relativePos());
-        Geom::Line line_back(origin, origin + _back.relativePos());
-        double dist_front, dist_back;
-        dist_front = Geom::distance(new_pos, line_front);
-        dist_back = Geom::distance(new_pos, line_back);
-        if (dist_front < dist_back) {
-            new_pos = Geom::projection(new_pos, line_front);
+    if (held_control(*event)) {
+        if (held_alt(*event)) {
+            // with Ctrl+Alt, constrain to handle lines
+            // project the new position onto a handle line that is closer
+            Geom::Point origin = _last_drag_origin();
+            Geom::Line line_front(origin, origin + _front.relativePos());
+            Geom::Line line_back(origin, origin + _back.relativePos());
+            double dist_front, dist_back;
+            dist_front = Geom::distance(new_pos, line_front);
+            dist_back = Geom::distance(new_pos, line_back);
+            if (dist_front < dist_back) {
+                new_pos = Geom::projection(new_pos, line_front);
+            } else {
+                new_pos = Geom::projection(new_pos, line_back);
+            }
         } else {
-            new_pos = Geom::projection(new_pos, line_back);
+            // with Ctrl, constrain to axes
+            // TODO maybe add diagonals when the distance from origin is large enough?
+            Geom::Point origin = _last_drag_origin();
+            Geom::Point delta = new_pos - origin;
+            Geom::Dim2 d = (fabs(delta[Geom::X]) < fabs(delta[Geom::Y])) ? Geom::X : Geom::Y;
+            new_pos[d] = origin[d];
         }
     } else {
-        // with Ctrl, constrain to axes
-        // TODO this probably has to be separated into an AxisConstrainablePoint class
-        // TODO maybe add diagonals when the distance from origin is large enough?
-        Geom::Point origin = _last_drag_origin();
-        Geom::Point delta = new_pos - origin;
-        Geom::Dim2 d = (fabs(delta[Geom::X]) < fabs(delta[Geom::Y])) ? Geom::X : Geom::Y;
-        new_pos[d] = origin[d];
+        // snapping?
     }
 }
 
@@ -711,7 +743,7 @@ Glib::ustring Node::_getTip(unsigned state)
             return C_("Path node tip",
                 "<b>Shift:</b> drag out a handle, click to toggle selection");
         }
-        return C_("Path node statusbar tip", "<b>Shift:</b> click to toggle selection");
+        return C_("Path node tip", "<b>Shift:</b> click to toggle selection");
     }
 
     if (state_held_control(state)) {
@@ -733,7 +765,7 @@ Glib::ustring Node::_getDragTip(GdkEventMotion *event)
     Geom::Point dist = position() - _last_drag_origin();
     GString *x = SP_PX_TO_METRIC_STRING(dist[Geom::X], _desktop->namedview->getDefaultMetric());
     GString *y = SP_PX_TO_METRIC_STRING(dist[Geom::Y], _desktop->namedview->getDefaultMetric());
-    Glib::ustring ret = format_tip(C_("Path node statusbar tip", "Move by %s, %s"),
+    Glib::ustring ret = format_tip(C_("Path node tip", "Move by %s, %s"),
         x->str, y->str);
     g_string_free(x, TRUE);
     g_string_free(y, TRUE);
@@ -915,7 +947,7 @@ void NodeList::clear()
 
 NodeList::iterator NodeList::erase(iterator i)
 {
-    // some acrobatics are required to ensure that the node is valid when deleted;
+    // some gymnastics are required to ensure that the node is valid when deleted;
     // otherwise the code that updates handle visibility will break
     Node *rm = static_cast<Node*>(i._node);
     ListNode *rmnext = rm->next, *rmprev = rm->prev;
@@ -927,6 +959,8 @@ NodeList::iterator NodeList::erase(iterator i)
     return i;
 }
 
+// TODO this method is nasty and ugly!
+// converting SubpathList to an intrusive list might allow us to get rid of it
 void NodeList::kill()
 {
     for (SubpathList::iterator i = _list.begin(); i != _list.end(); ++i) {
index 9a36642eb64cbadfbbe8059be4efc2ad568d55ae..68ad63ba9b831d065737f0d3405cfa2ea2f7a28a 100644 (file)
@@ -39,6 +39,7 @@ namespace Inkscape {
 namespace UI {
 
 class PathManipulator;
+class MultiPathManipulator;
 
 class Node;
 class Handle;
@@ -136,6 +137,7 @@ public:
 
     static char const *node_type_to_localized_string(NodeType type);
 protected:
+    virtual bool _eventHandler(GdkEvent *event);
     virtual void _setState(State state);
     virtual Glib::ustring _getTip(unsigned state);
     virtual Glib::ustring _getDragTip(GdkEventMotion *event);
@@ -294,12 +296,13 @@ class SubpathList : public std::list< boost::shared_ptr<NodeList> > {
 public:
     typedef std::list< boost::shared_ptr<NodeList> > list_type;
 
-    SubpathList() {}
+    SubpathList(PathManipulator &pm) : _path_manipulator(pm) {}
 
     sigc::signal<void, Node *> signal_insert_node;
     sigc::signal<void, Node *> signal_remove_node;
 private:
     list_type _nodelists;
+    PathManipulator &_path_manipulator;
     friend class NodeList;
     friend class Node;
     friend class Handle;
index f247e553789d2378999e35d0b31534a96148189a..42db45321e82f13e9e3353d9dace1b96a2be4927 100644 (file)
@@ -85,10 +85,11 @@ private:
 
 void build_segment(Geom::PathBuilder &, Node *, Node *);
 
-PathManipulator::PathManipulator(PathSharedData const &data, SPPath *path,
+PathManipulator::PathManipulator(MultiPathManipulator &mpm, SPPath *path,
         Geom::Matrix const &et, guint32 outline_color, Glib::ustring lpe_key)
-    : PointManipulator(data.node_data.desktop, *data.node_data.selection)
-    , _path_data(data)
+    : PointManipulator(mpm._path_data.node_data.desktop, *mpm._path_data.node_data.selection)
+    , _subpaths(*this)
+    , _multi_path_manipulator(mpm)
     , _path(path)
     , _spcurve(NULL)
     , _dragpoint(new CurveDragPoint(*this))
@@ -109,7 +110,7 @@ PathManipulator::PathManipulator(PathSharedData const &data, SPPath *path,
 
     _getGeometry();
 
-    _outline = sp_canvas_bpath_new(_path_data.outline_group, NULL);
+    _outline = sp_canvas_bpath_new(_multi_path_manipulator._path_data.outline_group, NULL);
     sp_canvas_item_hide(_outline);
     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(_outline), outline_color, 1.0,
         SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
@@ -297,6 +298,11 @@ void PathManipulator::shiftSelection(int dir)
     }
 }
 
+void PathManipulator::linearGrow(NodeList::iterator center, int dir)
+{
+    g_message("linearGrow unimplemented");
+}
+
 /** Invert selection in the entire path. */
 void PathManipulator::invertSelection()
 {
@@ -343,7 +349,7 @@ void PathManipulator::insertNodes()
 }
 
 /** Replace contiguous selections of nodes in each subpath with one node. */
-void PathManipulator::weldNodes(NodeList::iterator const &preserve_pos)
+void PathManipulator::weldNodes(NodeList::iterator preserve_pos)
 {
     if (!_num_selected) return;
     _dragpoint->setVisible(false);
@@ -453,7 +459,7 @@ void PathManipulator::breakNodes()
                 ins = new_sp;
             }
 
-            Node *n = new Node(_path_data.node_data, cur->position());
+            Node *n = new Node(_multi_path_manipulator._path_data.node_data, cur->position());
             ins->insert(ins->end(), n);
             cur->setType(NODE_CUSP, false);
             n->back()->setRelativePos(cur->back()->relativePos());
@@ -747,7 +753,7 @@ NodeList::iterator PathManipulator::subdivideSegment(NodeList::iterator first, d
     NodeList::iterator inserted;
     if (first->front()->isDegenerate() && second->back()->isDegenerate()) {
         // for a line segment, insert a cusp node
-        Node *n = new Node(_path_data.node_data,
+        Node *n = new Node(_multi_path_manipulator._path_data.node_data,
             Geom::lerp(t, first->position(), second->position()));
         n->setType(NODE_CUSP, false);
         inserted = list.insert(insert_at, n);
@@ -759,7 +765,7 @@ NodeList::iterator PathManipulator::subdivideSegment(NodeList::iterator first, d
         std::vector<Geom::Point> seg1 = div.first.points(), seg2 = div.second.points();
 
         // set new handle positions
-        Node *n = new Node(_path_data.node_data, seg2[0]);
+        Node *n = new Node(_multi_path_manipulator._path_data.node_data, seg2[0]);
         n->back()->setPosition(seg1[2]);
         n->front()->setPosition(seg2[1]);
         n->setType(NODE_SMOOTH, false);
@@ -771,6 +777,38 @@ NodeList::iterator PathManipulator::subdivideSegment(NodeList::iterator first, d
     return inserted;
 }
 
+/** Find the node that is closest/farthest from the origin
+ * @param origin Point of reference
+ * @param search_selected Consider selected nodes
+ * @param search_unselected Consider unselected nodes
+ * @param closest If true, return closest node, if false, return farthest
+ * @return The matching node, or an empty iterator if none found
+ */
+NodeList::iterator PathManipulator::extremeNode(NodeList::iterator origin, bool search_selected,
+    bool search_unselected, bool closest)
+{
+    NodeList::iterator match;
+    double extr_dist = closest ? HUGE_VAL : -HUGE_VAL;
+    if (_num_selected == 0 && !search_unselected) return match;
+
+    for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) {
+        for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) {
+            if(j->selected()) {
+                if (!search_selected) continue;
+            } else {
+                if (!search_unselected) continue;
+            }
+            double dist = Geom::distance(*j, *origin);
+            bool cond = closest ? (dist < extr_dist) : (dist > extr_dist);
+            if (cond) {
+                match = j;
+                extr_dist = dist;
+            }
+        }
+    }
+    return match;
+}
+
 /** Called by the XML observer when something else than us modifies the path. */
 void PathManipulator::_externalChange(unsigned type)
 {
@@ -839,7 +877,7 @@ void PathManipulator::_createControlPointsFromGeometry()
         SubpathPtr subpath(new NodeList(_subpaths));
         _subpaths.push_back(subpath);
 
-        Node *previous_node = new Node(_path_data.node_data, pit->initialPoint());
+        Node *previous_node = new Node(_multi_path_manipulator._path_data.node_data, pit->initialPoint());
         subpath->push_back(previous_node);
         Geom::Curve const &cseg = pit->back_closed();
         bool fuse_ends = pit->closed()
@@ -856,7 +894,7 @@ void PathManipulator::_createControlPointsFromGeometry()
                 /* regardless of segment type, create a new node at the end
                  * of this segment (unless this is the last segment of a closed path
                  * with a degenerate closing segment */
-                current_node = new Node(_path_data.node_data, pos);
+                current_node = new Node(_multi_path_manipulator._path_data.node_data, pos);
                 subpath->push_back(current_node);
             }
             // if this is a bezier segment, move handles appropriately
index e0d8c68ca9490e9668f5dcf280c536f9b42b793d..82ce7fd5d3e560689936299f7634f49af49fe779 100644 (file)
@@ -26,6 +26,7 @@ struct SPCanvasItem;
 
 namespace Inkscape {
 namespace XML { class Node; }
+
 namespace UI {
 
 class PathManipulator;
@@ -33,6 +34,8 @@ class ControlPointSelection;
 class PathManipulatorObserver;
 class CurveDragPoint;
 class PathCanvasGroups;
+class MultiPathManipulator;
+class Node;
 
 struct PathSharedData {
     NodeSharedData node_data;
@@ -49,7 +52,7 @@ class PathManipulator : public PointManipulator {
 public:
     typedef SPPath *ItemType;
 
-    PathManipulator(PathSharedData const &data, SPPath *path, Geom::Matrix const &edit_trans,
+    PathManipulator(MultiPathManipulator &mpm, SPPath *path, Geom::Matrix const &edit_trans,
         guint32 outline_color, Glib::ustring lpe_key);
     ~PathManipulator();
     virtual bool event(GdkEvent *);
@@ -64,13 +67,12 @@ public:
     void selectAll();
     void selectArea(Geom::Rect const &);
     void shiftSelection(int dir);
-    void linearGrow(int dir);
-    void spatialGrow(int dir);
+    void linearGrow(NodeList::iterator center, int dir);
     void invertSelection();
     void invertSelectionInSubpaths();
 
     void insertNodes();
-    void weldNodes(NodeList::iterator const &preserve_pos = NodeList::iterator());
+    void weldNodes(NodeList::iterator preserve_pos = NodeList::iterator());
     void weldSegments();
     void breakNodes();
     void deleteNodes(bool keep_shape = true);
@@ -85,6 +87,8 @@ public:
     void hideDragPoint();
 
     NodeList::iterator subdivideSegment(NodeList::iterator after, double t);
+    NodeList::iterator extremeNode(NodeList::iterator origin, bool search_selected,
+        bool search_unselected, bool closest);
 
     static bool is_item_type(void *item);
 private:
@@ -117,7 +121,7 @@ private:
     double _getStrokeTolerance();
 
     SubpathList _subpaths;
-    PathSharedData const &_path_data;
+    MultiPathManipulator &_multi_path_manipulator;
     SPPath *_path;
     SPCurve *_spcurve; // in item coordinates
     SPCanvasItem *_outline;
@@ -134,6 +138,7 @@ private:
 
     friend class PathManipulatorObserver;
     friend class CurveDragPoint;
+    friend class Node;
 };
 
 } // namespace UI
index f3e2847e4be1da5a1079dee4519fe4db8bf1a62b..d6181dbf4db69b54a56efdcbb114ec343d8a627e 100644 (file)
@@ -24,7 +24,7 @@
 #include "ui/tool/transform-handle-set.h"
 
 // FIXME BRAIN DAMAGE WARNING: this is a global variable in select-context.cpp
-// Should be moved to a location where it can be accessed globally
+// It should be moved to a header
 extern GdkPixbuf *handles[];
 GType sp_select_context_get_type();