Code

now that selection description includes style (filtered, clipped), we need to update...
[inkscape.git] / src / selection-chemistry.cpp
index 9695d58e91dd6ed8ac25f8dab61e3b8137025f23..badf27ecb45caa4d93541f5e806a371f43b66fd9 100644 (file)
@@ -25,7 +25,6 @@
 #include <gtkmm/clipboard.h>
 
 #include "svg/svg.h"
-#include "inkscape.h"
 #include "desktop.h"
 #include "desktop-style.h"
 #include "selection.h"
 #include <glibmm/i18n.h>
 #include "libnr/nr-matrix-rotate-ops.h"
 #include "libnr/nr-matrix-translate-ops.h"
-#include "libnr/nr-rotate-fns.h"
 #include "libnr/nr-scale-ops.h"
-#include "libnr/nr-scale-translate-ops.h"
-#include "libnr/nr-translate-matrix-ops.h"
-#include "libnr/nr-translate-scale-ops.h"
+#include <libnr/nr-matrix-ops.h>
+#include <2geom/transforms.h>
 #include "xml/repr.h"
 #include "style.h"
 #include "document-private.h"
@@ -63,7 +60,7 @@
 #include "sp-pattern.h"
 #include "sp-radial-gradient-fns.h"
 #include "sp-namedview.h"
-#include "prefs-utils.h"
+#include "preferences.h"
 #include "sp-offset.h"
 #include "sp-clippath.h"
 #include "sp-mask.h"
@@ -83,6 +80,9 @@
 #include "gradient-drag.h"
 #include "uri-references.h"
 #include "libnr/nr-convert2geom.h"
+#include "display/curve.h"
+#include "display/canvas-bpath.h"
+#include "inkscape-private.h"
 
 // For clippath editing
 #include "tools-switch.h"
@@ -92,8 +92,8 @@
 
 #include "ui/clipboard.h"
 
-using NR::X;
-using NR::Y;
+using Geom::X;
+using Geom::Y;
 
 /* The clipboard handling is in ui/clipboard.cpp now. There are some legacy functions left here,
 because the layer manipulation code uses them. It should be rewritten specifically
@@ -103,7 +103,7 @@ for that purpose. */
  * Copies repr and its inherited css style elements, along with the accumulated transform 'full_t',
  * then prepends the copy to 'clip'.
  */
-void sp_selection_copy_one (Inkscape::XML::Node *repr, NR::Matrix full_t, GSList **clip, Inkscape::XML::Document* xml_doc)
+void sp_selection_copy_one (Inkscape::XML::Node *repr, Geom::Matrix full_t, GSList **clip, Inkscape::XML::Document* xml_doc)
 {
     Inkscape::XML::Node *copy = repr->duplicate(xml_doc);
 
@@ -130,7 +130,7 @@ void sp_selection_copy_impl (GSList const *items, GSList **clip, Inkscape::XML::
 
     // Copy item reprs:
     for (GSList *i = (GSList *) sorted_items; i != NULL; i = i->next) {
-        sp_selection_copy_one (SP_OBJECT_REPR (i->data), from_2geom(sp_item_i2doc_affine(SP_ITEM (i->data))), clip, xml_doc);
+        sp_selection_copy_one (SP_OBJECT_REPR (i->data), sp_item_i2doc_affine(SP_ITEM (i->data)), clip, xml_doc);
     }
 
     *clip = g_slist_reverse(*clip);
@@ -148,10 +148,10 @@ GSList *sp_selection_paste_impl (SPDocument *doc, SPObject *parent, GSList **cli
         Inkscape::XML::Node *copy = repr->duplicate(xml_doc);
 
         // premultiply the item transform by the accumulated parent transform in the paste layer
-        NR::Matrix local = from_2geom(sp_item_i2doc_affine(SP_ITEM(parent)));
-        if (!local.test_identity()) {
+        Geom::Matrix local (sp_item_i2doc_affine(SP_ITEM(parent)));
+        if (!local.isIdentity()) {
             gchar const *t_str = copy->attribute("transform");
-            NR::Matrix item_t (NR::identity());
+            Geom::Matrix item_t (Geom::identity());
             if (t_str)
                 sp_svg_transform_read(t_str, &item_t);
             item_t *= local.inverse();
@@ -181,9 +181,8 @@ void sp_selection_delete_impl(GSList const *items, bool propagate = true, bool p
 }
 
 
-void sp_selection_delete()
+void sp_selection_delete(SPDesktop *desktop)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (desktop == NULL) {
         return;
     }
@@ -220,14 +219,27 @@ void sp_selection_delete()
                      _("Delete"));
 }
 
-/* fixme: sequencing */
-void sp_selection_duplicate(bool suppressDone)
+void add_ids_recursive (std::vector<const gchar *> &ids, SPObject *obj)
+{
+    if (!obj)
+        return;
+
+    ids.push_back(SP_OBJECT_ID(obj));
+
+    if (SP_IS_GROUP(obj)) {
+        for (SPObject *child = sp_object_first_child(obj) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
+            add_ids_recursive (ids, child);
+        }
+    }
+}
+
+void sp_selection_duplicate(SPDesktop *desktop, bool suppressDone)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (desktop == NULL)
         return;
 
-    Inkscape::XML::Document* xml_doc = sp_document_repr_doc(desktop->doc());
+    SPDocument *doc = desktop->doc();
+    Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc);
     Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     // check if something is selected
@@ -240,22 +252,61 @@ void sp_selection_duplicate(bool suppressDone)
 
     selection->clear();
 
-    // sorting items from different parents sorts each parent's subset without possibly mixing them, just what we need
+    // sorting items from different parents sorts each parent's subset without possibly mixing
+    // them, just what we need
     reprs = g_slist_sort(reprs, (GCompareFunc) sp_repr_compare_position);
 
     GSList *newsel = NULL;
 
+    std::vector<const gchar *> old_ids;
+    std::vector<const gchar *> new_ids;
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+    bool relink_clones = prefs->getBool("/options/relinkclonesonduplicate/value");
+
     while (reprs) {
-        Inkscape::XML::Node *parent = ((Inkscape::XML::Node *) reprs->data)->parent();
-        Inkscape::XML::Node *copy = ((Inkscape::XML::Node *) reprs->data)->duplicate(xml_doc);
+        Inkscape::XML::Node *old_repr = (Inkscape::XML::Node *) reprs->data;
+        Inkscape::XML::Node *parent = old_repr->parent();
+        Inkscape::XML::Node *copy = old_repr->duplicate(xml_doc);
 
         parent->appendChild(copy);
 
+        if (relink_clones) {
+            SPObject *old_obj = doc->getObjectByRepr(old_repr);
+            SPObject *new_obj = doc->getObjectByRepr(copy);
+            add_ids_recursive (old_ids, old_obj);
+            add_ids_recursive (new_ids, new_obj);
+        }
+
         newsel = g_slist_prepend(newsel, copy);
         reprs = g_slist_remove(reprs, reprs->data);
         Inkscape::GC::release(copy);
     }
 
+    if (relink_clones) {
+
+        g_assert (old_ids.size() == new_ids.size());
+
+        for(unsigned int i = 0; i < old_ids.size(); i++) {
+            const gchar *id = old_ids[i];
+            SPObject *old_clone = doc->getObjectById(id);
+            if (SP_IS_USE(old_clone)) {
+                SPItem *orig = sp_use_get_original(SP_USE(old_clone));
+                for(unsigned int j = 0; j < old_ids.size(); j++) {
+                    if (!strcmp(SP_OBJECT_ID(orig), old_ids[j])) {
+                        // we have both orig and clone in selection, relink
+                        // std::cout << id  << " old, its ori: " << SP_OBJECT_ID(orig) << "; will relink:" << new_ids[i] << " to " << new_ids[j] << "\n";
+                        gchar *newref = g_strdup_printf ("#%s", new_ids[j]);
+                        SPObject *new_clone = doc->getObjectById(new_ids[i]);
+                        SP_OBJECT_REPR(new_clone)->setAttribute("xlink:href", newref);
+                        new_clone->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+                        g_free (newref);
+                    }
+                }
+            }
+        }
+    }
+
+
     if ( !suppressDone ) {
         sp_document_done(sp_desktop_document(desktop), SP_VERB_EDIT_DUPLICATE,
                          _("Duplicate"));
@@ -266,9 +317,8 @@ void sp_selection_duplicate(bool suppressDone)
     g_slist_free(newsel);
 }
 
-void sp_edit_clear_all()
+void sp_edit_clear_all(SPDesktop *dt)
 {
-    SPDesktop *dt = SP_ACTIVE_DESKTOP;
     if (!dt)
         return;
 
@@ -309,19 +359,19 @@ get_all_items (GSList *list, SPObject *from, SPDesktop *desktop, bool onlyvisibl
     return list;
 }
 
-void sp_edit_select_all_full (bool force_all_layers, bool invert)
+void sp_edit_select_all_full (SPDesktop *dt, bool force_all_layers, bool invert)
 {
-    SPDesktop *dt = SP_ACTIVE_DESKTOP;
     if (!dt)
         return;
 
     Inkscape::Selection *selection = sp_desktop_selection(dt);
 
     g_return_if_fail(SP_IS_GROUP(dt->currentLayer()));
-
-    PrefsSelectionContext inlayer = (PrefsSelectionContext)prefs_get_int_attribute ("options.kbselection", "inlayer", PREFS_SELECTION_LAYER);
-    bool onlyvisible = prefs_get_int_attribute ("options.kbselection", "onlyvisible", 1);
-    bool onlysensitive = prefs_get_int_attribute ("options.kbselection", "onlysensitive", 1);
+    
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+    PrefsSelectionContext inlayer = (PrefsSelectionContext) prefs->getInt("/options/kbselection/inlayer", PREFS_SELECTION_LAYER);
+    bool onlyvisible = prefs->getBool("/options/kbselection/onlyvisible", true);
+    bool onlysensitive = prefs->getBool("/options/kbselection/onlysensitive", true);
 
     GSList *items = NULL;
 
@@ -375,29 +425,28 @@ void sp_edit_select_all_full (bool force_all_layers, bool invert)
     }
 }
 
-void sp_edit_select_all ()
+void sp_edit_select_all (SPDesktop *desktop)
 {
-    sp_edit_select_all_full (false, false);
+    sp_edit_select_all_full (desktop, false, false);
 }
 
-void sp_edit_select_all_in_all_layers ()
+void sp_edit_select_all_in_all_layers (SPDesktop *desktop)
 {
-    sp_edit_select_all_full (true, false);
+    sp_edit_select_all_full (desktop, true, false);
 }
 
-void sp_edit_invert ()
+void sp_edit_invert (SPDesktop *desktop)
 {
-    sp_edit_select_all_full (false, true);
+    sp_edit_select_all_full (desktop, false, true);
 }
 
-void sp_edit_invert_in_all_layers ()
+void sp_edit_invert_in_all_layers (SPDesktop *desktop)
 {
-    sp_edit_select_all_full (true, true);
+    sp_edit_select_all_full (desktop, true, true);
 }
 
-void sp_selection_group()
+void sp_selection_group(SPDesktop *desktop)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (desktop == NULL)
         return;
 
@@ -441,10 +490,10 @@ void sp_selection_group()
                 // At this point, current may already have no item, due to its being a clone whose original is already moved away
                 // So we copy it artificially calculating the transform from its repr->attr("transform") and the parent transform
                 gchar const *t_str = current->attribute("transform");
-                NR::Matrix item_t (NR::identity());
+                Geom::Matrix item_t (Geom::identity());
                 if (t_str)
                     sp_svg_transform_read(t_str, &item_t);
-                item_t *= from_2geom(sp_item_i2doc_affine(SP_ITEM(doc->getObjectByRepr(current->parent()))));
+                item_t *= sp_item_i2doc_affine(SP_ITEM(doc->getObjectByRepr(current->parent())));
                 //FIXME: when moving both clone and original from a transformed group (either by
                 //grouping into another parent, or by cut/paste) the transform from the original's
                 //parent becomes embedded into original itself, and this affects its clones. Fix
@@ -487,9 +536,8 @@ void sp_selection_group()
     Inkscape::GC::release(group);
 }
 
-void sp_selection_ungroup()
+void sp_selection_ungroup(SPDesktop *desktop)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (desktop == NULL)
         return;
 
@@ -546,6 +594,36 @@ void sp_selection_ungroup()
                      _("Ungroup"));
 }
 
