Code

Fix mask editing behavior on undo and outline display for masks/clips;
authorKrzysztof Kosiński <tweenk.pl@gmail.com>
Sat, 5 Dec 2009 02:48:07 +0000 (03:48 +0100)
committerKrzysztof Kosiński <tweenk.pl@gmail.com>
Sat, 5 Dec 2009 02:48:07 +0000 (03:48 +0100)
prepare to fix LPE path parameters

src/ui/tool/multi-path-manipulator.cpp
src/ui/tool/multi-path-manipulator.h
src/ui/tool/node-tool.cpp
src/ui/tool/node-tool.h
src/ui/tool/path-manipulator.cpp
src/ui/tool/path-manipulator.h
src/ui/tool/shape-record.h [new file with mode: 0644]

index 6b245702a1fbc36d2e37f4586f7fbaf1e26d74bd..7c539f6b90850e5140d63337a24bb1a4b4966a0e 100644 (file)
@@ -16,6 +16,7 @@
 #include "desktop-handles.h"
 #include "document.h"
 #include "message-stack.h"
+#include "preferences.h"
 #include "sp-path.h"
 #include "ui/tool/control-point-selection.h"
 #include "ui/tool/event-utils.h"
@@ -130,36 +131,48 @@ void MultiPathManipulator::cleanup()
     }
 }
 
-void MultiPathManipulator::setItems(std::map<SPPath*,
-    std::pair<Geom::Matrix, guint32> > const &items)
+void MultiPathManipulator::setItems(std::set<ShapeRecord> const &s)
 {
-    typedef std::map<SPPath*, std::pair<Geom::Matrix, guint32> > TransMap;
-    typedef std::set<SPPath*> ItemSet;
-    ItemSet to_remove, to_add, current, new_items;
-
-    for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) {
-        current.insert(i->first);
-    }
-    for (TransMap::const_iterator i = items.begin(); i != items.end(); ++i) {
-        new_items.insert(i->first);
+    std::set<ShapeRecord> shapes(s);
+
+    // iterate over currently edited items, modifying / removing them as necessary
+    for (MapType::iterator i = _mmap.begin(); i != _mmap.end();) {
+        std::set<ShapeRecord>::iterator si = shapes.find(i->first);
+        if (si == shapes.end()) {
+            // This item is no longer supposed to be edited - remove its manipulator
+            _mmap.erase(i++);
+        } else {
+            ShapeRecord const &sr = i->first;
+            ShapeRecord const &sr_new = *si;
+            // if the shape record differs, replace the key only and modify other values
+            if (sr.edit_transform != sr_new.edit_transform ||
+                sr.role != sr_new.role)
+            {
+                boost::shared_ptr<PathManipulator> hold(i->second);
+                if (sr.edit_transform != sr_new.edit_transform)
+                    hold->setControlsTransform(sr_new.edit_transform);
+                if (sr.role != sr_new.role) {
+                    //hold->setOutlineColor(_getOutlineColor(sr_new.role));
+                }
+                _mmap.erase(sr);
+                _mmap.insert(std::make_pair(sr_new, hold));
+            }
+            shapes.erase(si); // remove the processed record
+            ++i;
+        }
     }
 
-    std::set_difference(current.begin(), current.end(), new_items.begin(), new_items.end(),
-        std::inserter(to_remove, to_remove.end()));
-    std::set_difference(new_items.begin(), new_items.end(), current.begin(), current.end(),
-        std::inserter(to_add, to_add.end()));
-
-    for (ItemSet::iterator i = to_remove.begin(); i != to_remove.end(); ++i) {
-        _mmap.erase(*i);
-    }
-    for (ItemSet::iterator i = to_add.begin(); i != to_add.end(); ++i) {
-        boost::shared_ptr<PathManipulator> pm;
-        TransMap::const_iterator f = items.find(*i);
-        pm.reset(new PathManipulator(_path_data, *i, f->second.first, f->second.second));
-        pm->showHandles(_show_handles);
-        pm->showOutline(_show_outline);
-        pm->showPathDirection(_show_path_direction);
-        _mmap.insert(std::make_pair(*i, pm));
+    // add newly selected items
+    for (std::set<ShapeRecord>::iterator i = shapes.begin(); i != shapes.end(); ++i) {
+        ShapeRecord const &r = *i;
+        if (!SP_IS_PATH(r.item)) continue;
+        boost::shared_ptr<PathManipulator> newpm(new PathManipulator(_path_data, (SPPath*) r.item,
+            r.edit_transform, _getOutlineColor(r.role)));
+        newpm->showHandles(_show_handles);
+        // always show outlines for clips and masks
+        newpm->showOutline(_show_outline || r.role != SHAPE_ROLE_NORMAL);
+        newpm->showPathDirection(_show_path_direction);
+        _mmap.insert(std::make_pair(r, newpm));
     }
 }
 
@@ -369,7 +382,10 @@ void MultiPathManipulator::move(Geom::Point const &delta)
 
 void MultiPathManipulator::showOutline(bool show)
 {
-    invokeForAll(&PathManipulator::showOutline, show);
+    for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) {
+        // always show outlines for clipping paths and masks
+        i->second->showOutline(show || i->first.role != SHAPE_ROLE_NORMAL);
+    }
     _show_outline = show;
 }
 
