summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 820678c)
raw | patch | inline | side by side (parent: 820678c)
author | Krzysztof Kosiński <tweenk.pl@gmail.com> | |
Sun, 14 Mar 2010 17:38:50 +0000 (18:38 +0100) | ||
committer | Krzysztof 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 | patch | blob | history | |
src/ui/tool/modifier-tracker.cpp | [new file with mode: 0644] | patch | blob |
src/ui/tool/modifier-tracker.h | [new file with mode: 0644] | patch | blob |
src/ui/tool/multi-path-manipulator.cpp | patch | blob | history | |
src/ui/tool/multi-path-manipulator.h | patch | blob | history | |
src/ui/tool/node.cpp | patch | blob | history | |
src/ui/tool/node.h | patch | blob | history | |
src/ui/tool/path-manipulator.cpp | patch | blob | history | |
src/ui/tool/path-manipulator.h | patch | blob | history |
index e149430216795d3da4325e40d48322eb5168aaed..4640a3cea84182dfb2b2bd4cf7cf534f7027a958 100644 (file)
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
--- /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
--- /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)
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)
#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"
PathSharedData const &_path_data;
private:
sigc::connection &_changed;
+ ModifierTracker _tracker;
bool _show_handles;
bool _show_outline;
bool _show_path_direction;
diff --git a/src/ui/tool/node.cpp b/src/ui/tool/node.cpp
index ae924f694a9d3dd7f412f45339b48c17e13f33cb..b72da1374b7b7918f2d6e9b82ebe45289679ac82 100644 (file)
--- a/src/ui/tool/node.cpp
+++ b/src/ui/tool/node.cpp
: _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()
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;
}
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);
}
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()
// 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;
}
}
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);
}
diff --git a/src/ui/tool/node.h b/src/ui/tool/node.h
index c798a1fdbb9cd7b5ae34a269c485f2bb1be95f3e..e502ddea1b151394a51d014f4c358489a97bb83b 100644 (file)
--- a/src/ui/tool/node.h
+++ b/src/ui/tool/node.h
*/
struct ListNode {
- ListNode *next;
- ListNode *prev;
- NodeList *list;
+ ListNode *ln_next;
+ ListNode *ln_prev;
+ NodeList *ln_list;
};
struct NodeSharedData {
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);
/// 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>
// 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; }
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> {
~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()); }
}
// 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); }
typedef std::list< boost::shared_ptr<NodeList> > list_type;
SubpathList(PathManipulator &pm) : _path_manipulator(pm) {}
+ PathManipulator &pm() { return _path_manipulator; }
private:
list_type _nodelists;
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;
}
}
}
+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)
{
// 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);
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)
class PathCanvasGroups;
class MultiPathManipulator;
class Node;
+class Handle;
struct PathSharedData {
NodeSharedData node_data;
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);
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,
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;