+/** Replace all groups in the list with their member objects, recursively; returns a new list, frees old */
+GSList *
+sp_degroup_list (GSList *items)
+{
+    GSList *out = NULL;
+    bool has_groups = false;
+    for (GSList *item = items; item; item = item->next) {
+        if (!SP_IS_GROUP(item->data)) {
+            out = g_slist_prepend(out, item->data);
+        } else {
+            has_groups = true;
+            GSList *members = sp_item_group_item_list (SP_GROUP(item->data));
+            for (GSList *member = members; member; member = member->next) {
+                out = g_slist_prepend(out, member->data);
+            }
+            g_slist_free (members);
+        }
+    }
+    out = g_slist_reverse (out);
+    g_slist_free (items);
+
+    if (has_groups) { // recurse if we unwrapped a group - it may have contained others
+        out = sp_degroup_list (out);
+    }
+
+    return out;
+}
+
+/** If items in the list have a common parent, return it, otherwise return NULL */
 static SPGroup *
 sp_item_list_common_parent_group(GSList const *items)
 {
@@ -567,14 +645,14 @@ sp_item_list_common_parent_group(GSList const *items)
 }
 
 /** Finds out the minimum common bbox of the selected items. */
-static NR::Maybe<NR::Rect>
+static Geom::OptRect
 enclose_items(GSList const *items)
 {
     g_assert(items != NULL);
 
-    NR::Maybe<NR::Rect> r = NR::Nothing();
+    Geom::OptRect r;
     for (GSList const *i = items; i; i = i->next) {
-        r = NR::union_bounds(r, sp_item_bbox_desktop((SPItem *) i->data));
+        r = Geom::unify(r, sp_item_bbox_desktop((SPItem *) i->data));
     }
     return r;
 }
@@ -594,9 +672,8 @@ prev_sibling(SPObject *child)
 }
 
 void
-sp_selection_raise()
+sp_selection_raise(SPDesktop *desktop)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (!desktop)
         return;
 
@@ -621,7 +698,7 @@ sp_selection_raise()
     rev = g_slist_sort(rev, (GCompareFunc) sp_item_repr_compare_position);
 
     // Determine the common bbox of the selected items.