@@ -385,6 +401,13 @@ void MultiPathManipulator::showPathDirection(bool show)
     _show_path_direction = show;
 }
 
+void MultiPathManipulator::updateOutlineColors()
+{
+    //for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) {
+    //    i->second->setOutlineColor(_getOutlineColor(i->first.role));
+    //}
+}
+
 bool MultiPathManipulator::event(GdkEvent *event)
 {
     switch (event->type) {
@@ -553,6 +576,22 @@ void MultiPathManipulator::_doneWithCleanup(gchar const *reason) {
     _changed.unblock();
 }
 
+guint32 MultiPathManipulator::_getOutlineColor(ShapeRole role)
+{
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+    switch(role) {
+    case SHAPE_ROLE_CLIPPING_PATH:
+        return prefs->getColor("/tools/nodes/clipping_path_color", 0x00ff00ff);
+    case SHAPE_ROLE_MASK:
+        return prefs->getColor("/tools/nodes/mask_color", 0x0000ffff);
+    case SHAPE_ROLE_LPE_PARAM:
+        return prefs->getColor("/tools/nodes/lpe_param_color", 0xb700ffff);
+    case SHAPE_ROLE_NORMAL:
+    default:
+        return prefs->getColor("/tools/nodes/outline_color", 0xff0000ff);
+    }
+}
+
 } // namespace UI
 } // namespace Inkscape
 
index 89c86b0196574d47e5c46899361502d0f8bb689d..b66451ad31b293ee73646d0058780eac7c924511 100644 (file)
 #include <sigc++/connection.h>
 #include "display/display-forward.h"
 #include "forward.h"
+#include "ui/tool/commit-events.h"
 #include "ui/tool/manipulator.h"
 #include "ui/tool/node-types.h"
-#include "ui/tool/commit-events.h"
+#include "ui/tool/shape-record.h"
 
 struct SPCanvasGroup;
 
@@ -40,7 +41,8 @@ public:
     bool empty() { return _mmap.empty(); }
     unsigned size() { return _mmap.empty(); }
     // TODO fix this garbage!
-    void setItems(std::map<SPPath*, std::pair<Geom::Matrix, guint32> > const &items);
+    void setItems(std::set<ShapeRecord> const &);
+    //std::map<SPPath*, std::pair<Geom::Matrix, guint32> > const &items);
     void clear() { _mmap.clear(); }
     void cleanup();
 
@@ -71,12 +73,12 @@ public:
     void showOutline(bool show);
     void showHandles(bool show);
     void showPathDirection(bool show);
-    void setOutlineTransform(SPPath *item, Geom::Matrix const &t);
+    void updateOutlineColors();
     
     sigc::signal<void> signal_coords_changed;
 private:
