Code

Implement keyboard shortcuts for single handle adjustments.
authorKrzysztof Kosiński <tweenk.pl@gmail.com>
Sun, 14 Mar 2010 17:38:50 +0000 (18:38 +0100)
committerKrzysztof Kosiński <tweenk.pl@gmail.com>
Sun, 14 Mar 2010 17:38:50 +0000 (18:38 +0100)
Minor disambiguating cleanup in node.h.

src/ui/tool/Makefile_insert
src/ui/tool/modifier-tracker.cpp [new file with mode: 0644]
src/ui/tool/modifier-tracker.h [new file with mode: 0644]
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

index e149430216795d3da4325e40d48322eb5168aaed..4640a3cea84182dfb2b2bd4cf7cf534f7027a958 100644 (file)
@@ -12,6 +12,8 @@ ink_common_sources += \
        ui/tool/event-utils.h                   \
        ui/tool/manipulator.cpp                 \
        ui/tool/manipulator.h                   \
+       ui/tool/modifier-tracker.cpp                    \
+       ui/tool/modifier-tracker.h                      \
        ui/tool/multi-path-manipulator.cpp      \
        ui/tool/multi-path-manipulator.h        \
        ui/tool/node.cpp                        \
diff --git a/src/ui/tool/modifier-tracker.cpp b/src/ui/tool/modifier-tracker.cpp
new file mode 100644 (file)
index 0000000..8c6033b
--- /dev/null
@@ -0,0 +1,93 @@
+/** @file
+ * Fine-grained modifier tracker for event handling.
+ */
+/* Authors:
+ *   Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright (C) 2009 Authors
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <gdk/gdk.h>
+#include <gdk/gdkkeysyms.h>
+#include "ui/tool/event-utils.h"
+#include "ui/tool/modifier-tracker.h"
+
+namespace Inkscape {
+namespace UI {
+
+ModifierTracker::ModifierTracker()
+    : _left_shift(false)
+    , _right_shift(false)
+    , _left_ctrl(false)
+    , _right_ctrl(false)
+    , _left_alt(false)
+    , _right_alt(false)
+{}
+
+bool ModifierTracker::event(GdkEvent *event)
+{
+    switch (event->type) {
+    case GDK_KEY_PRESS:
+        switch (shortcut_key(event->key)) {
+        case GDK_Shift_L:
+            _left_shift = true;
+            break;
+        case GDK_Shift_R:
+            _right_shift = true;
+            break;
+        case GDK_Control_L:
+            _left_ctrl = true;
+            break;
+        case GDK_Control_R:
+            _right_ctrl = true;
+            break;
+        case GDK_Alt_L:
+            _left_alt = true;
+            break;
+        case GDK_Alt_R:
+            _right_alt = true;
+            break;
+        }
+        break;
+    case GDK_KEY_RELEASE:
+        switch (shortcut_key(event->key)) {
+        case GDK_Shift_L:
+            _left_shift = false;
+            break;
+        case GDK_Shift_R:
+            _right_shift = false;
+            break;
+        case GDK_Control_L:
+            _left_ctrl = false;
+            break;
+        case GDK_Control_R:
+            _right_ctrl = false;
+            break;
+        case GDK_Alt_L:
+            _left_alt = false;
+            break;
+        case GDK_Alt_R:
+            _right_alt = false;
+            break;
+        }
+        break;
+    default: break;
+    }
+
+    return false;
+}
+
+} // namespace UI
+} // namespace Inkscape
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/ui/tool/modifier-tracker.h b/src/ui/tool/modifier-tracker.h
new file mode 100644 (file)
index 0000000..55538ea
--- /dev/null
@@ -0,0 +1,54 @@
+/** @file
+ * Fine-grained modifier tracker for event handling.
+ */
+/* Authors:
+ *   Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright (C) 2009 Authors
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef SEEN_UI_TOOL_MODIFIER_TRACKER_H
+#define SEEN_UI_TOOL_MODIFIER_TRACKER_H
+
+#include <gdk/gdk.h>
+
+namespace Inkscape {
+namespace UI {
+
+class ModifierTracker {
+public:
+    ModifierTracker();
+    bool event(GdkEvent *);
+
+    bool leftShift() const { return _left_shift; }
+    bool rightShift() const { return _right_shift; }
+    bool leftControl() const { return _left_ctrl; }
+    bool rightControl() const { return _right_ctrl; }
+    bool leftAlt() const { return _left_alt; }
+    bool rightAlt() const { return _right_alt; }
+
+private:
+    bool _left_shift;
+    bool _right_shift;
+    bool _left_ctrl;
+    bool _right_ctrl;
+    bool _left_alt;
+    bool _right_alt;
+};
+
+} // namespace UI
+} // namespace Inkscape
+
+#endif // SEEN_UI_TOOL_MODIFIER_TRACKER_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
index 9accbd0ae636b840e4f8df72277513f67a4d0954..fe97058c456d2231c624c93d4063c83e444b7396 100644 (file)
@@ -440,9 +440,60 @@ void MultiPathManipulator::updateOutlineColors()
 
 bool MultiPathManipulator::event(GdkEvent *event)
 {
+    _tracker.event(event);
+    guint key = 0;
+    if (event->type == GDK_KEY_PRESS) {
+        key = shortcut_key(event->key);
+    }
+
+    // Single handle adjustments go here.
+    if (_selection.size() == 1 && event->type == GDK_KEY_PRESS) {
+        do {
+            Node *n = dynamic_cast<Node *>(*_selection.begin());
+            if (!n) break;
+
+            PathManipulator &pm = n->nodeList().subpathList().pm();
+
+            int which = 0;
+            if (_tracker.rightAlt() || _tracker.rightControl()) {
+                which = 1;
+            }
+            if (_tracker.leftAlt() || _tracker.leftControl()) {
+                if (which != 0) break; // ambiguous
+                which = -1;
+            }
+            if (which == 0) break; // no handle chosen
+            bool one_pixel = _tracker.leftAlt() || _tracker.rightAlt();
+
+            switch (key) {
+            // single handle functions
+            // rotation
+            case GDK_bracketleft:
+            case GDK_braceleft:
+                pm.rotateHandle(n, which, 1, one_pixel);
+                break;
+            case GDK_bracketright:
+            case GDK_braceright:
+                pm.rotateHandle(n, which, -1, one_pixel);
+                break;
+            // adjust length
+            case GDK_period:
+            case GDK_greater:
+                pm.scaleHandle(n, which, 1, one_pixel);
+                break;
+            case GDK_comma:
+            case GDK_less:
+                pm.scaleHandle(n, which, -1, one_pixel);
+                break;
+            }
+            return true;
+        } while(0);
+    }
+
+
     switch (event->type) {
     case GDK_KEY_PRESS:
-        switch (shortcut_key(event->key)) {
+        switch (key) {
         case GDK_Insert:
         case GDK_KP_Insert:
             // Insert - insert nodes in the middle of selected segments
index 121818f97b54245aac25a301232ab41b272b6386..181ae6d1d6c32643447b916d79b0cb609362fe5a 100644 (file)
@@ -16,6 +16,7 @@
 #include "forward.h"
 #include "ui/tool/commit-events.h"
 #include "ui/tool/manipulator.h"
+#include "ui/tool/modifier-tracker.h"
 #include "ui/tool/node.h"
 #include "ui/tool/node-types.h"
 #include "ui/tool/shape-record.h"
@@ -110,6 +111,7 @@ public:
     PathSharedData const &_path_data;
 private:
     sigc::connection &_changed;
+    ModifierTracker _tracker;
     bool _show_handles;
     bool _show_outline;
     bool _show_path_direction;
index ae924f694a9d3dd7f412f45339b48c17e13f33cb..b72da1374b7b7918f2d6e9b82ebe45289679ac82 100644 (file)
@@ -1149,9 +1149,9 @@ NodeList::NodeList(SubpathList &splist)
     : _list(splist)
     , _closed(false)
 {
-    this->list = this;
-    this->next = this;
-    this->prev = this;
+    this->ln_list = this;
+    this->ln_next = this;
+    this->ln_prev = this;
 }
 
 NodeList::~NodeList()
@@ -1161,13 +1161,13 @@ NodeList::~NodeList()
 
 bool NodeList::empty()
 {
-    return next == this;
+    return ln_next == this;
 }
 
 NodeList::size_type NodeList::size()
 {
     size_type sz = 0;
-    for (ListNode *ln = next; ln != this; ln = ln->next) ++sz;
+    for (ListNode *ln = ln_next; ln != this; ln = ln->ln_next) ++sz;
     return sz;
 }
 
@@ -1198,11 +1198,11 @@ NodeList::iterator NodeList::before(double t, double *fracpart)
 NodeList::iterator NodeList::insert(iterator i, Node *x)
 {
     ListNode *ins = i._node;
-    x->next = ins;
-    x->prev = ins->prev;
-    ins->prev->next = x;
-    ins->prev = x;
-    x->ListNode::list = this;
+    x->ln_next = ins;
+    x->ln_prev = ins->ln_prev;
+    ins->ln_prev->ln_next = x;
+    ins->ln_prev = x;
+    x->ln_list = this;
     return iterator(x);
 }
 
@@ -1221,48 +1221,48 @@ void NodeList::splice(iterator pos, NodeList &list, iterator i)
 void NodeList::splice(iterator pos, NodeList &/*list*/, iterator first, iterator last)
 {
     ListNode *ins_beg = first._node, *ins_end = last._node, *at = pos._node;
-    for (ListNode *ln = ins_beg; ln != ins_end; ln = ln->next) {
-        ln->list = this;
+    for (ListNode *ln = ins_beg; ln != ins_end; ln = ln->ln_next) {
+        ln->ln_list = this;
     }
-    ins_beg->prev->next = ins_end;
-    ins_end->prev->next = at;
-    at->prev->next = ins_beg;
+    ins_beg->ln_prev->ln_next = ins_end;
+    ins_end->ln_prev->ln_next = at;
+    at->ln_prev->ln_next = ins_beg;
 
-    ListNode *atprev = at->prev;
-    at->prev = ins_end->prev;
-    ins_end->prev = ins_beg->prev;
-    ins_beg->prev = atprev;
+    ListNode *atprev = at->ln_prev;
+    at->ln_prev = ins_end->ln_prev;
+    ins_end->ln_prev = ins_beg->ln_prev;
+    ins_beg->ln_prev = atprev;
 }
 
 void NodeList::shift(int n)
 {
     // 1. make the list perfectly cyclic
-    next->prev = prev;
-    prev->next = next;
+    ln_next->ln_prev = ln_prev;
+    ln_prev->ln_next = ln_next;
     // 2. find new begin
-    ListNode *new_begin = next;
+    ListNode *new_begin = ln_next;
     if (n > 0) {
-        for (; n > 0; --n) new_begin = new_begin->next;
+        for (; n > 0; --n) new_begin = new_begin->ln_next;
     } else {
-        for (; n < 0; ++n) new_begin = new_begin->prev;
+        for (; n < 0; ++n) new_begin = new_begin->ln_prev;
     }
     // 3. relink begin to list
-    next = new_begin;
-    prev = new_begin->prev;
-    new_begin->prev->next = this;
-    new_begin->prev = this;
+    ln_next = new_begin;
+    ln_prev = new_begin->ln_prev;
+    new_begin->ln_prev->ln_next = this;
+    new_begin->ln_prev = this;
 }
 
 void NodeList::reverse()
 {
-    for (ListNode *ln = next; ln != this; ln = ln->prev) {
-        std::swap(ln->next, ln->prev);
+    for (ListNode *ln = ln_next; ln != this; ln = ln->ln_prev) {
+        std::swap(ln->ln_next, ln->ln_prev);
         Node *node = static_cast<Node*>(ln);
         Geom::Point save_pos = node->front()->position();
         node->front()->setPosition(node->back()->position());
         node->back()->setPosition(save_pos);
     }
-    std::swap(next, prev);
+    std::swap(ln_next, ln_prev);
 }
 
 void NodeList::clear()
@@ -1275,11 +1275,11 @@ NodeList::iterator NodeList::erase(iterator i)
     // 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;
+    ListNode *rmnext = rm->ln_next, *rmprev = rm->ln_prev;
     ++i;
     delete rm;
-    rmprev->next = rmnext;
-    rmnext->prev = rmprev;
+    rmprev->ln_next = rmnext;
+    rmnext->ln_prev = rmprev;
     return i;
 }
 
@@ -1296,10 +1296,10 @@ void NodeList::kill()
 }
 
 NodeList &NodeList::get(Node *n) {
-    return *(n->list());
+    return n->nodeList();
 }
 NodeList &NodeList::get(iterator const &i) {
-    return *(i._node->list);
+    return *(i._node->ln_list);
 }
 
 