-    NR::Maybe<NR::Rect> selected = enclose_items(items);
+    Geom::OptRect selected = enclose_items(items);
 
     // Iterate over all objects in the selection (starting from top).
     if (selected) {
@@ -631,7 +708,7 @@ sp_selection_raise()
             for (SPObject *newref = child->next; newref; newref = newref->next) {
                 // if the sibling is an item AND overlaps our selection,
                 if (SP_IS_ITEM(newref)) {
-                    NR::Maybe<NR::Rect> newref_bbox = sp_item_bbox_desktop(SP_ITEM(newref));
+                    Geom::OptRect newref_bbox = sp_item_bbox_desktop(SP_ITEM(newref));
                     if ( newref_bbox && selected->intersects(*newref_bbox) ) {
                         // AND if it's not one of our selected objects,
                         if (!g_slist_find((GSList *) items, newref)) {
@@ -653,9 +730,8 @@ sp_selection_raise()
                      Q_("undo_action|Raise"));
 }
 
-void sp_selection_raise_to_top()
+void sp_selection_raise_to_top(SPDesktop *desktop)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (desktop == NULL)
         return;
 
@@ -690,9 +766,8 @@ void sp_selection_raise_to_top()
 }
 
 void
-sp_selection_lower()
+sp_selection_lower(SPDesktop *desktop)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (desktop == NULL)
         return;
 
@@ -713,7 +788,7 @@ sp_selection_lower()
     Inkscape::XML::Node *grepr = SP_OBJECT_REPR(group);
 
     // Determine the common bbox of the selected items.
-    NR::Maybe<NR::Rect> selected = enclose_items(items);
+    Geom::OptRect selected = enclose_items(items);
 
     /* Construct direct-ordered list of selected children. */
     GSList *rev = g_slist_copy((GSList *) items);
@@ -728,7 +803,7 @@ sp_selection_lower()
             for (SPObject *newref = prev_sibling(child); newref; newref = prev_sibling(newref)) {
                 // if the sibling is an item AND overlaps our selection,
                 if (SP_IS_ITEM(newref)) {
-                    NR::Maybe<NR::Rect> ref_bbox = sp_item_bbox_desktop(SP_ITEM(newref));
+                    Geom::OptRect ref_bbox = sp_item_bbox_desktop(SP_ITEM(newref));
                     if ( ref_bbox && selected->intersects(*ref_bbox) ) {
                         // AND if it's not one of our selected objects,
                         if (!g_slist_find((GSList *) items, newref)) {
@@ -753,9 +828,8 @@ sp_selection_lower()
                      _("Lower"));
 }
 
-void sp_selection_lower_to_bottom()
+void sp_selection_lower_to_bottom(SPDesktop *desktop)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (desktop == NULL)
         return;
 
@@ -815,10 +889,10 @@ sp_redo(SPDesktop *desktop, SPDocument *)
             desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Nothing to redo."));
 }
 
-void sp_selection_cut()
+void sp_selection_cut(SPDesktop *desktop)
 {
     sp_selection_copy();
-    sp_selection_delete();
+    sp_selection_delete(desktop);
 }
 
 /**
@@ -853,7 +927,7 @@ take_style_from_item (SPItem *item)
     }
 
     // FIXME: also transform gradient/pattern fills, by forking? NO, this must be nondestructive
-    double ex = NR::expansion(from_2geom(sp_item_i2doc_affine(item)));
+    double ex = to_2geom(sp_item_i2doc_affine(item)).descrim();
     if (ex != 1.0) {
         css = sp_css_attr_scale (css, ex);
     }
@@ -868,26 +942,26 @@ void sp_selection_copy()
     cm->copy();
 }
 
-void sp_selection_paste(bool in_place)
+void sp_selection_paste(SPDesktop *desktop, bool in_place)
 {
     Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get();
     if(cm->paste(in_place))
-        sp_document_done(SP_ACTIVE_DOCUMENT, SP_VERB_EDIT_PASTE, _("Paste"));
+        sp_document_done(sp_desktop_document(desktop), SP_VERB_EDIT_PASTE, _("Paste"));
 }
 
-void sp_selection_paste_style()
+void sp_selection_paste_style(SPDesktop *desktop)
 {
     Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get();
     if(cm->pasteStyle())
-        sp_document_done(SP_ACTIVE_DOCUMENT, SP_VERB_EDIT_PASTE_STYLE, _("Paste style"));
+        sp_document_done(sp_desktop_document(desktop), SP_VERB_EDIT_PASTE_STYLE, _("Paste style"));
 }
 
 
-void sp_selection_paste_livepatheffect()
+void sp_selection_paste_livepatheffect(SPDesktop *desktop)
 {
     Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get();
     if(cm->pastePathEffect())
-        sp_document_done(SP_ACTIVE_DOCUMENT, SP_VERB_EDIT_PASTE_LIVEPATHEFFECT,
+        sp_document_done(sp_desktop_document(desktop), SP_VERB_EDIT_PASTE_LIVEPATHEFFECT,
                      _("Paste live path effect"));
 }
 
@@ -899,9 +973,8 @@ void sp_selection_remove_livepatheffect_impl(SPItem *item)
     }
 }
 
-void sp_selection_remove_livepatheffect()
+void sp_selection_remove_livepatheffect(SPDesktop *desktop)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (desktop == NULL) return;
 
     Inkscape::Selection *selection = sp_desktop_selection(desktop);
@@ -923,9 +996,8 @@ void sp_selection_remove_livepatheffect()
                      _("Remove live path effect"));
 }
 
-void sp_selection_remove_filter ()
+void sp_selection_remove_filter (SPDesktop *desktop)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (desktop == NULL) return;
 
     Inkscape::Selection *selection = sp_desktop_selection(desktop);
@@ -946,26 +1018,24 @@ void sp_selection_remove_filter ()
 }
 
 
-void sp_selection_paste_size (bool apply_x, bool apply_y)
+void sp_selection_paste_size (SPDesktop *desktop, bool apply_x, bool apply_y)
 {
     Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get();
     if(cm->pasteSize(false, apply_x, apply_y))
-        sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_EDIT_PASTE_SIZE,
+        sp_document_done(sp_desktop_document(desktop), SP_VERB_EDIT_PASTE_SIZE,
                      _("Paste size"));
 }
 
-void sp_selection_paste_size_separately (bool apply_x, bool apply_y)
+void sp_selection_paste_size_separately (SPDesktop *desktop, bool apply_x, bool apply_y)
 {
     Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get();
     if(cm->pasteSize(true, apply_x, apply_y))
-        sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_EDIT_PASTE_SIZE_SEPARATELY,
+        sp_document_done(sp_desktop_document(desktop), SP_VERB_EDIT_PASTE_SIZE_SEPARATELY,
                      _("Paste size separately"));
 }
 
-void sp_selection_to_next_layer(bool suppressDone)
+void sp_selection_to_next_layer(SPDesktop *dt, bool suppressDone)
 {
-    SPDesktop *dt = SP_ACTIVE_DESKTOP;
-
     Inkscape::Selection *selection = sp_desktop_selection(dt);
 
     // check if something is selected
@@ -1009,10 +1079,8 @@ void sp_selection_to_next_layer(bool suppressDone)
     g_slist_free ((GSList *) items);
 }
 
-void sp_selection_to_prev_layer(bool suppressDone)
+void sp_selection_to_prev_layer(SPDesktop *dt, bool suppressDone)
 {
-    SPDesktop *dt = SP_ACTIVE_DESKTOP;
-
     Inkscape::Selection *selection = sp_desktop_selection(dt);
 
     // check if something is selected
@@ -1103,7 +1171,7 @@ value of set_i2d==false is only used by seltrans when it's dragging objects live
 that case, items are already in the new position, but the repr is in the old, and this function
 then simply updates the repr from item->transform.
  */
-void sp_selection_apply_affine(Inkscape::Selection *selection, NR::Matrix const &affine, bool set_i2d)
+void sp_selection_apply_affine(Inkscape::Selection *selection, Geom::Matrix const &affine, bool set_i2d)
 {
     if (selection->isEmpty())
         return;
@@ -1111,7 +1179,7 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, NR::Matrix const
     for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
         SPItem *item = SP_ITEM(l->data);
 
-        NR::Point old_center(0,0);
+        Geom::Point old_center(0,0);
         if (set_i2d && item->isCenterSet())
             old_center = item->getCenter();
 
@@ -1144,7 +1212,8 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, NR::Matrix const
         }
 
         // "clones are unmoved when original is moved" preference
-        int compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
+        Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+        int compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
         bool prefs_unmoved = (compensation == SP_CLONE_COMPENSATION_UNMOVED);
         bool prefs_parallel = (compensation == SP_CLONE_COMPENSATION_PARALLEL);
 
@@ -1178,26 +1247,26 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, NR::Matrix const
             sp_object_read_attr (SP_OBJECT (item), "transform");
 
             // calculate the matrix we need to apply to the clone to cancel its induced transform from its original
-            NR::Matrix parent_transform = from_2geom(sp_item_i2root_affine(SP_ITEM(SP_OBJECT_PARENT (item))));
-            NR::Matrix t = parent_transform * from_2geom(matrix_to_desktop (matrix_from_desktop (to_2geom(affine), item), item)) * parent_transform.inverse();
-            NR::Matrix t_inv =parent_transform * from_2geom(matrix_to_desktop (matrix_from_desktop (to_2geom(affine.inverse()), item), item)) * parent_transform.inverse();
-            NR::Matrix result = t_inv * item->transform * t;
+            Geom::Matrix parent2dt = sp_item_i2d_affine(SP_ITEM(SP_OBJECT_PARENT (item)));
+            Geom::Matrix t = parent2dt * affine * parent2dt.inverse();
+            Geom::Matrix t_inv = t.inverse();
+            Geom::Matrix result = t_inv * item->transform * t;
 
-            if ((prefs_parallel || prefs_unmoved) && affine.is_translation()) {
+            if ((prefs_parallel || prefs_unmoved) && affine.isTranslation()) {
                 // we need to cancel out the move compensation, too
 
                 // find out the clone move, same as in sp_use_move_compensate
-                NR::Matrix parent = sp_use_get_parent_transform (SP_USE(item));
-                NR::Matrix clone_move = parent.inverse() * t * parent;
+                Geom::Matrix parent = sp_use_get_parent_transform (SP_USE(item));
+                Geom::Matrix clone_move = parent.inverse() * t * parent;
 
                 if (prefs_parallel) {
-                    NR::Matrix move = result * clone_move * t_inv;
+                    Geom::Matrix move = result * clone_move * t_inv;
                     sp_item_write_transform(item, SP_OBJECT_REPR(item), move, &move);
 
                 } else if (prefs_unmoved) {
                     //if (SP_IS_USE(sp_use_get_original(SP_USE(item))))
-                    //    clone_move = NR::identity();
-                    NR::Matrix move = result * clone_move;
+                    //    clone_move = Geom::identity();
+                    Geom::Matrix move = result * clone_move;
                     sp_item_write_transform(item, SP_OBJECT_REPR(item), move, &t);
                 }
 
@@ -1208,23 +1277,22 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, NR::Matrix const
 
         } else {
             if (set_i2d) {
-                sp_item_set_i2d_affine(item, sp_item_i2d_affine(item) * to_2geom(affine));
+                sp_item_set_i2d_affine(item, sp_item_i2d_affine(item) * (Geom::Matrix)affine);
             }
             sp_item_write_transform(item, SP_OBJECT_REPR(item), item->transform, NULL);
         }
 
         // if we're moving the actual object, not just updating the repr, we can transform the
         // center by the same matrix (only necessary for non-translations)
-        if (set_i2d && item->isCenterSet() && !affine.is_translation()) {
+        if (set_i2d && item->isCenterSet() && !affine.isTranslation()) {
             item->setCenter(old_center * affine);
             SP_OBJECT(item)->updateRepr();
         }
     }
 }
 
-void sp_selection_remove_transform()
+void sp_selection_remove_transform(SPDesktop *desktop)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (desktop == NULL)
         return;
 
@@ -1248,129 +1316,99 @@ sp_selection_scale_absolute(Inkscape::Selection *selection,
     if (selection->isEmpty())
         return;
 
-    NR::Maybe<NR::Rect> const bbox(selection->bounds());
-    if ( !bbox || bbox->isEmpty() ) {
+    Geom::OptRect const bbox(selection->bounds());
+    if ( !bbox ) {
         return;
     }
 
-    NR::translate const p2o(-bbox->min());
+    Geom::Translate const p2o(-bbox->min());
 
-    NR::scale const newSize(x1 - x0,
+    Geom::Scale const newSize(x1 - x0,
                             y1 - y0);
-    NR::scale const scale( newSize / NR::scale(bbox->dimensions()) );
-    NR::translate const o2n(x0, y0);
-    NR::Matrix const final( p2o * scale * o2n );
+    Geom::Scale const scale( newSize * Geom::Scale(bbox->dimensions()).inverse() );
+    Geom::Translate const o2n(x0, y0);
+    Geom::Matrix const final( p2o * scale * o2n );
 
     sp_selection_apply_affine(selection, final);
 }
 
 
-void sp_selection_scale_relative(Inkscape::Selection *selection, NR::Point const &align, NR::scale const &scale)
+void sp_selection_scale_relative(Inkscape::Selection *selection, Geom::Point const &align, Geom::Scale const &scale)
 {
     if (selection->isEmpty())
         return;
 
-    NR::Maybe<NR::Rect> const bbox(selection->bounds());
+    Geom::OptRect const bbox(selection->bounds());
 
-    if ( !bbox || bbox->isEmpty() ) {
+    if ( !bbox ) {
         return;
     }
 
     // FIXME: ARBITRARY LIMIT: don't try to scale above 1 Mpx, it won't display properly and will crash sooner or later anyway
-    if ( bbox->extent(NR::X) * scale[NR::X] > 1e6  ||
-         bbox->extent(NR::Y) * scale[NR::Y] > 1e6 )
+    if ( bbox->dimensions()[Geom::X] * scale[Geom::X] > 1e6  ||
+         bbox->dimensions()[Geom::Y] * scale[Geom::Y] > 1e6 )
     {
         return;
     }
 
-    NR::translate const n2d(-align);
-    NR::translate const d2n(align);
-    NR::Matrix const final( n2d * scale * d2n );
+    Geom::Translate const n2d(-align);
+    Geom::Translate const d2n(align);
+    Geom::Matrix const final( n2d * scale * d2n );
     sp_selection_apply_affine(selection, final);
 }
 
 void
-sp_selection_rotate_relative(Inkscape::Selection *selection, NR::Point const &center, gdouble const angle_degrees)
+sp_selection_rotate_relative(Inkscape::Selection *selection, Geom::Point const &center, gdouble const angle_degrees)
 {
-    NR::translate const d2n(center);
-    NR::translate const n2d(-center);
-    NR::rotate const rotate(rotate_degrees(angle_degrees));
-    NR::Matrix const final( NR::Matrix(n2d) * rotate * d2n );
+    Geom::Translate const d2n(center);
+    Geom::Translate const n2d(-center);
+    Geom::Rotate const rotate(Geom::Rotate::from_degrees(angle_degrees));
+    Geom::Matrix const final( Geom::Matrix(n2d) * rotate * d2n );
     sp_selection_apply_affine(selection, final);
 }
 
 void
-sp_selection_skew_relative(Inkscape::Selection *selection, NR::Point const &align, double dx, double dy)
-{
-    NR::translate const d2n(align);
-    NR::translate const n2d(-align);
-    NR::Matrix const skew(1, dy,
-                          dx, 1,
-                          0, 0);
-    NR::Matrix const final( n2d * skew * d2n );
+sp_selection_skew_relative(Inkscape::Selection *selection, Geom::Point const &align, double dx, double dy)
+{
+    Geom::Translate const d2n(align);
+    Geom::Translate const n2d(-align);
+    Geom::Matrix const skew(1, dy,
+                            dx, 1,
+                            0, 0);
+    Geom::Matrix const final( n2d * skew * d2n );
     sp_selection_apply_affine(selection, final);
 }
 
-void sp_selection_move_relative(Inkscape::Selection *selection, NR::Point const &move)
+void sp_selection_move_relative(Inkscape::Selection *selection, Geom::Point const &move)
 {
-    sp_selection_apply_affine(selection, NR::Matrix(NR::translate(move)));
+    sp_selection_apply_affine(selection, Geom::Matrix(Geom::Translate(move)));
 }
 
 void sp_selection_move_relative(Inkscape::Selection *selection, double dx, double dy)
 {
-    sp_selection_apply_affine(selection, NR::Matrix(NR::translate(dx, dy)));
+    sp_selection_apply_affine(selection, Geom::Matrix(Geom::Translate(dx, dy)));
 }
 
-
 /**
- * \brief sp_selection_rotate_90
- *
- * This function rotates selected objects 90 degrees clockwise.
- *
+ * @brief Rotates selected objects 90 degrees, either clock-wise or counter-clockwise, depending on the value of ccw
  */
-
-void sp_selection_rotate_90_cw()
+void sp_selection_rotate_90(SPDesktop *desktop, bool ccw)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
-
     Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     if (selection->isEmpty())
         return;
 
     GSList const *l = selection->itemList();
-    NR::rotate const rot_neg_90(NR::Point(0, -1));
+    Geom::Rotate const rot_90(Geom::Point(0, ccw ? 1 : -1)); // pos. or neg. rotation, depending on the value of ccw
     for (GSList const *l2 = l ; l2 != NULL ; l2 = l2->next) {
         SPItem *item = SP_ITEM(l2->data);
-        sp_item_rotate_rel(item, rot_neg_90);
+        sp_item_rotate_rel(item, rot_90);
     }
 
-    sp_document_done(sp_desktop_document(desktop), SP_VERB_OBJECT_ROTATE_90_CCW,
-                     _("Rotate 90&#176; CW"));
-}
-
-
-/**
- * @brief Rotates selected objects 90 degrees counter-clockwise.
- */
-void sp_selection_rotate_90_ccw()
-{
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
-
-    Inkscape::Selection *selection = sp_desktop_selection(desktop);
-
-    if (selection->isEmpty())
-        return;
-
-    GSList const *l = selection->itemList();
-    NR::rotate const rot_neg_90(NR::Point(0, 1));
-    for (GSList const *l2 = l ; l2 != NULL ; l2 = l2->next) {
-        SPItem *item = SP_ITEM(l2->data);
-        sp_item_rotate_rel(item, rot_neg_90);
-    }
-
-    sp_document_done(sp_desktop_document(desktop), SP_VERB_OBJECT_ROTATE_90_CW,
-                     _("Rotate 90&#176; CCW"));
+    sp_document_done(sp_desktop_document(desktop),
+                     ccw ? SP_VERB_OBJECT_ROTATE_90_CCW : SP_VERB_OBJECT_ROTATE_90_CW,
+                     ccw ? _("Rotate 90&#176; CCW") : _("Rotate 90&#176; CW"));
 }
 
 void
@@ -1379,7 +1417,7 @@ sp_selection_rotate(Inkscape::Selection *selection, gdouble const angle_degrees)
     if (selection->isEmpty())
         return;
 
-    NR::Maybe<NR::Point> center = selection->center();
+    boost::optional<Geom::Point> center = selection->center();
     if (!center) {
         return;
     }
@@ -1394,6 +1432,21 @@ sp_selection_rotate(Inkscape::Selection *selection, gdouble const angle_degrees)
                            _("Rotate"));
 }
 
+// helper function:
+static
+Geom::Point
+cornerFarthestFrom(Geom::Rect const &r, Geom::Point const &p){
+    Geom::Point m = r.midpoint();
+    unsigned i = 0;
+    if (p[X] < m[X]) {
+        i = 1;
+    }
+    if (p[Y] < m[Y]) {
+        i = 3 - i;
+    }
+    return r.corner(i);
+}
+
 /**
 \param  angle   the angle in "angular pixels", i.e. how many visible pixels must move the outermost point of the rotated object
 */
@@ -1403,8 +1456,8 @@ sp_selection_rotate_screen(Inkscape::Selection *selection, gdouble angle)
     if (selection->isEmpty())
         return;
 
-    NR::Maybe<NR::Rect> const bbox(selection->bounds());
-    NR::Maybe<NR::Point> center = selection->center();
+    Geom::OptRect const bbox(selection->bounds());
+    boost::optional<Geom::Point> center = selection->center();
 
     if ( !bbox || !center ) {
         return;
@@ -1412,7 +1465,7 @@ sp_selection_rotate_screen(Inkscape::Selection *selection, gdouble angle)
 
     gdouble const zoom = selection->desktop()->current_zoom();
     gdouble const zmove = angle / zoom;
-    gdouble const r = NR::L2(bbox->cornerFarthestFrom(*center) - *center);
+    gdouble const r = Geom::L2(cornerFarthestFrom(*bbox, *center) - *center);
 
     gdouble const zangle = 180 * atan2(zmove, r) / M_PI;
 
@@ -1432,12 +1485,12 @@ sp_selection_scale(Inkscape::Selection *selection, gdouble grow)
     if (selection->isEmpty())
         return;
 
-    NR::Maybe<NR::Rect> const bbox(selection->bounds());
+    Geom::OptRect const bbox(selection->bounds());
     if (!bbox) {
         return;
     }
 
-    NR::Point const center(bbox->midpoint());
+    Geom::Point const center(bbox->midpoint());
 
     // you can't scale "do nizhe pola" (below zero)
     double const max_len = bbox->maxExtent();
@@ -1446,7 +1499,7 @@ sp_selection_scale(Inkscape::Selection *selection, gdouble grow)
     }
 
     double const times = 1.0 + grow / max_len;
-    sp_selection_scale_relative(selection, center, NR::scale(times, times));
+    sp_selection_scale_relative(selection, center, Geom::Scale(times, times));
 
     sp_document_maybe_done(sp_desktop_document(selection->desktop()),
                            ( (grow > 0)
@@ -1469,22 +1522,21 @@ sp_selection_scale_times(Inkscape::Selection *selection, gdouble times)
     if (selection->isEmpty())
         return;
 
-    NR::Maybe<NR::Rect> sel_bbox = selection->bounds();
+    Geom::OptRect sel_bbox = selection->bounds();
 
     if (!sel_bbox) {
         return;
     }
 
-    NR::Point const center(sel_bbox->midpoint());
-    sp_selection_scale_relative(selection, center, NR::scale(times, times));
+    Geom::Point const center(sel_bbox->midpoint());
+    sp_selection_scale_relative(selection, center, Geom::Scale(times, times));
     sp_document_done(sp_desktop_document(selection->desktop()), SP_VERB_CONTEXT_SELECT,
                      _("Scale by whole factor"));
 }
 
 void
-sp_selection_move(gdouble dx, gdouble dy)
+sp_selection_move(SPDesktop *desktop, gdouble dx, gdouble dy)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     Inkscape::Selection *selection = sp_desktop_selection(desktop);
     if (selection->isEmpty()) {
         return;
@@ -1505,10 +1557,8 @@ sp_selection_move(gdouble dx, gdouble dy)
 }
 
 void
-sp_selection_move_screen(gdouble dx, gdouble dy)
+sp_selection_move_screen(SPDesktop *desktop, gdouble dx, gdouble dy)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
-
     Inkscape::Selection *selection = sp_desktop_selection(desktop);
     if (selection->isEmpty()) {
         return;
@@ -1585,15 +1635,15 @@ private:
 }
 
 void
-sp_selection_item_next(void)
+sp_selection_item_next(SPDesktop *desktop)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     g_return_if_fail(desktop != NULL);
     Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
-    PrefsSelectionContext inlayer = (PrefsSelectionContext)prefs_get_int_attribute ("options.kbselection", "inlayer", PREFS_SELECTION_LAYER);
-    bool onlyvisible = prefs_get_int_attribute ("options.kbselection", "onlyvisible", 1);
-    bool onlysensitive = prefs_get_int_attribute ("options.kbselection", "onlysensitive", 1);
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+    PrefsSelectionContext inlayer = (PrefsSelectionContext)prefs->getInt("/options/kbselection/inlayer", PREFS_SELECTION_LAYER);
+    bool onlyvisible = prefs->getBool("/options/kbselection/onlyvisible", true);
+    bool onlysensitive = prefs->getBool("/options/kbselection/onlysensitive", true);
 
     SPObject *root;
     if (PREFS_SELECTION_ALL != inlayer) {
@@ -1613,17 +1663,17 @@ sp_selection_item_next(void)
 }
 
 void
-sp_selection_item_prev(void)
+sp_selection_item_prev(SPDesktop *desktop)
 {
-    SPDocument *document = SP_ACTIVE_DOCUMENT;
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+    SPDocument *document = sp_desktop_document(desktop);
     g_return_if_fail(document != NULL);
     g_return_if_fail(desktop != NULL);
     Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
-    PrefsSelectionContext inlayer = (PrefsSelectionContext)prefs_get_int_attribute ("options.kbselection", "inlayer", PREFS_SELECTION_LAYER);
-    bool onlyvisible = prefs_get_int_attribute ("options.kbselection", "onlyvisible", 1);
-    bool onlysensitive = prefs_get_int_attribute ("options.kbselection", "onlysensitive", 1);
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+    PrefsSelectionContext inlayer = (PrefsSelectionContext) prefs->getInt("/options/kbselection/inlayer", PREFS_SELECTION_LAYER);
+    bool onlyvisible = prefs->getBool("/options/kbselection/onlyvisible", true);
+    bool onlysensitive = prefs->getBool("/options/kbselection/onlysensitive", true);
 
     SPObject *root;
     if (PREFS_SELECTION_ALL != inlayer) {
@@ -1679,17 +1729,20 @@ void sp_selection_edit_clip_or_mask(SPDesktop * dt, bool clip)
                     if ( SP_IS_ITEM(child) ) {
                         // If not already in nodecontext, goto it!
                         if (!tools_isactive(dt, TOOLS_NODES)) {
-                            tools_switch_current(TOOLS_NODES);
+                            tools_switch(dt, TOOLS_NODES);
                         }
 
-                        ShapeEditor * shape_editor = SP_NODE_CONTEXT( dt->event_context )->shape_editor;
-                        shape_editor->set_item(SP_ITEM(child));
+                        ShapeEditor * shape_editor = dt->event_context->shape_editor;
+                        // TODO: should we set the item for nodepath or knotholder or both? seems to work with both.
+                        shape_editor->set_item(SP_ITEM(child), SH_NODEPATH);
+                        shape_editor->set_item(SP_ITEM(child), SH_KNOTHOLDER);
                         Inkscape::NodePath::Path *np = shape_editor->get_nodepath();
                         if (np) {
                             // take colors from prefs (same as used in outline mode)
+                            Inkscape::Preferences *prefs = Inkscape::Preferences::get();
                             np->helperpath_rgba = clip ? 
-                                prefs_get_int_attribute("options.wireframecolors", "clips", 0x00ff00ff) : 
-                                prefs_get_int_attribute("options.wireframecolors", "masks", 0x0000ffff);
+                                prefs->getInt("/options/wireframecolors/clips", 0x00ff00ff) : 
+                                prefs->getInt("/options/wireframecolors/masks", 0x0000ffff);
                             np->helperpath_width = 1.0;
                             sp_nodepath_show_helperpath(np, true);
                         }
@@ -1792,15 +1845,15 @@ SPItem *next_item(SPDesktop *desktop, GSList *path, SPObject *root,
  */
 void scroll_to_show_item(SPDesktop *desktop, SPItem *item)
 {
-    NR::Rect dbox = desktop->get_display_area();
-    NR::Maybe<NR::Rect> sbox = sp_item_bbox_desktop(item);
+    Geom::Rect dbox = desktop->get_display_area();
+    Geom::OptRect sbox = sp_item_bbox_desktop(item);
 
     if ( sbox && dbox.contains(*sbox) == false ) {
-        NR::Point const s_dt = sbox->midpoint();
-        NR::Point const s_w = desktop->d2w(s_dt);
-        NR::Point const d_dt = dbox.midpoint();
-        NR::Point const d_w = desktop->d2w(d_dt);
-        NR::Point const moved_w( d_w - s_w );
+        Geom::Point const s_dt = sbox->midpoint();
+        Geom::Point const s_w = desktop->d2w(s_dt);
+        Geom::Point const d_dt = dbox.midpoint();
+        Geom::Point const d_w = desktop->d2w(d_dt);
+        Geom::Point const moved_w( d_w - s_w );
         gint const dx = (gint) moved_w[X];
         gint const dy = (gint) moved_w[Y];
         desktop->scroll_world(dx, dy);
@@ -1809,9 +1862,8 @@ void scroll_to_show_item(SPDesktop *desktop, SPItem *item)
 
 
 void
-sp_selection_clone()
+sp_selection_clone(SPDesktop *desktop)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (desktop == NULL)
         return;
 
@@ -1865,9 +1917,8 @@ sp_selection_clone()
 }
 
 void
-sp_selection_relink()
+sp_selection_relink(SPDesktop *desktop)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (!desktop)
         return;
 
@@ -1914,9 +1965,8 @@ sp_selection_relink()
 
 
 void
-sp_selection_unlink()
+sp_selection_unlink(SPDesktop *desktop)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (!desktop)
         return;
 
@@ -1980,9 +2030,8 @@ sp_selection_unlink()
 }
 
 void
-sp_select_clone_original()
+sp_select_clone_original(SPDesktop *desktop)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (desktop == NULL)
         return;
 
@@ -2025,6 +2074,25 @@ sp_select_clone_original()
     }
 
     if (original) {
+        Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+        bool highlight = prefs->getBool("/options/highlightoriginal/value");
+        if (highlight) {
+            Geom::OptRect a = item->getBounds(sp_item_i2d_affine(item));
+            Geom::OptRect b = original->getBounds(sp_item_i2d_affine(original));
+            if ( a && b ) {
+                // draw a flashing line between the objects
+                SPCurve *curve = new SPCurve();
+                curve->moveto(a->midpoint());
+                curve->lineto(b->midpoint());
+
+                SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), curve);
+                sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), 0x0000ddff, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT, 5, 3);
+                sp_canvas_item_show(canvasitem);
+                curve->unref();
+                desktop->add_temporary_canvasitem (canvasitem, 1000);
+            }
+        }
+
         selection->clear();
         selection->set(original);
         if (SP_CYCLING == SP_CYCLE_FOCUS) {
@@ -2034,9 +2102,8 @@ sp_select_clone_original()
 }
 
 
-void sp_selection_to_marker(bool apply)
+void sp_selection_to_marker(SPDesktop *desktop, bool apply)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (desktop == NULL)
         return;
 
@@ -2052,16 +2119,16 @@ void sp_selection_to_marker(bool apply)
     }
 
     sp_document_ensure_up_to_date(doc);
-    NR::Maybe<NR::Rect> r = selection->bounds();
-    NR::Maybe<NR::Point> c = selection->center();
-    if ( !r || !c || r->isEmpty() ) {
+    Geom::OptRect r = selection->bounds();
+    boost::optional<Geom::Point> c = selection->center();
+    if ( !r || !c ) {
         return;
     }
 
     // calculate the transform to be applied to objects to move them to 0,0
-    NR::Point move_p = NR::Point(0, sp_document_height(doc)) - *c;
-    move_p[NR::Y] = -move_p[NR::Y];
-    NR::Matrix move = NR::Matrix (NR::translate (move_p));
+    Geom::Point move_p = Geom::Point(0, sp_document_height(doc)) - *c;
+    move_p[Geom::Y] = -move_p[Geom::Y];
+    Geom::Matrix move = Geom::Matrix (Geom::Translate (move_p));
 
     GSList *items = g_slist_copy((GSList *) selection->itemList());
 
@@ -2070,7 +2137,7 @@ void sp_selection_to_marker(bool apply)
     // bottommost object, after sorting
     SPObject *parent = SP_OBJECT_PARENT (items->data);
 
-    NR::Matrix parent_transform = from_2geom(sp_item_i2root_affine(SP_ITEM(parent)));
+    Geom::Matrix parent_transform (sp_item_i2doc_affine(SP_ITEM(parent)));
 
     // remember the position of the first item
     gint pos = SP_OBJECT_REPR (items->data)->position();
@@ -2083,7 +2150,7 @@ void sp_selection_to_marker(bool apply)
         repr_copies = g_slist_prepend (repr_copies, dup);
     }
 
-    NR::Rect bounds(desktop->dt2doc(r->min()), desktop->dt2doc(r->max()));
+    Geom::Rect bounds(desktop->dt2doc(r->min()), desktop->dt2doc(r->max()));
 
     if (apply) {
         // delete objects so that their clones don't get alerted; this object will be restored shortly
@@ -2096,18 +2163,20 @@ void sp_selection_to_marker(bool apply)
     // Hack: Temporarily set clone compensation to unmoved, so that we can move clone-originals
     // without disturbing clones.
     // See ActorAlign::on_button_click() in src/ui/dialog/align-and-distribute.cpp
-    int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
-    prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+    int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
+    prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
 
     gchar const *mark_id = generate_marker(repr_copies, bounds, doc,
-                                           ( NR::Matrix(NR::translate(desktop->dt2doc(NR::Point(r->min()[NR::X],
-                                                                                                r->max()[NR::Y]))))
+                                           ( Geom::Matrix(Geom::Translate(desktop->dt2doc(
+                                                                              Geom::Point(r->min()[Geom::X],
+                                                                                          r->max()[Geom::Y]))))
                                              * parent_transform.inverse() ),
                                            parent_transform * move);
     (void)mark_id;
 
     // restore compensation setting
-    prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
+    prefs->setInt("/options/clonecompensation/value", saved_compensation);
 
 
     g_slist_free (items);
@@ -2130,9 +2199,8 @@ static void sp_selection_to_guides_recursive(SPItem *item, bool deleteitem, bool
     }
 }
 
-void sp_selection_to_guides()
+void sp_selection_to_guides(SPDesktop *desktop)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (desktop == NULL)
         return;
 
@@ -2145,9 +2213,10 @@ void sp_selection_to_guides()
         desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to convert to guides."));
         return;
     }
-    bool deleteitem = (prefs_get_int_attribute("tools", "cvg_keep_objects", 0) == 0);
-    bool wholegroups = (prefs_get_int_attribute("tools", "cvg_convert_whole_groups", 0) != 0);
+    
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+    bool deleteitem = !prefs->getBool("/tools/cvg_keep_objects", 0);
+    bool wholegroups = prefs->getBool("/tools/cvg_convert_whole_groups", 0);
 
     for (GSList const *i = items; i != NULL; i = i->next) {
         sp_selection_to_guides_recursive(SP_ITEM(i->data), deleteitem, wholegroups);
@@ -2157,9 +2226,8 @@ void sp_selection_to_guides()
 }
 
 void
-sp_selection_tile(bool apply)
+sp_selection_tile(SPDesktop *desktop, bool apply)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (desktop == NULL)
         return;
 
@@ -2175,15 +2243,15 @@ sp_selection_tile(bool apply)
     }
 
     sp_document_ensure_up_to_date(doc);
-    NR::Maybe<NR::Rect> r = selection->bounds();
-    if ( !r || r->isEmpty() ) {
+    Geom::OptRect r = selection->bounds();
+    if ( !r ) {
         return;
     }
 
     // calculate the transform to be applied to objects to move them to 0,0
-    NR::Point move_p = NR::Point(0, sp_document_height(doc)) - (r->min() + NR::Point (0, r->extent(NR::Y)));
-    move_p[NR::Y] = -move_p[NR::Y];
-    NR::Matrix move = NR::Matrix (NR::translate (move_p));
+    Geom::Point move_p = Geom::Point(0, sp_document_height(doc)) - (r->min() + Geom::Point (0, r->dimensions()[Geom::Y]));
+    move_p[Geom::Y] = -move_p[Geom::Y];
+    Geom::Matrix move = Geom::Matrix (Geom::Translate (move_p));
 
     GSList *items = g_slist_copy((GSList *) selection->itemList());
 
@@ -2192,7 +2260,7 @@ sp_selection_tile(bool apply)
     // bottommost object, after sorting
     SPObject *parent = SP_OBJECT_PARENT (items->data);
 
-    NR::Matrix parent_transform = from_2geom(sp_item_i2root_affine(SP_ITEM(parent)));
+    Geom::Matrix parent_transform (sp_item_i2doc_affine(SP_ITEM(parent)));
 
     // remember the position of the first item
     gint pos = SP_OBJECT_REPR (items->data)->position();
@@ -2206,7 +2274,7 @@ sp_selection_tile(bool apply)
     // restore the z-order after prepends
     repr_copies = g_slist_reverse (repr_copies);
 
-    NR::Rect bounds(desktop->dt2doc(r->min()), desktop->dt2doc(r->max()));
+    Geom::Rect bounds(desktop->dt2doc(r->min()), desktop->dt2doc(r->max()));
 
     if (apply) {
         // delete objects so that their clones don't get alerted; this object will be restored shortly
@@ -2219,29 +2287,30 @@ sp_selection_tile(bool apply)
     // Hack: Temporarily set clone compensation to unmoved, so that we can move clone-originals
     // without disturbing clones.
     // See ActorAlign::on_button_click() in src/ui/dialog/align-and-distribute.cpp
-    int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
-    prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+    int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
+    prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
 
     gchar const *pat_id = pattern_tile(repr_copies, bounds, doc,
-                                       ( NR::Matrix(NR::translate(desktop->dt2doc(NR::Point(r->min()[NR::X],
-                                                                                            r->max()[NR::Y]))))
-                                         * parent_transform.inverse() ),
+                                       ( Geom::Matrix(Geom::Translate(desktop->dt2doc(Geom::Point(r->min()[Geom::X],
+                                                                                            r->max()[Geom::Y]))))
+                                         * to_2geom(parent_transform.inverse()) ),
                                        parent_transform * move);
 
     // restore compensation setting
-    prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
+    prefs->setInt("/options/clonecompensation/value", saved_compensation);
 
     if (apply) {
         Inkscape::XML::Node *rect = xml_doc->createElement("svg:rect");
         rect->setAttribute("style", g_strdup_printf("stroke:none;fill:url(#%s)", pat_id));
 
-        NR::Point min = bounds.min() * parent_transform.inverse();
-        NR::Point max = bounds.max() * parent_transform.inverse();
+        Geom::Point min = bounds.min() * to_2geom(parent_transform.inverse());
+        Geom::Point max = bounds.max() * to_2geom(parent_transform.inverse());
 
-        sp_repr_set_svg_double(rect, "width", max[NR::X] - min[NR::X]);
-        sp_repr_set_svg_double(rect, "height", max[NR::Y] - min[NR::Y]);
-        sp_repr_set_svg_double(rect, "x", min[NR::X]);
-        sp_repr_set_svg_double(rect, "y", min[NR::Y]);
+        sp_repr_set_svg_double(rect, "width", max[Geom::X] - min[Geom::X]);
+        sp_repr_set_svg_double(rect, "height", max[Geom::Y] - min[Geom::Y]);
+        sp_repr_set_svg_double(rect, "x", min[Geom::X]);
+        sp_repr_set_svg_double(rect, "y", min[Geom::Y]);
 
         // restore parent and position
         SP_OBJECT_REPR (parent)->appendChild(rect);
@@ -2261,9 +2330,8 @@ sp_selection_tile(bool apply)
 }
 
 void
-sp_selection_untile()
+sp_selection_untile(SPDesktop *desktop)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (desktop == NULL)
         return;
 
@@ -2302,7 +2370,7 @@ sp_selection_untile()
 
         SPPattern *pattern = pattern_getroot (SP_PATTERN (server));
 
-        NR::Matrix pat_transform = pattern_patternTransform (SP_PATTERN (server));
+        Geom::Matrix pat_transform = to_2geom(pattern_patternTransform (SP_PATTERN (server)));
         pat_transform *= item->transform;
 
         for (SPObject *child = sp_object_first_child(SP_OBJECT(pattern)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
@@ -2315,7 +2383,7 @@ sp_selection_untile()
             // this is needed to make sure the new item has curve (simply requestDisplayUpdate does not work)
             sp_document_ensure_up_to_date (doc);
 
-            NR::Matrix transform( i->transform * pat_transform );
+            Geom::Matrix transform( i->transform * pat_transform );
             sp_item_write_transform(i, SP_OBJECT_REPR(i), transform);
 
             new_select = g_slist_prepend(new_select, i);
@@ -2403,9 +2471,8 @@ sp_document_get_export_hints (SPDocument *doc, char const **filename, float *xdp
 }
 
 void
-sp_selection_create_bitmap_copy ()
+sp_selection_create_bitmap_copy (SPDesktop *desktop)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (desktop == NULL)
         return;
 
@@ -2450,8 +2517,16 @@ sp_selection_create_bitmap_copy ()
     // Imagemagick is known not to handle spaces in filenames, so we replace anything but letters,
     // digits, and a few other chars, with "_"
     filename = g_strcanon (filename, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.=+~$#@^&!?", '_');
-    // Build the complete path by adding document->base if set
-    gchar *filepath = g_build_filename (document->base?document->base:"", filename, NULL);
+
+    // Build the complete path by adding document base dir, if set, otherwise home dir
+    gchar * directory = NULL;
+    if (SP_DOCUMENT_URI(document)) {
+        directory = g_dirname(SP_DOCUMENT_URI(document));
+    }
+    if (directory == NULL) {
+        directory = homedir_path(NULL);
+    }
+    gchar *filepath = g_build_filename (directory, filename, NULL);
 
     //g_print ("%s\n", filepath);
 
@@ -2461,9 +2536,10 @@ sp_selection_create_bitmap_copy ()
     Inkscape::XML::Node *parent = SP_OBJECT_REPR(parent_object);
 
     // Calculate resolution
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
     double res;
-    int const prefs_res = prefs_get_int_attribute ("options.createbitmap", "resolution", 0);
-    int const prefs_min = prefs_get_int_attribute ("options.createbitmap", "minsize", 0);
+    int const prefs_res = prefs->getInt("/options/createbitmap/resolution", 0);
+    int const prefs_min = prefs->getInt("/options/createbitmap/minsize", 0);
     if (0 < prefs_res) {
         // If it's given explicitly in prefs, take it
         res = prefs_res;
@@ -2495,31 +2571,31 @@ sp_selection_create_bitmap_copy ()
 
     // Find out if we have to run an external filter
     gchar const *run = NULL;
-    gchar const *filter = prefs_get_string_attribute ("options.createbitmap", "filter");
-    if (filter) {
+    Glib::ustring filter = prefs->getString("/options/createbitmap/filter");
+    if (!filter.empty()) {
         // filter command is given;
         // see if we have a parameter to pass to it
-        gchar const *param1 = prefs_get_string_attribute ("options.createbitmap", "filter_param1");
-        if (param1) {
-            if (param1[strlen(param1) - 1] == '%') {
+        Glib::ustring param1 = prefs->getString("/options/createbitmap/filter_param1");
+        if (!param1.empty()) {
+            if (param1[param1.length() - 1] == '%') {
                 // if the param string ends with %, interpret it as a percentage of the image's max dimension
                 gchar p1[256];
-                g_ascii_dtostr (p1, 256, ceil (g_ascii_strtod (param1, NULL) * MAX(width, height) / 100));
+                g_ascii_dtostr (p1, 256, ceil (g_ascii_strtod (param1.data(), NULL) * MAX(width, height) / 100));
                 // the first param is always the image filename, the second is param1
-                run = g_strdup_printf ("%s \"%s\" %s", filter, filepath, p1);
+                run = g_strdup_printf ("%s \"%s\" %s", filter.data(), filepath, p1);
             } else {
                 // otherwise pass the param1 unchanged
-                run = g_strdup_printf ("%s \"%s\" %s", filter, filepath, param1);
+                run = g_strdup_printf ("%s \"%s\" %s", filter.data(), filepath, param1.data());
             }
         } else {
             // run without extra parameter
-            run = g_strdup_printf ("%s \"%s\"", filter, filepath);
+            run = g_strdup_printf ("%s \"%s\"", filter.data(), filepath);
         }
     }
 
     // Calculate the matrix that will be applied to the image so that it exactly overlaps the source objects
-    NR::Matrix eek = from_2geom(sp_item_i2d_affine (SP_ITEM(parent_object)));
-    NR::Matrix t;
+    Geom::Matrix eek (sp_item_i2d_affine (SP_ITEM(parent_object)));
+    Geom::Matrix t;
 
     double shift_x = bbox.x0;
     double shift_y = bbox.y1;
@@ -2527,7 +2603,7 @@ sp_selection_create_bitmap_copy ()
         shift_x = round (shift_x);
         shift_y = -round (-shift_y); // this gets correct rounding despite coordinate inversion, remove the negations when the inversion is gone
     }
-    t = NR::scale(1, -1) * NR::translate (shift_x, shift_y) * eek.inverse();
+    t = Geom::Scale(1, -1) * Geom::Translate (shift_x, shift_y) * eek.inverse();
 
     // Do the export
     sp_export_png_file(document, filepath,
@@ -2602,9 +2678,8 @@ sp_selection_create_bitmap_copy ()
  *
  */
 void
-sp_selection_set_mask(bool apply_clip_path, bool apply_to_layer)
+sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_layer)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (desktop == NULL)
         return;
 
@@ -2641,8 +2716,9 @@ sp_selection_set_mask(bool apply_clip_path, bool apply_to_layer)
     GSList *mask_items = NULL;
     GSList *apply_to_items = NULL;
     GSList *items_to_delete = NULL;
-    bool topmost = prefs_get_int_attribute ("options.maskobject", "topmost", 1);
-    bool remove_original = prefs_get_int_attribute ("options.maskobject", "remove", 1);
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+    bool topmost = prefs->getBool("/options/maskobject/topmost", true);
+    bool remove_original = prefs->getBool("/options/maskobject/remove", true);
 
     if (apply_to_layer) {
         // all selected items are used for mask, which is applied to a layer
@@ -2694,7 +2770,7 @@ sp_selection_set_mask(bool apply_clip_path, bool apply_to_layer)
         SPItem *item = reinterpret_cast<SPItem *>(i->data);
         // inverted object transform should be applied to a mask object,
         // as mask is calculated in user space (after applying transform)
-        NR::Matrix maskTransform (item->transform.inverse());
+        Geom::Matrix maskTransform (item->transform.inverse());
 
         GSList *mask_items_dup = NULL;
         for (GSList *mask_item = mask_items; NULL != mask_item; mask_item = mask_item->next) {
@@ -2730,8 +2806,7 @@ sp_selection_set_mask(bool apply_clip_path, bool apply_to_layer)
         sp_document_done (doc, SP_VERB_OBJECT_SET_MASK, _("Set mask"));
 }
 
-void sp_selection_unset_mask(bool apply_clip_path) {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+void sp_selection_unset_mask(SPDesktop *desktop, bool apply_clip_path) {
     if (desktop == NULL)
         return;
 
@@ -2745,11 +2820,14 @@ void sp_selection_unset_mask(bool apply_clip_path) {
         return;
     }
 
-    bool remove_original = prefs_get_int_attribute ("options.maskobject", "remove", 1);
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+    bool remove_original = prefs->getBool("/options/maskobject/remove", true);
     sp_document_ensure_up_to_date(doc);
 
     gchar const *attributeName = apply_clip_path ? "clip-path" : "mask";
-    std::map<SPObject*,SPItem*> referenced_objects;
+    std::map<SPObject*,SPItem*> referenced_objects; 
+    // SPObject* refers to a group containing the clipped path or mask itself, 
+    // whereas SPItem* refers to the item being clipped or masked
     for (GSList const *i = selection->itemList(); NULL != i; i = i->next) {
         if (remove_original) {
             // remember referenced mask/clippath, so orphaned masks can be moved back to document
@@ -2773,9 +2851,10 @@ void sp_selection_unset_mask(bool apply_clip_path) {
 
     // restore mask objects into a document
     for ( std::map<SPObject*,SPItem*>::iterator it = referenced_objects.begin() ; it != referenced_objects.end() ; ++it) {
-        SPObject *obj = (*it).first;
+        SPObject *obj = (*it).first; // Group containing the clipped paths or masks
         GSList *items_to_move = NULL;
         for (SPObject *child = sp_object_first_child(obj) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
+            // Collect all clipped paths and masks within a single group 
             Inkscape::XML::Node *copy = SP_OBJECT_REPR(child)->duplicate(xml_doc);
             items_to_move = g_slist_prepend (items_to_move, copy);
         }
@@ -2789,6 +2868,7 @@ void sp_selection_unset_mask(bool apply_clip_path) {
         Inkscape::XML::Node *parent = SP_OBJECT_REPR((*it).second)->parent();
         gint pos = SP_OBJECT_REPR((*it).second)->position();
 
+        // Iterate through all clipped paths / masks
         for (GSList *i = items_to_move; NULL != i; i = i->next) {
             Inkscape::XML::Node *repr = (Inkscape::XML::Node *)i->data;
 
@@ -2800,7 +2880,7 @@ void sp_selection_unset_mask(bool apply_clip_path) {
             selection->add(repr);
 
             // transform mask, so it is moved the same spot where mask was applied
-            NR::Matrix transform (mask_item->transform);
+            Geom::Matrix transform (mask_item->transform);
             transform *= (*it).second->transform;
             sp_item_write_transform(mask_item, SP_OBJECT_REPR(mask_item), transform);
         }
@@ -2830,8 +2910,8 @@ fit_canvas_to_selection(SPDesktop *desktop)
         desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to fit canvas to."));
         return false;
     }
-    NR::Maybe<NR::Rect> const bbox(desktop->selection->bounds());
-    if (bbox && !bbox->isEmpty()) {
+    Geom::OptRect const bbox(desktop->selection->bounds());
+    if (bbox) {
         doc->fitToRect(*bbox);
         return true;
     } else {
@@ -2858,8 +2938,8 @@ fit_canvas_to_drawing(SPDocument *doc)
 
     sp_document_ensure_up_to_date(doc);
     SPItem const *const root = SP_ITEM(doc->root);
-    NR::Maybe<NR::Rect> const bbox(root->getBounds(from_2geom(sp_item_i2r_affine(root))));
-    if (bbox && !bbox->isEmpty()) {
+    Geom::OptRect const bbox(root->getBounds(sp_item_i2d_affine(root)));
+    if (bbox) {
         doc->fitToRect(*bbox);
         return true;
     } else {