-    typedef std::pair<SPPath*, boost::shared_ptr<PathManipulator> > MapPair;
-    typedef std::map<SPPath*, boost::shared_ptr<PathManipulator> > MapType;
+    typedef std::pair<ShapeRecord, boost::shared_ptr<PathManipulator> > MapPair;
+    typedef std::map<ShapeRecord, boost::shared_ptr<PathManipulator> > MapType;
 
     template <typename R>
     void invokeForAll(R (PathManipulator::*method)()) {
@@ -106,7 +108,7 @@ private:
     void _commit(CommitEvent cps);
     void _done(gchar const *);
     void _doneWithCleanup(gchar const *);
-    void _storeClipMaskItems(SPObject *obj, std::set<SPPath*> &, bool);
+    guint32 _getOutlineColor(ShapeRole role);
 
     MapType _mmap;
     PathSharedData const &_path_data;
index a57057c92b0d7c77375679d111b190d08ae83191..31c7227447226d9ba83d3b4b945fdb9aa32ab95e 100644 (file)
@@ -32,6 +32,7 @@
 #include "ui/tool/multi-path-manipulator.h"
 #include "ui/tool/path-manipulator.h"
 #include "ui/tool/selector.h"
+#include "ui/tool/shape-record.h"
 
 #include "pixmaps/cursor-node.xpm"
 #include "pixmaps/cursor-node-d.xpm"
@@ -108,6 +109,7 @@ void ink_node_tool_init(InkNodeTool *nt)
     event_context->hot_y = 1;
 
     new (&nt->_selection_changed_connection) sigc::connection();
+    new (&nt->_selection_modified_connection) sigc::connection();
     new (&nt->_mouseover_changed_connection) sigc::connection();
     //new (&nt->_mgroup) Inkscape::UI::ManipulatorGroup(nt->desktop);
     new (&nt->_selected_nodes) CSelPtr();
@@ -123,6 +125,7 @@ void ink_node_tool_dispose(GObject *object)
     nt->enableGrDrag(false);
 
     nt->_selection_changed_connection.disconnect();
+    nt->_selection_modified_connection.disconnect();
     nt->_mouseover_changed_connection.disconnect();
     nt->_multipath.~MultiPathPtr();
     nt->_selected_nodes.~CSelPtr();
@@ -138,6 +141,7 @@ void ink_node_tool_dispose(GObject *object)
     
     nt->_path_data.~PathSharedDataPtr();
     nt->_selection_changed_connection.~connection();
+    nt->_selection_modified_connection.~connection();
     nt->_mouseover_changed_connection.~connection();
 
     if (nt->_node_message_context) {
@@ -183,6 +187,12 @@ void ink_node_tool_setup(SPEventContext *ec)
             sigc::bind<0>(
                 sigc::ptr_fun(&ink_node_tool_selection_changed),
                 nt));
+    nt->_selection_modified_connection.disconnect();
+    nt->_selection_modified_connection =
+        selection->connectModified(
+            sigc::hide(sigc::bind<0>(
+                sigc::ptr_fun(&ink_node_tool_selection_changed),
+                nt)));
     nt->_mouseover_changed_connection.disconnect();
     nt->_mouseover_changed_connection = 
         Inkscape::UI::ControlPoint::signal_mouseover_change.connect(
@@ -298,6 +308,36 @@ void store_clip_mask_items(SPItem *clipped, SPObject *obj, std::map<SPItem*,
     }
 }
 
+/** Recursively collect ShapeRecords */
+void gather_items(InkNodeTool *nt, SPItem *base, SPObject *obj, Inkscape::UI::ShapeRole role,
+    std::set<Inkscape::UI::ShapeRecord> &s)
+{
+    using namespace Inkscape::UI;
+    if (!obj) return;
+    if (role != SHAPE_ROLE_NORMAL && (SP_IS_GROUP(obj) || SP_IS_OBJECTGROUP(obj))) {
+        for (SPObject *c = obj->children; c; c = c->next) {
+            gather_items(nt, base, c, role, s);
+        }
+    } else if (SP_IS_ITEM(obj)) {
+        SPItem *item = static_cast<SPItem*>(obj);
+        ShapeRecord r;
+        r.item = item;
+        // TODO add support for objectBoundingBox
+        r.edit_transform = base ? sp_item_i2doc_affine(base) : Geom::identity();
+        r.role = role;
+        r.edit_original = false;
+        if (s.insert(r).second) {
+            // this item was encountered the first time
+            if (nt->edit_clipping_paths && item->clip_ref) {
+                gather_items(nt, item, item->clip_ref->getObject(), SHAPE_ROLE_CLIPPING_PATH, s);
+            }
+            if (nt->edit_masks && item->mask_ref) {
+                gather_items(nt, item, item->mask_ref->getObject(), SHAPE_ROLE_MASK, s);
+            }
+        }
+    }
+}
+
 struct IsPath {
     bool operator()(SPItem *i) const { return SP_IS_PATH(i); }
 };
@@ -305,41 +345,28 @@ struct IsPath {
 void ink_node_tool_selection_changed(InkNodeTool *nt, Inkscape::Selection *sel)
 {
     using namespace Inkscape::UI;
+
+    std::set<ShapeRecord> shapes;
+
     // TODO this is ugly!!!
-    typedef std::map<SPItem*, std::pair<Geom::Matrix, guint32> > TransMap;
-    typedef std::map<SPPath*, std::pair<Geom::Matrix, guint32> > PathMap;
+    //typedef std::map<SPItem*, std::pair<Geom::Matrix, guint32> > TransMap;
+    //typedef std::map<SPPath*, std::pair<Geom::Matrix, guint32> > PathMap;
     GSList const *ilist = sel->itemList();
-    TransMap items;
-    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
 
     for (GSList *i = const_cast<GSList*>(ilist); i; i = i->next) {
         SPObject *obj = static_cast<SPObject*>(i->data);
         if (SP_IS_ITEM(obj)) {
-            items.insert(std::make_pair(SP_ITEM(obj),
-                std::make_pair(Geom::identity(),
-                prefs->getColor("/tools/nodes/outline_color", 0xff0000ff))));
-            if (nt->edit_clipping_paths && SP_ITEM(i->data)->clip_ref) {
-                store_clip_mask_items(SP_ITEM(i->data),
-                    SP_OBJECT(SP_ITEM(i->data)->clip_ref->getObject()), items,
-                    nt->desktop->dt2doc(),
-                    prefs->getColor("/tools/nodes/clipping_path_color", 0x00ff00ff));
-            }
-            if (nt->edit_masks && SP_ITEM(i->data)->mask_ref) {
-                store_clip_mask_items(SP_ITEM(i->data),
-                    SP_OBJECT(SP_ITEM(i->data)->mask_ref->getObject()), items,
-                    nt->desktop->dt2doc(),
-                    prefs->getColor("/tools/nodes/mask_color", 0x0000ffff));
-            }
+            gather_items(nt, NULL, static_cast<SPItem*>(obj), SHAPE_ROLE_NORMAL, shapes);
         }
     }
 
     // ugly hack: set the first editable non-path item for knotholder
     // maybe use multiple ShapeEditors for now, to allow editing many shapes at once?
     bool something_set = false;
-    for (TransMap::iterator i = items.begin(); i != items.end(); ++i) {
-        SPItem *obj = i->first;
-        if (SP_IS_SHAPE(obj) && !SP_IS_PATH(obj)) {
-            nt->shape_editor->set_item(obj, SH_KNOTHOLDER);
+    for (std::set<ShapeRecord>::iterator i = shapes.begin(); i != shapes.end(); ++i) {
+        ShapeRecord const &r = *i;
+        if (SP_IS_SHAPE(r.item) && !SP_IS_PATH(r.item)) {
+            nt->shape_editor->set_item(r.item, SH_KNOTHOLDER);
             something_set = true;
             break;
         }
@@ -347,16 +374,8 @@ void ink_node_tool_selection_changed(InkNodeTool *nt, Inkscape::Selection *sel)
     if (!something_set) {
         nt->shape_editor->unset_item(SH_KNOTHOLDER);
     }
-    
-    PathMap p;
-    for (TransMap::iterator i = items.begin(); i != items.end(); ++i) {
-        if (SP_IS_PATH(i->first)) {
-            p.insert(std::make_pair(SP_PATH(i->first),
-                std::make_pair(i->second.first, i->second.second)));
-        }
-    }
 
-    nt->_multipath->setItems(p);
+    nt->_multipath->setItems(shapes);
     ink_node_tool_update_tip(nt, NULL);
     nt->desktop->updateNow();
 }
index f47ea0ccb799a3e1fcbc7bf60043a1c6506422a1..65b16ff72172e8ffb80a2bc0da335cb70bc176ef 100644 (file)
@@ -46,6 +46,7 @@ struct InkNodeTool : public SPEventContext
 {
     sigc::connection _selection_changed_connection;
     sigc::connection _mouseover_changed_connection;
+    sigc::connection _selection_modified_connection;
     Inkscape::MessageContext *_node_message_context;
     SPItem *flashed_item;
     Inkscape::Display::TemporaryItem *flash_tempitem;
index ef85723306e1f284cab3183c8429051bcb517c5d..e9ec78b2e08a51d6e892d7d4ec90421c7207a35c 100644 (file)
@@ -690,6 +690,18 @@ void PathManipulator::showPathDirection(bool show)
     _updateOutline();
 }
 
+void PathManipulator::setControlsTransform(Geom::Matrix const &tnew)
+{
+    Geom::Matrix delta = _i2d_transform.inverse() * _edit_transform.inverse() * tnew * _i2d_transform;
+    _edit_transform = tnew;
+    for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) {
+        for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) {
+            j->transform(delta);
+        }
+    }
+    _createGeometryFromControlPoints();
+}
+
 /** Insert a node in the segment beginning with the supplied iterator,
  * at the given time value */
 NodeList::iterator PathManipulator::subdivideSegment(NodeList::iterator first, double t)
index 9ed9e4fb6b0c9d796a9dbb46bd02c4558feb39ee..01a2b6cbf5bbc3c756c7d812252e044d69f0508d 100644 (file)
@@ -80,7 +80,7 @@ public:
     void showOutline(bool show);
     void showHandles(bool show);
     void showPathDirection(bool show);
-    void setOutlineTransform(Geom::Matrix const &);
+    void setControlsTransform(Geom::Matrix const &);
 
     NodeList::iterator subdivideSegment(NodeList::iterator after, double t);
 
diff --git a/src/ui/tool/shape-record.h b/src/ui/tool/shape-record.h
new file mode 100644 (file)
index 0000000..cc2f8be
--- /dev/null
@@ -0,0 +1,56 @@
+/** @file
+ * Structures that store data needed for shape editing which are not contained
+ * directly in the XML node
+ */
+/* 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_SHAPE_RECORD_H
+#define SEEN_UI_TOOL_SHAPE_RECORD_H
+
+#include <boost/operators.hpp>
+#include <2geom/matrix.h>
+
+class SPItem;
+namespace Inkscape {
+namespace UI {
+
+/** Role of the shape in the drawing - affects outline display and color */
+enum ShapeRole {
+    SHAPE_ROLE_NORMAL,
+    SHAPE_ROLE_CLIPPING_PATH,
+    SHAPE_ROLE_MASK,
+    SHAPE_ROLE_LPE_PARAM // implies edit_original set to true in ShapeRecord
+};
+
+struct ShapeRecord :
+    public boost::totally_ordered<ShapeRecord>
+{
+    SPItem *item; // SP node for the edited shape
+    Geom::Matrix edit_transform; // how to transform controls - used for clipping paths and masks
+    ShapeRole role;
+    bool edit_original; // whether to use original-d instead of d for editing
+
+    inline bool operator==(ShapeRecord const &o) const { return item == o.item; }
+    inline bool operator<(ShapeRecord const &o) const { return item < o.item; }
+};
+
+} // namespace UI
+} // namespace Inkscape
+
+#endif
+
+/*
+  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 :