index c798a1fdbb9cd7b5ae34a269c485f2bb1be95f3e..e502ddea1b151394a51d014f4c358489a97bb83b 100644 (file)
@@ -66,9 +66,9 @@ struct SubpathListMember : public ListMember<SubpathListMember> {
 */
 
 struct ListNode {
-    ListNode *next;
-    ListNode *prev;
-    NodeList *list;
+    ListNode *ln_next;
+    ListNode *ln_prev;
+    NodeList *ln_list;
 };
 
 struct NodeSharedData {
@@ -139,7 +139,7 @@ public:
     Handle *front() { return &_front; }
     Handle *back()  { return &_back;  }
     static NodeType parse_nodetype(char x);
-    NodeList *list() { return static_cast<ListNode*>(this)->list; }
+    NodeList &nodeList() { return *(static_cast<ListNode*>(this)->ln_list); }
     void sink();
 
     static char const *node_type_to_localized_string(NodeType type);
@@ -182,24 +182,28 @@ private:
 
 /// Iterator for editable nodes
 /** Use this class for all operations that require some knowledge about the node's
- * neighbors. It works like a bidirectional iterator.
+ * neighbors. It is a bidirectional iterator.
  *
  * Because paths can be cyclic, node iterators have two different ways to
- * increment and decrement them. Nodes can be iterated over either in the
- * sequence order, which always has a beginning and an end, or in the path order,
- * which can be cyclic (moving to the next node never yields the end iterator).
+ * increment and decrement them. When using ++/--, the end iterator will eventually
+ * be returned. Whent using advance()/retreat(), the end iterator will only be returned
+ * when the path is open. If it's closed, calling advance() will cycle indefinitely.
+ * This is particularly useful for cases where the adjacency of nodes is more important
+ * than their sequence order.
  *
  * When @a i is a node iterator, then:
  * - <code>++i</code> moves the iterator to the next node in sequence order;
  * - <code>--i</code> moves the iterator to the previous node in sequence order;
- * - <code>i.next()</code> returns the next node with wrap-around if the path is cyclic;
- * - <code>i.prev()</code> returns the previous node with wrap-around if the path is cyclic.
+ * - <code>i.next()</code> returns the next node with wrap-around;
+ * - <code>i.prev()</code> returns the previous node with wrap-around;
+ * - <code>i.advance()</code> moves the iterator to the next node with wrap-around;
+ * - <code>i.retreat()</code> moves the iterator to the previous node with wrap-around.
  *
  * next() and prev() do not change their iterator. They can return the end iterator
  * if the path is open.
  *
- * Unlike most other iterators, you can check whether a node iterator is invalid
- * (is an end iterator) without having access to the iterator's container.
+ * Unlike most other iterators, you can check whether you've reached the end of the list
+ * without having access to the iterator's container.
  * Simply use <code>if (i) { ...</code>
  * */
 template <typename N>
@@ -215,11 +219,11 @@ public:
     // default copy, default assign
 
     self &operator++() {
-        _node = _node->next;
+        _node = _node->ln_next;
         return *this;
     }
     self &operator--() {
-        _node = _node->prev;
+        _node = _node->ln_prev;
         return *this;
     }
     bool operator==(self const &other) const { return _node == other._node; }
@@ -232,13 +236,14 @@ public:
 
     self next() const;
     self prev() const;
+    self &advance();
+    self &retreat();
 private:
     NodeIterator(ListNode const *n)
         : _node(const_cast<ListNode*>(n))
     {}
     ListNode *_node;
     friend class NodeList;
-    friend class std::tr1::hash<self>;
 };
 
 class NodeList : ListNode, boost::noncopyable, public boost::enable_shared_from_this<NodeList> {
@@ -259,9 +264,9 @@ public:
     ~NodeList();
 
     // iterators
-    iterator begin() { return iterator(next); }
+    iterator begin() { return iterator(ln_next); }
     iterator end() { return iterator(this); }
-    const_iterator begin() const { return const_iterator(next); }
+    const_iterator begin() const { return const_iterator(ln_next); }
     const_iterator end() const { return const_iterator(this); }
     reverse_iterator rbegin() { return reverse_iterator(end()); }
     reverse_iterator rend() { return reverse_iterator(begin()); }
@@ -305,11 +310,12 @@ public:
     }
 
     // member access - undefined results when the list is empty
-    Node &front() { return *static_cast<Node*>(next); }
-    Node &back() { return *static_cast<Node*>(prev); }
+    Node &front() { return *static_cast<Node*>(ln_next); }
+    Node &back() { return *static_cast<Node*>(ln_prev); }
 
     // HACK remove this subpath from its path. This will be removed later.
     void kill();
+    SubpathList &subpathList() { return _list; }
 
     static iterator get_iterator(Node *n) { return iterator(n); }
     static const_iterator get_iterator(Node const *n) { return const_iterator(n); }
@@ -335,6 +341,7 @@ public:
     typedef std::list< boost::shared_ptr<NodeList> > list_type;
 
     SubpathList(PathManipulator &pm) : _path_manipulator(pm) {}
+    PathManipulator &pm() { return _path_manipulator; }
 
 private:
     list_type _nodelists;
@@ -360,43 +367,32 @@ inline PathManipulator &Handle::_pm() {
     return _parent->_pm();
 }
 inline PathManipulator &Node::_pm() {
-    return list()->_list._path_manipulator;
+    return nodeList().subpathList().pm();
 }
 
 // definitions for node iterator
 template <typename N>
 NodeIterator<N>::operator bool() const {
-    return _node && static_cast<ListNode*>(_node->list) != _node;
+    return _node && static_cast<ListNode*>(_node->ln_list) != _node;
 }
 template <typename N>
 NodeIterator<N> NodeIterator<N>::next() const {
     NodeIterator<N> ret(*this);
     ++ret;
-    if (G_UNLIKELY(!ret) && _node->list->closed()) ++ret;
+    if (G_UNLIKELY(!ret) && _node->ln_list->closed()) ++ret;
     return ret;
 }
 template <typename N>
 NodeIterator<N> NodeIterator<N>::prev() const {
     NodeIterator<N> ret(*this);
     --ret;
-    if (G_UNLIKELY(!ret) && _node->list->closed()) --ret;
+    if (G_UNLIKELY(!ret) && _node->ln_list->closed()) --ret;
     return ret;
 }
 
 } // namespace UI
 } // namespace Inkscape
 
-namespace std {
-namespace tr1 {
-template <typename N>
-struct hash< Inkscape::UI::NodeIterator<N> > : public unary_function<Inkscape::UI::NodeIterator<N>, size_t> {
-    size_t operator()(Inkscape::UI::NodeIterator<N> const &ni) const {
-        return reinterpret_cast<size_t>(ni._node);
-    }
-};
-}
-}
-
 #endif
 
 /*
index 0b0254108d37e2851a7fa45a1189cd098902b109..d395d0e0a443f290a5450b4e8634cff5f3504997 100644 (file)
@@ -598,10 +598,10 @@ unsigned PathManipulator::_deleteStretch(NodeList::iterator start, NodeList::ite
 
     // We can't use nl->erase(start, end), because it would break when the stretch
     // crosses the beginning of a closed subpath
-    NodeList *nl = start->list();
+    NodeList &nl = start->nodeList();
     while (start != end) {
         NodeList::iterator next = start.next();
-        nl->erase(start);
+        nl.erase(start);
         start = next;
     }
 
@@ -728,6 +728,68 @@ void PathManipulator::setSegmentType(SegmentType type)
     }
 }
 
+void PathManipulator::scaleHandle(Node *n, int which, int dir, bool pixel)
+{
+    if (n->type() == NODE_SYMMETRIC || n->type() == NODE_AUTO) {
+        n->setType(NODE_SMOOTH);
+    }
+    Handle *h = _chooseHandle(n, which);
+    double length_change;
+
+    if (pixel) {
+        length_change = 1.0 / _desktop->current_zoom() * dir;
+    } else {
+        Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+        length_change = prefs->getDoubleLimited("/options/defaultscale/value", 2, 1, 1000);
+        length_change *= dir;
+    }
+
+    Geom::Point relpos = h->relativePos();
+    double rellen = relpos.length();
+    h->setRelativePos(relpos * ((rellen + length_change) / rellen));
+    update();
+
+    gchar const *key = which < 0 ? "handle:scale:left" : "handle:scale:right";
+    _commit(_("Scale handle"), key);
+}
+
+void PathManipulator::rotateHandle(Node *n, int which, int dir, bool pixel)
+{
+    if (n->type() != NODE_CUSP) {
+        n->setType(NODE_CUSP);
+    }
+    Handle *h = _chooseHandle(n, which);
+    double angle;
+
+    if (pixel) {
+        // Rotate by "one pixel"
+        angle = atan2(1.0 / _desktop->current_zoom(), h->length()) * dir;
+    } else {
+        Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+        int snaps = prefs->getIntLimited("/options/rotationsnapsperpi/value", 12, 1, 1000);
+        angle = M_PI * dir / snaps;
+    }
+    h->setRelativePos(h->relativePos() * Geom::Rotate(angle));
+    update();
+    gchar const *key = which < 0 ? "handle:rotate:left" : "handle:rotate:right";
+    _commit(_("Rotate handle"), key);
+}
+
+Handle *PathManipulator::_chooseHandle(Node *n, int which)
+{
+    Geom::Point f = n->front()->position(), b = n->back()->position();
+    if (which < 0) {
+        // pick left handle.
+        // we just swap the handles and pick the right handle below.
+        std::swap(f, b);
+    }
+    if (f[Geom::X] >= b[Geom::X]) {
+        return n->front();
+    } else {
+        return n->back();
+    }
+}
+
 /** Set the visibility of handles. */
 void PathManipulator::showHandles(bool show)
 {
@@ -1187,11 +1249,11 @@ bool PathManipulator::_nodeClicked(Node *n, GdkEventButton *event)
         // Ctrl+Alt+click: delete nodes
         hideDragPoint();
         NodeList::iterator iter = NodeList::get_iterator(n);
-        NodeList *nl = iter->list();
+        NodeList &nl = iter->nodeList();
 
-        if (nl->size() <= 1 || (nl->size() <= 2 && !nl->closed())) {
+        if (nl.size() <= 1 || (nl.size() <= 2 && !nl.closed())) {
             // Removing last node of closed path - delete it
-            nl->kill();
+            nl.kill();
         } else {
             // In other cases, delete the node under cursor
             _deleteStretch(iter, iter.next(), true);
@@ -1304,6 +1366,13 @@ void PathManipulator::_commit(Glib::ustring const &annotation)
     sp_document_done(sp_desktop_document(_desktop), SP_VERB_CONTEXT_NODE, annotation.data());
 }
 
+void PathManipulator::_commit(Glib::ustring const &annotation, gchar const *key)
+{
+    writeXML();
+    sp_document_maybe_done(sp_desktop_document(_desktop), key, SP_VERB_CONTEXT_NODE,
+            annotation.data());
+}
+
 /** Update the position of the curve drag point such that it is over the nearest
  * point of the path. */
 void PathManipulator::_updateDragPoint(Geom::Point const &evp)
index 1e9e5057547952cfd0a56b4dbc385aa23a20d5d6..a8f1c957ee5f4a2a499afb3ae1035d4f72c8c41b 100644 (file)
@@ -36,6 +36,7 @@ class CurveDragPoint;
 class PathCanvasGroups;
 class MultiPathManipulator;
 class Node;
+class Handle;
 
 struct PathSharedData {
     NodeSharedData node_data;
@@ -76,6 +77,9 @@ public:
     void reverseSubpaths(bool selected_only);
     void setSegmentType(SegmentType);
 
+    void scaleHandle(Node *n, int which, int dir, bool pixel);
+    void rotateHandle(Node *n, int which, int dir, bool pixel);
+
     void showOutline(bool show);
     void showHandles(bool show);
     void showPathDirection(bool show);
@@ -83,6 +87,7 @@ public:
     void setLiveObjects(bool set);
     void setControlsTransform(Geom::Matrix const &);
     void hideDragPoint();
+    MultiPathManipulator &mpm() { return _multi_path_manipulator; }
 
     NodeList::iterator subdivideSegment(NodeList::iterator after, double t);
     NodeList::iterator extremeNode(NodeList::iterator origin, bool search_selected,
@@ -113,9 +118,11 @@ private:
     void _externalChange(unsigned type);
     void _removeNodesFromSelection();
     void _commit(Glib::ustring const &annotation);
+    void _commit(Glib::ustring const &annotation, gchar const *key);
     void _updateDragPoint(Geom::Point const &);
     void _updateOutlineOnZoomChange();
     double _getStrokeTolerance();
+    Handle *_chooseHandle(Node *n, int which);
 
     SubpathList _subpaths;
     MultiPathManipulator &_multi_path_manipulator;