Code

Connector tool: make connectors avoid the convex hull of shapes.
[inkscape.git] / src / selection-chemistry.cpp
index a5f27038965c5962ed962473cebc3f9bc28ab7ba..e55bba2a5926079ef2e3a2fd99f300d7b8e791c2 100644 (file)
@@ -25,9 +25,9 @@
 #include <gtkmm/clipboard.h>
 
 #include "svg/svg.h"
-#include "inkscape.h"
 #include "desktop.h"
 #include "desktop-style.h"
+#include "dir-util.h"
 #include "selection.h"
 #include "tools-switch.h"
 #include "desktop-handles.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 "xml/rebase-hrefs.h"
 #include "style.h"
 #include "document-private.h"
 #include "sp-gradient.h"
@@ -63,7 +62,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 +82,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 +94,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 +105,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);
 
@@ -122,22 +124,22 @@ void sp_selection_copy_one (Inkscape::XML::Node *repr, NR::Matrix full_t, GSList
     *clip = g_slist_prepend(*clip, copy);
 }
 
-void sp_selection_copy_impl (GSList const *items, GSList **clip, Inkscape::XML::Document* xml_doc)
+void sp_selection_copy_impl(GSList const *items, GSList **clip, Inkscape::XML::Document* xml_doc)
 {
     // Sort items:
-    GSList *sorted_items = g_slist_copy ((GSList *) items);
+    GSList *sorted_items = g_slist_copy((GSList *) items);
     sorted_items = g_slist_sort((GSList *) sorted_items, (GCompareFunc) sp_object_compare_position);
 
     // 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);
-    g_slist_free ((GSList *) sorted_items);
+    g_slist_free((GSList *) sorted_items);
 }
 
-GSList *sp_selection_paste_impl (SPDocument *doc, SPObject *parent, GSList **clip)
+GSList *sp_selection_paste_impl(SPDocument *doc, SPObject *parent, GSList **clip)
 {
     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
 
@@ -148,10 +150,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,14 +183,13 @@ 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;
     }
 
-    if (tools_isactive (desktop, TOOLS_TEXT))
+    if (tools_isactive(desktop, TOOLS_TEXT))
         if (sp_text_delete_selection(desktop->event_context)) {
             sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT,
                              _("Delete text"));
@@ -205,8 +206,8 @@ void sp_selection_delete()
 
     GSList const *selected = g_slist_copy(const_cast<GSList *>(selection->itemList()));
     selection->clear();
-    sp_selection_delete_impl (selected);
-    g_slist_free ((GSList *) selected);
+    sp_selection_delete_impl(selected);
+    g_slist_free((GSList *) selected);
 
     /* a tool may have set up private information in it's selection context
      * that depends on desktop items.  I think the only sane way to deal with
@@ -214,20 +215,33 @@ void sp_selection_delete()
      * associated selection context.  For example: deleting an object
      * while moving it around the canvas.
      */
-    tools_switch ( desktop, tools_active ( desktop ) );
+    tools_switch( desktop, tools_active( desktop ) );
 
     sp_document_done(sp_desktop_document(desktop), SP_VERB_EDIT_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 +254,63 @@ 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));
+                if (!orig) // orphaned
+                    continue;
+                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 +321,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;
 
@@ -279,7 +333,7 @@ void sp_edit_clear_all()
     GSList *items = sp_item_group_item_list(SP_GROUP(dt->currentLayer()));
 
     while (items) {
-        SP_OBJECT (items->data)->deleteObject();
+        SP_OBJECT(items->data)->deleteObject();
         items = g_slist_remove(items, items->data);
     }
 
@@ -288,30 +342,29 @@ void sp_edit_clear_all()
 }
 
 GSList *
-get_all_items (GSList *list, SPObject *from, SPDesktop *desktop, bool onlyvisible, bool onlysensitive, GSList const *exclude)
+get_all_items(GSList *list, SPObject *from, SPDesktop *desktop, bool onlyvisible, bool onlysensitive, GSList const *exclude)
 {
     for (SPObject *child = sp_object_first_child(SP_OBJECT(from)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
         if (SP_IS_ITEM(child) &&
             !desktop->isLayer(SP_ITEM(child)) &&
             (!onlysensitive || !SP_ITEM(child)->isLocked()) &&
             (!onlyvisible || !desktop->itemIsHidden(SP_ITEM(child))) &&
-            (!exclude || !g_slist_find ((GSList *) exclude, child))
+            (!exclude || !g_slist_find((GSList *) exclude, child))
             )
         {
-            list = g_slist_prepend (list, SP_ITEM(child));
+            list = g_slist_prepend(list, SP_ITEM(child));
         }
 
         if (SP_IS_ITEM(child) && desktop->isLayer(SP_ITEM(child))) {
-            list = get_all_items (list, child, desktop, onlyvisible, onlysensitive, exclude);
+            list = get_all_items(list, child, desktop, onlyvisible, onlysensitive, exclude);
         }
     }
 
     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;
 
@@ -319,9 +372,10 @@ void sp_edit_select_all_full (bool force_all_layers, bool invert)
 
     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;
 
@@ -342,66 +396,65 @@ void sp_edit_select_all_full (bool force_all_layers, bool invert)
         GSList *all_items = sp_item_group_item_list(SP_GROUP(dt->currentLayer()));
 
         for (GSList *i = all_items; i; i = i->next) {
-            SPItem *item = SP_ITEM (i->data);
+            SPItem *item = SP_ITEM(i->data);
 
             if (item && (!onlysensitive || !item->isLocked())) {
                 if (!onlyvisible || !dt->itemIsHidden(item)) {
                     if (!dt->isLayer(item)) {
-                        if (!invert || !g_slist_find ((GSList *) exclude, item)) {
-                            items = g_slist_prepend (items, item); // leave it in the list
+                        if (!invert || !g_slist_find((GSList *) exclude, item)) {
+                            items = g_slist_prepend(items, item); // leave it in the list
                         }
                     }
                 }
             }
         }
 
-        g_slist_free (all_items);
+        g_slist_free(all_items);
             break;
         }
         case PREFS_SELECTION_LAYER_RECURSIVE: {
-            items = get_all_items (NULL, dt->currentLayer(), dt, onlyvisible, onlysensitive, exclude);
+            items = get_all_items(NULL, dt->currentLayer(), dt, onlyvisible, onlysensitive, exclude);
             break;
         }
         default: {
-        items = get_all_items (NULL, dt->currentRoot(), dt, onlyvisible, onlysensitive, exclude);
+        items = get_all_items(NULL, dt->currentRoot(), dt, onlyvisible, onlysensitive, exclude);
             break;
     }
     }
 
-    selection->setList (items);
+    selection->setList(items);
 
     if (items) {
-        g_slist_free (items);
+        g_slist_free(items);
     }
 }
 
-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;
 
-    SPDocument *doc = sp_desktop_document (desktop);
+    SPDocument *doc = sp_desktop_document(desktop);
     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
 
     Inkscape::Selection *selection = sp_desktop_selection(desktop);
@@ -436,40 +489,40 @@ void sp_selection_group()
             Inkscape::GC::release(spnew);
             topmost --; // only reduce count for those items deleted from topmost_parent
         } else { // move it to topmost_parent first
-                GSList *temp_clip = NULL;
-
-                // 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());
-                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()))));
-                //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
-                //this by remembering the transform diffs we write to each item into an array and
-                //then, if this is clone, looking up its original in that array and pre-multiplying
-                //it by the inverse of that original's transform diff.
-
-                sp_selection_copy_one (current, item_t, &temp_clip, xml_doc);
-                sp_repr_unparent(current);
-
-                // paste into topmost_parent (temporarily)
-                GSList *copied = sp_selection_paste_impl (doc, doc->getObjectByRepr(topmost_parent), &temp_clip);
-                if (temp_clip) g_slist_free (temp_clip);
-                if (copied) { // if success,
-                    // take pasted object (now in topmost_parent)
-                    Inkscape::XML::Node *in_topmost = (Inkscape::XML::Node *) copied->data;
-                    // make a copy
-                    Inkscape::XML::Node *spnew = in_topmost->duplicate(xml_doc);
-                    // remove pasted
-                    sp_repr_unparent(in_topmost);
-                    // put its copy into group
-                    group->appendChild(spnew);
-                    Inkscape::GC::release(spnew);
-                    g_slist_free (copied);
-                }
+            GSList *temp_clip = NULL;
+
+            // 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");
+            Geom::Matrix item_t(Geom::identity());
+            if (t_str)
+                sp_svg_transform_read(t_str, &item_t);
+            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
+            // this by remembering the transform diffs we write to each item into an array and
+            // then, if this is clone, looking up its original in that array and pre-multiplying
+            // it by the inverse of that original's transform diff.
+
+            sp_selection_copy_one(current, item_t, &temp_clip, xml_doc);
+            sp_repr_unparent(current);
+
+            // paste into topmost_parent (temporarily)
+            GSList *copied = sp_selection_paste_impl(doc, doc->getObjectByRepr(topmost_parent), &temp_clip);
+            if (temp_clip) g_slist_free(temp_clip);
+            if (copied) { // if success,
+                // take pasted object (now in topmost_parent)
+                Inkscape::XML::Node *in_topmost = (Inkscape::XML::Node *) copied->data;
+                // make a copy
+                Inkscape::XML::Node *spnew = in_topmost->duplicate(xml_doc);
+                // remove pasted
+                sp_repr_unparent(in_topmost);
+                // put its copy into group
+                group->appendChild(spnew);
+                Inkscape::GC::release(spnew);
+                g_slist_free(copied);
+            }
         }
         p = g_slist_remove(p, current);
     }
@@ -487,9 +540,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 +598,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 +649,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 +676,8 @@ prev_sibling(SPObject *child)
 }
 
 void
-sp_selection_raise()
+sp_selection_raise(SPDesktop *desktop)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (!desktop)
         return;
 
@@ -621,7 +702,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 +712,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)) {
@@ -649,13 +730,14 @@ sp_selection_raise()
     }
 
     sp_document_done(sp_desktop_document(desktop), SP_VERB_SELECTION_RAISE,
-                    //TRANSLATORS: Only put the word "Raise" in the translation. Means "to raise an object" in the undo history
+                     //TRANSLATORS: only translate "string" in "context|string".
+                     // For more details, see http://developer.gnome.org/doc/API/2.0/glib/glib-I18N.html#Q-:CAPS
+                     // "Raise" means "to raise an object" in the undo history
                      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 +772,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 +794,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 +809,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 +834,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,47 +895,47 @@ 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);
 }
 
 /**
  * \pre item != NULL
  */
 SPCSSAttr *
-take_style_from_item (SPItem *item)
+take_style_from_item(SPItem *item)
 {
     // write the complete cascaded style, context-free
-    SPCSSAttr *css = sp_css_attr_from_object (SP_OBJECT(item), SP_STYLE_FLAG_ALWAYS);
+    SPCSSAttr *css = sp_css_attr_from_object(SP_OBJECT(item), SP_STYLE_FLAG_ALWAYS);
     if (css == NULL)
         return NULL;
 
     if ((SP_IS_GROUP(item) && SP_OBJECT(item)->children) ||
-        (SP_IS_TEXT (item) && SP_OBJECT(item)->children && SP_OBJECT(item)->children->next == NULL)) {
+        (SP_IS_TEXT(item) && SP_OBJECT(item)->children && SP_OBJECT(item)->children->next == NULL)) {
         // if this is a text with exactly one tspan child, merge the style of that tspan as well
         // If this is a group, merge the style of its topmost (last) child with style
-        for (SPObject *last_element = item->lastChild(); last_element != NULL; last_element = SP_OBJECT_PREV (last_element)) {
-            if (SP_OBJECT_STYLE (last_element) != NULL) {
-                SPCSSAttr *temp = sp_css_attr_from_object (last_element, SP_STYLE_FLAG_IFSET);
+        for (SPObject *last_element = item->lastChild(); last_element != NULL; last_element = SP_OBJECT_PREV(last_element)) {
+            if (SP_OBJECT_STYLE(last_element) != NULL) {
+                SPCSSAttr *temp = sp_css_attr_from_object(last_element, SP_STYLE_FLAG_IFSET);
                 if (temp) {
-                    sp_repr_css_merge (css, temp);
-                    sp_repr_css_attr_unref (temp);
+                    sp_repr_css_merge(css, temp);
+                    sp_repr_css_attr_unref(temp);
                 }
                 break;
             }
         }
     }
-    if (!(SP_IS_TEXT (item) || SP_IS_TSPAN (item) || SP_IS_TREF(item) || SP_IS_STRING (item))) {
+    if (!(SP_IS_TEXT(item) || SP_IS_TSPAN(item) || SP_IS_TREF(item) || SP_IS_STRING(item))) {
         // do not copy text properties from non-text objects, it's confusing
-        css = sp_css_attr_unset_text (css);
+        css = sp_css_attr_unset_text(css);
     }
 
     // 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);
+        css = sp_css_attr_scale(css, ex);
     }
 
     return css;
@@ -868,40 +948,40 @@ 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"));
+    if (cm->paste(in_place))
+        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"));
+    if (cm->pasteStyle())
+        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,
-                     _("Paste live path effect"));
+    if (cm->pastePathEffect())
+        sp_document_done(sp_desktop_document(desktop), SP_VERB_EDIT_PASTE_LIVEPATHEFFECT,
+                         _("Paste live path effect"));
 }
 
 
 void sp_selection_remove_livepatheffect_impl(SPItem *item)
 {
-    if ( item && SP_IS_LPE_ITEM(item) ) {
+    if ( item && SP_IS_LPE_ITEM(item) &&
+         sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
         sp_lpe_item_remove_all_path_effects(SP_LPE_ITEM(item), false);
     }
 }
 
-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);
@@ -919,13 +999,12 @@ void sp_selection_remove_livepatheffect()
 
     }
 
-    sp_document_done(sp_desktop_document (desktop), SP_VERB_EDIT_REMOVE_LIVEPATHEFFECT,
+    sp_document_done(sp_desktop_document(desktop), SP_VERB_EDIT_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);
@@ -941,31 +1020,29 @@ void sp_selection_remove_filter ()
     sp_desktop_set_style(desktop, css);
     sp_repr_css_attr_unref(css);
 
-    sp_document_done(sp_desktop_document (desktop), SP_VERB_EDIT_REMOVE_FILTER,
+    sp_document_done(sp_desktop_document(desktop), SP_VERB_EDIT_REMOVE_FILTER,
                      _("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,
-                     _("Paste size"));
+    if (cm->pasteSize(false, apply_x, apply_y))
+        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,
-                     _("Paste size separately"));
+    if (cm->pasteSize(true, apply_x, apply_y))
+        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
@@ -974,28 +1051,28 @@ void sp_selection_to_next_layer(bool suppressDone)
         return;
     }
 
-    GSList const *items = g_slist_copy ((GSList *) selection->itemList());
+    GSList const *items = g_slist_copy((GSList *) selection->itemList());
 
     bool no_more = false; // Set to true, if no more layers above
     SPObject *next=Inkscape::next_layer(dt->currentRoot(), dt->currentLayer());
     if (next) {
         GSList *temp_clip = NULL;
-        sp_selection_copy_impl (items, &temp_clip, sp_document_repr_doc(dt->doc()));
-        sp_selection_delete_impl (items, false, false);
+        sp_selection_copy_impl(items, &temp_clip, sp_document_repr_doc(dt->doc()));
+        sp_selection_delete_impl(items, false, false);
         next=Inkscape::next_layer(dt->currentRoot(), dt->currentLayer()); // Fixes bug 1482973: crash while moving layers
         GSList *copied;
-        if(next) {
-            copied = sp_selection_paste_impl (sp_desktop_document (dt), next, &temp_clip);
+        if (next) {
+            copied = sp_selection_paste_impl(sp_desktop_document(dt), next, &temp_clip);
         } else {
-            copied = sp_selection_paste_impl (sp_desktop_document (dt), dt->currentLayer(), &temp_clip);
+            copied = sp_selection_paste_impl(sp_desktop_document(dt), dt->currentLayer(), &temp_clip);
             no_more = true;
         }
         selection->setReprList((GSList const *) copied);
-        g_slist_free (copied);
-        if (temp_clip) g_slist_free (temp_clip);
+        g_slist_free(copied);
+        if (temp_clip) g_slist_free(temp_clip);
         if (next) dt->setCurrentLayer(next);
         if ( !suppressDone ) {
-            sp_document_done(sp_desktop_document (dt), SP_VERB_LAYER_MOVE_TO_NEXT,
+            sp_document_done(sp_desktop_document(dt), SP_VERB_LAYER_MOVE_TO_NEXT,
                              _("Raise to next layer"));
         }
     } else {
@@ -1006,13 +1083,11 @@ void sp_selection_to_next_layer(bool suppressDone)
         dt->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No more layers above."));
     }
 
-    g_slist_free ((GSList *) items);
+    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
@@ -1021,28 +1096,28 @@ void sp_selection_to_prev_layer(bool suppressDone)
         return;
     }
 
-    GSList const *items = g_slist_copy ((GSList *) selection->itemList());
+    GSList const *items = g_slist_copy((GSList *) selection->itemList());
 
     bool no_more = false; // Set to true, if no more layers below
     SPObject *next=Inkscape::previous_layer(dt->currentRoot(), dt->currentLayer());
     if (next) {
         GSList *temp_clip = NULL;
-        sp_selection_copy_impl (items, &temp_clip, sp_document_repr_doc(dt->doc())); // we're in the same doc, so no need to copy defs
-        sp_selection_delete_impl (items, false, false);
+        sp_selection_copy_impl(items, &temp_clip, sp_document_repr_doc(dt->doc())); // we're in the same doc, so no need to copy defs
+        sp_selection_delete_impl(items, false, false);
         next=Inkscape::previous_layer(dt->currentRoot(), dt->currentLayer()); // Fixes bug 1482973: crash while moving layers
         GSList *copied;
-        if(next) {
-            copied = sp_selection_paste_impl (sp_desktop_document (dt), next, &temp_clip);
+        if (next) {
+            copied = sp_selection_paste_impl(sp_desktop_document(dt), next, &temp_clip);
         } else {
-            copied = sp_selection_paste_impl (sp_desktop_document (dt), dt->currentLayer(), &temp_clip);
+            copied = sp_selection_paste_impl(sp_desktop_document(dt), dt->currentLayer(), &temp_clip);
             no_more = true;
         }
         selection->setReprList((GSList const *) copied);
-        g_slist_free (copied);
-        if (temp_clip) g_slist_free (temp_clip);
+        g_slist_free(copied);
+        if (temp_clip) g_slist_free(temp_clip);
         if (next) dt->setCurrentLayer(next);
         if ( !suppressDone ) {
-            sp_document_done(sp_desktop_document (dt), SP_VERB_LAYER_MOVE_TO_PREV,
+            sp_document_done(sp_desktop_document(dt), SP_VERB_LAYER_MOVE_TO_PREV,
                              _("Lower to previous layer"));
         }
     } else {
@@ -1053,11 +1128,11 @@ void sp_selection_to_prev_layer(bool suppressDone)
         dt->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No more layers below."));
     }
 
-    g_slist_free ((GSList *) items);
+    g_slist_free((GSList *) items);
 }
 
 bool
-selection_contains_original (SPItem *item, Inkscape::Selection *selection)
+selection_contains_original(SPItem *item, Inkscape::Selection *selection)
 {
     bool contains_original = false;
 
@@ -1066,7 +1141,7 @@ selection_contains_original (SPItem *item, Inkscape::Selection *selection)
     SPItem *item_use_first = item;
     while (is_use && item_use && !contains_original)
     {
-        item_use = sp_use_get_original (SP_USE(item_use));
+        item_use = sp_use_get_original(SP_USE(item_use));
         contains_original |= selection->includes(item_use);
         if (item_use == item_use_first)
             break;
@@ -1084,7 +1159,7 @@ selection_contains_original (SPItem *item, Inkscape::Selection *selection)
 
 
 bool
-selection_contains_both_clone_and_original (Inkscape::Selection *selection)
+selection_contains_both_clone_and_original(Inkscape::Selection *selection)
 {
     bool clone_with_original = false;
     for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
@@ -1103,7 +1178,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, bool compensate)
 {
     if (selection->isEmpty())
         return;
@@ -1111,23 +1186,23 @@ 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();
 
 #if 0 /* Re-enable this once persistent guides have a graphical indication.
-        At the time of writing, this is the only place to re-enable. */
+         At the time of writing, this is the only place to re-enable. */
         sp_item_update_cns(*item, selection->desktop());
 #endif
 
         // we're moving both a clone and its original or any ancestor in clone chain?
         bool transform_clone_with_original = selection_contains_original(item, selection);
         // ...both a text-on-path and its path?
-        bool transform_textpath_with_path = (SP_IS_TEXT_TEXTPATH(item) && selection->includes( sp_textpath_get_path_item (SP_TEXTPATH(sp_object_first_child(SP_OBJECT(item)))) ));
+        bool transform_textpath_with_path = (SP_IS_TEXT_TEXTPATH(item) && selection->includes( sp_textpath_get_path_item(SP_TEXTPATH(sp_object_first_child(SP_OBJECT(item)))) ));
         // ...both a flowtext and its frame?
-        bool transform_flowtext_with_frame = (SP_IS_FLOWTEXT(item) && selection->includes( SP_FLOWTEXT(item)->get_frame (NULL))); // (only the first frame is checked so far)
+        bool transform_flowtext_with_frame = (SP_IS_FLOWTEXT(item) && selection->includes( SP_FLOWTEXT(item)->get_frame(NULL))); // (only the first frame is checked so far)
         // ...both an offset and its source?
-        bool transform_offset_with_source = (SP_IS_OFFSET(item) && SP_OFFSET (item)->sourceHref) && selection->includes( sp_offset_get_source (SP_OFFSET(item)) );
+        bool transform_offset_with_source = (SP_IS_OFFSET(item) && SP_OFFSET(item)->sourceHref) && selection->includes( sp_offset_get_source(SP_OFFSET(item)) );
 
         // If we're moving a connector, we want to detach it
         // from shapes that aren't part of the selection, but
@@ -1144,20 +1219,21 @@ 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);
 
-       // If this is a clone and it's selected along with its original, do not move it; it will feel the
-       // transform of its original and respond to it itself. Without this, a clone is doubly
-       // transformed, very unintuitive.
-      // Same for textpath if we are also doing ANY transform to its path: do not touch textpath,
-      // letters cannot be squeezed or rotated anyway, they only refill the changed path.
-      // Same for linked offset if we are also moving its source: do not move it.
+        /* If this is a clone and it's selected along with its original, do not move it;
+         * it will feel the transform of its original and respond to it itself.
+         * Without this, a clone is doubly transformed, very unintuitive.
+         *
+         * Same for textpath if we are also doing ANY transform to its path: do not touch textpath,
+         * letters cannot be squeezed or rotated anyway, they only refill the changed path.
+         * Same for linked offset if we are also moving its source: do not move it. */
         if (transform_textpath_with_path || transform_offset_with_source) {
-               // restore item->transform field from the repr, in case it was changed by seltrans
-            sp_object_read_attr (SP_OBJECT (item), "transform");
-
+            // Restore item->transform field from the repr, in case it was changed by seltrans.
+            sp_object_read_attr(SP_OBJECT(item), "transform");
         } else if (transform_flowtext_with_frame) {
             // apply the inverse of the region's transform to the <use> so that the flow remains
             // the same (even though the output itself gets transformed)
@@ -1166,7 +1242,7 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, NR::Matrix const
                     continue;
                 for (SPObject *use = region->firstChild() ; use ; use = SP_OBJECT_NEXT(use)) {
                     if (!SP_IS_USE(use)) continue;
-                    sp_item_write_transform(SP_USE(use), SP_OBJECT_REPR(use), item->transform.inverse(), NULL);
+                    sp_item_write_transform(SP_USE(use), SP_OBJECT_REPR(use), item->transform.inverse(), NULL, compensate);
                 }
             }
         } else if (transform_clone_with_original) {
@@ -1175,56 +1251,55 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, NR::Matrix const
             // transform and its move compensation are both cancelled out.
 
             // restore item->transform field from the repr, in case it was changed by seltrans
-            sp_object_read_attr (SP_OBJECT (item), "transform");
+            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;
-                    sp_item_write_transform(item, SP_OBJECT_REPR(item), move, &move);
+                    Geom::Matrix move = result * clone_move * t_inv;
+                    sp_item_write_transform(item, SP_OBJECT_REPR(item), move, &move, compensate);
 
                 } 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;
-                    sp_item_write_transform(item, SP_OBJECT_REPR(item), move, &t);
+                    //    clone_move = Geom::identity();
+                    Geom::Matrix move = result * clone_move;
+                    sp_item_write_transform(item, SP_OBJECT_REPR(item), move, &t, compensate);
                 }
 
             } else {
                 // just apply the result
-                sp_item_write_transform(item, SP_OBJECT_REPR(item), result, &t);
+                sp_item_write_transform(item, SP_OBJECT_REPR(item), result, &t, compensate);
             }
 
         } 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);
+            sp_item_write_transform(item, SP_OBJECT_REPR(item), item->transform, NULL, compensate);
         }
 
         // 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() || affine.isIdentity())) {
             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 +1323,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,
-                            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 newSize(x1 - x0,
+                              y1 - y0);
+    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, bool compensate)
 {
-    sp_selection_apply_affine(selection, NR::Matrix(NR::translate(move)));
+    sp_selection_apply_affine(selection, Geom::Matrix(Geom::Translate(move)), true, compensate);
 }
 
 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));
-    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_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));
+    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_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 +1424,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 +1439,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 +1463,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 +1472,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 +1492,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 +1506,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 +1529,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 +1564,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 +1642,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 +1670,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,19 +1736,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;
+                        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)
-                            np->helperpath_rgba = clip ? 
-                                prefs_get_int_attribute("options.wireframecolors", "clips", 0x00ff00ff) : 
-                                prefs_get_int_attribute("options.wireframecolors", "masks", 0x0000ffff);
+                            Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+                            np->helperpath_rgba = clip ?
+                                prefs->getInt("/options/wireframecolors/clips", 0x00ff00ff) :
+                                prefs->getInt("/options/wireframecolors/masks", 0x0000ffff);
                             np->helperpath_width = 1.0;
                             sp_nodepath_show_helperpath(np, true);
                         }
@@ -1794,15 +1852,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);
@@ -1811,9 +1869,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;
 
@@ -1844,7 +1901,7 @@ sp_selection_clone()
         clone->setAttribute("x", "0", false);
         clone->setAttribute("y", "0", false);
         clone->setAttribute("xlink:href", g_strdup_printf("#%s", sel_repr->attribute("id")), false);
-        
+
         clone->setAttribute("inkscape:transform-center-x", sel_repr->attribute("inkscape:transform-center-x"), false);
         clone->setAttribute("inkscape:transform-center-y", sel_repr->attribute("inkscape:transform-center-y"), false);
 
@@ -1867,9 +1924,8 @@ sp_selection_clone()
 }
 
 void
-sp_selection_relink()
+sp_selection_relink(SPDesktop *desktop)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (!desktop)
         return;
 
@@ -1886,7 +1942,7 @@ sp_selection_relink()
         desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Copy an <b>object</b> to clipboard to relink clones to."));
         return;
     }
-    gchar *newref = g_strdup_printf ("#%s", newid);
+    gchar *newref = g_strdup_printf("#%s", newid);
 
     // Get a copy of current selection.
     bool relinked = false;
@@ -1910,15 +1966,14 @@ sp_selection_relink()
         desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("<b>No clones to relink</b> in the selection."));
     } else {
         sp_document_done(sp_desktop_document(desktop), SP_VERB_EDIT_UNLINK_CLONE,
-                     _("Relink clone"));
+                         _("Relink clone"));
     }
 }
 
 
 void
-sp_selection_unlink()
+sp_selection_unlink(SPDesktop *desktop)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (!desktop)
         return;
 
@@ -1982,9 +2037,8 @@ sp_selection_unlink()
 }
 
 void
-sp_select_clone_original()
+sp_select_clone_original(SPDesktop *desktop)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (desktop == NULL)
         return;
 
@@ -2002,13 +2056,13 @@ sp_select_clone_original()
 
     SPItem *original = NULL;
     if (SP_IS_USE(item)) {
-        original = sp_use_get_original (SP_USE(item));
-    } else if (SP_IS_OFFSET(item) && SP_OFFSET (item)->sourceHref) {
-        original = sp_offset_get_source (SP_OFFSET(item));
+        original = sp_use_get_original(SP_USE(item));
+    } else if (SP_IS_OFFSET(item) && SP_OFFSET(item)->sourceHref) {
+        original = sp_offset_get_source(SP_OFFSET(item));
     } else if (SP_IS_TEXT_TEXTPATH(item)) {
-        original = sp_textpath_get_path_item (SP_TEXTPATH(sp_object_first_child(SP_OBJECT(item))));
+        original = sp_textpath_get_path_item(SP_TEXTPATH(sp_object_first_child(SP_OBJECT(item))));
     } else if (SP_IS_FLOWTEXT(item)) {
-        original = SP_FLOWTEXT(item)->get_frame (NULL); // first frame only
+        original = SP_FLOWTEXT(item)->get_frame(NULL); // first frame only
     } else { // it's an object that we don't know what to do with
         desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, error);
         return;
@@ -2019,14 +2073,33 @@ sp_select_clone_original()
         return;
     }
 
-    for (SPObject *o = original; o && !SP_IS_ROOT(o); o = SP_OBJECT_PARENT (o)) {
-        if (SP_IS_DEFS (o)) {
+    for (SPObject *o = original; o && !SP_IS_ROOT(o); o = SP_OBJECT_PARENT(o)) {
+        if (SP_IS_DEFS(o)) {
             desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("The object you're trying to select is <b>not visible</b> (it is in &lt;defs&gt;)"));
             return;
         }
     }
 
     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) {
@@ -2036,9 +2109,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;
 
@@ -2054,73 +2126,75 @@ 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());
 
-    items = g_slist_sort (items, (GCompareFunc) sp_object_compare_position);
+    items = g_slist_sort(items, (GCompareFunc) sp_object_compare_position);
 
     // bottommost object, after sorting
-    SPObject *parent = SP_OBJECT_PARENT (items->data);
+    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();
+    gint pos = SP_OBJECT_REPR(items->data)->position();
     (void)pos; // TODO check why this was remembered
 
     // create a list of duplicates
     GSList *repr_copies = NULL;
     for (GSList *i = items; i != NULL; i = i->next) {
-        Inkscape::XML::Node *dup = (SP_OBJECT_REPR (i->data))->duplicate(xml_doc);
-        repr_copies = g_slist_prepend (repr_copies, dup);
+        Inkscape::XML::Node *dup = (SP_OBJECT_REPR(i->data))->duplicate(xml_doc);
+        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
         for (GSList *i = items; i != NULL; i = i->next) {
-            SPObject *item = SP_OBJECT (i->data);
-            item->deleteObject (false);
+            SPObject *item = SP_OBJECT(i->data);
+            item->deleteObject(false);
         }
     }
 
     // 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);
+    g_slist_free(items);
 
-    sp_document_done (doc, SP_VERB_EDIT_SELECTION_2_MARKER,
-                      _("Objects to marker"));
+    sp_document_done(doc, SP_VERB_EDIT_SELECTION_2_MARKER,
+                     _("Objects to marker"));
 }
 
 static void sp_selection_to_guides_recursive(SPItem *item, bool deleteitem, bool wholegroups) {
     if (SP_IS_GROUP(item) && !SP_IS_BOX3D(item) && !wholegroups) {
-        for (GSList *i = sp_item_group_item_list (SP_GROUP(item)); i != NULL; i = i->next) {
+        for (GSList *i = sp_item_group_item_list(SP_GROUP(item)); i != NULL; i = i->next) {
             sp_selection_to_guides_recursive(SP_ITEM(i->data), deleteitem, wholegroups);
         }
     } else {
@@ -2132,9 +2206,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;
 
@@ -2147,21 +2220,21 @@ 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);
     }
 
-    sp_document_done (doc, SP_VERB_EDIT_SELECTION_2_GUIDES, _("Objects to guides"));
+    sp_document_done(doc, SP_VERB_EDIT_SELECTION_2_GUIDES, _("Objects to guides"));
 }
 
 void
-sp_selection_tile(bool apply)
+sp_selection_tile(SPDesktop *desktop, bool apply)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (desktop == NULL)
         return;
 
@@ -2177,78 +2250,79 @@ 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());
 
-    items = g_slist_sort (items, (GCompareFunc) sp_object_compare_position);
+    items = g_slist_sort(items, (GCompareFunc) sp_object_compare_position);
 
     // bottommost object, after sorting
-    SPObject *parent = SP_OBJECT_PARENT (items->data);
+    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();
+    gint pos = SP_OBJECT_REPR(items->data)->position();
 
     // create a list of duplicates
     GSList *repr_copies = NULL;
     for (GSList *i = items; i != NULL; i = i->next) {
-        Inkscape::XML::Node *dup = (SP_OBJECT_REPR (i->data))->duplicate(xml_doc);
-        repr_copies = g_slist_prepend (repr_copies, dup);
+        Inkscape::XML::Node *dup = (SP_OBJECT_REPR(i->data))->duplicate(xml_doc);
+        repr_copies = g_slist_prepend(repr_copies, dup);
     }
     // restore the z-order after prepends
-    repr_copies = g_slist_reverse (repr_copies);
+    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
         for (GSList *i = items; i != NULL; i = i->next) {
-            SPObject *item = SP_OBJECT (i->data);
-            item->deleteObject (false);
+            SPObject *item = SP_OBJECT(i->data);
+            item->deleteObject(false);
         }
     }
 
     // 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);
+        SP_OBJECT_REPR(parent)->appendChild(rect);
         rect->setPosition(pos > 0 ? pos : 0);
-        SPItem *rectangle = (SPItem *) sp_desktop_document (desktop)->getObjectByRepr(rect);
+        SPItem *rectangle = (SPItem *) sp_desktop_document(desktop)->getObjectByRepr(rect);
 
         Inkscape::GC::release(rect);
 
@@ -2256,16 +2330,15 @@ sp_selection_tile(bool apply)
         selection->set(rectangle);
     }
 
-    g_slist_free (items);
+    g_slist_free(items);
 
-    sp_document_done (doc, SP_VERB_EDIT_TILE,
-                      _("Objects to pattern"));
+    sp_document_done(doc, SP_VERB_EDIT_TILE,
+                     _("Objects to pattern"));
 }
 
 void
-sp_selection_untile()
+sp_selection_untile(SPDesktop *desktop)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (desktop == NULL)
         return;
 
@@ -2290,7 +2363,7 @@ sp_selection_untile()
 
         SPItem *item = (SPItem *) items->data;
 
-        SPStyle *style = SP_OBJECT_STYLE (item);
+        SPStyle *style = SP_OBJECT_STYLE(item);
 
         if (!style || !style->fill.isPaintserver())
             continue;
@@ -2302,30 +2375,30 @@ sp_selection_untile()
 
         did = true;
 
-        SPPattern *pattern = pattern_getroot (SP_PATTERN (server));
+        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) ) {
             Inkscape::XML::Node *copy = SP_OBJECT_REPR(child)->duplicate(xml_doc);
-            SPItem *i = SP_ITEM (desktop->currentLayer()->appendChildRepr(copy));
+            SPItem *i = SP_ITEM(desktop->currentLayer()->appendChildRepr(copy));
 
            // FIXME: relink clones to the new canvas objects
            // use SPObject::setid when mental finishes it to steal ids of
 
             // this is needed to make sure the new item has curve (simply requestDisplayUpdate does not work)
-            sp_document_ensure_up_to_date (doc);
+            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);
         }
 
-        SPCSSAttr *css = sp_repr_css_attr_new ();
-        sp_repr_css_set_property (css, "fill", "none");
-        sp_repr_css_change (SP_OBJECT_REPR (item), css, "style");
+        SPCSSAttr *css = sp_repr_css_attr_new();
+        sp_repr_css_set_property(css, "fill", "none");
+        sp_repr_css_change(SP_OBJECT_REPR(item), css, "style");
     }
 
     if (!did) {
@@ -2338,7 +2411,7 @@ sp_selection_untile()
 }
 
 void
-sp_selection_get_export_hints (Inkscape::Selection *selection, char const **filename, float *xdpi, float *ydpi)
+sp_selection_get_export_hints(Inkscape::Selection *selection, char const **filename, float *xdpi, float *ydpi)
 {
     if (selection->isEmpty()) {
         return;
@@ -2349,7 +2422,7 @@ sp_selection_get_export_hints (Inkscape::Selection *selection, char const **file
     bool xdpi_search = TRUE;
     bool ydpi_search = TRUE;
 
-    for(; reprlst != NULL &&
+    for (; reprlst != NULL &&
             filename_search &&
             xdpi_search &&
             ydpi_search;
@@ -2384,7 +2457,7 @@ sp_selection_get_export_hints (Inkscape::Selection *selection, char const **file
 }
 
 void
-sp_document_get_export_hints (SPDocument *doc, char const **filename, float *xdpi, float *ydpi)
+sp_document_get_export_hints(SPDocument *doc, char const **filename, float *xdpi, float *ydpi)
 {
     Inkscape::XML::Node * repr = sp_document_repr_root(doc);
     gchar const *dpi_string;
@@ -2405,9 +2478,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;
 
@@ -2428,7 +2500,7 @@ sp_selection_create_bitmap_copy ()
 
     // Get the bounding box of the selection
     NRRect bbox;
-    sp_document_ensure_up_to_date (document);
+    sp_document_ensure_up_to_date(document);
     selection->bounds(&bbox);
     if (NR_RECT_DFLS_TEST_EMPTY(&bbox)) {
         desktop->clearWaitingCursor();
@@ -2436,7 +2508,7 @@ sp_selection_create_bitmap_copy ()
     }
 
     // List of the items to show; all others will be hidden
-    GSList *items = g_slist_copy ((GSList *) selection->itemList());
+    GSList *items = g_slist_copy((GSList *) selection->itemList());
 
     // Sort items so that the topmost comes last
     items = g_slist_sort(items, (GCompareFunc) sp_item_repr_compare_position);
@@ -2444,18 +2516,29 @@ sp_selection_create_bitmap_copy ()
     // Generate a random value from the current time (you may create bitmap from the same object(s)
     // multiple times, and this is done so that they don't clash)
     GTimeVal cu;
-    g_get_current_time (&cu);
+    g_get_current_time(&cu);
     guint current = (int) (cu.tv_sec * 1000000 + cu.tv_usec) % 1024;
 
-    // Create the filename
-    gchar *filename = g_strdup_printf ("%s-%s-%u.png", document->name, SP_OBJECT_REPR(items->data)->attribute("id"), current);
+    // Create the filename.
+    gchar *const basename = g_strdup_printf("%s-%s-%u.png",
+                                            document->name,
+                                            SP_OBJECT_REPR(items->data)->attribute("id"),
+                                            current);
     // 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);
+    g_strcanon(basename, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.=+~$#@^&!?", '_');
 
-    //g_print ("%s\n", filepath);
+    // 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, basename, NULL);
+
+    //g_print("%s\n", filepath);
 
     // Remember parent and z-order of the topmost one
     gint pos = SP_OBJECT_REPR(g_slist_last(items)->data)->position();
@@ -2463,25 +2546,26 @@ 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;
     } else if (0 < prefs_min) {
         // If minsize is given, look up minimum bitmap size (default 250 pixels) and calculate resolution from it
-        res = PX_PER_IN * prefs_min / MIN ((bbox.x1 - bbox.x0), (bbox.y1 - bbox.y0));
+        res = PX_PER_IN * prefs_min / MIN((bbox.x1 - bbox.x0), (bbox.y1 - bbox.y0));
     } else {
         float hint_xdpi = 0, hint_ydpi = 0;
         char const *hint_filename;
         // take resolution hint from the selected objects
-        sp_selection_get_export_hints (selection, &hint_filename, &hint_xdpi, &hint_ydpi);
+        sp_selection_get_export_hints(selection, &hint_filename, &hint_xdpi, &hint_ydpi);
         if (hint_xdpi != 0) {
             res = hint_xdpi;
         } else {
             // take resolution hint from the document
-            sp_document_get_export_hints (document, &hint_filename, &hint_xdpi, &hint_ydpi);
+            sp_document_get_export_hints(document, &hint_filename, &hint_xdpi, &hint_ydpi);
             if (hint_xdpi != 0) {
                 res = hint_xdpi;
             } else {
@@ -2492,70 +2576,74 @@ sp_selection_create_bitmap_copy ()
     }
 
     // The width and height of the bitmap in pixels
-    unsigned width = (unsigned) floor ((bbox.x1 - bbox.x0) * res / PX_PER_IN);
-    unsigned height =(unsigned) floor ((bbox.y1 - bbox.y0) * res / PX_PER_IN);
+    unsigned width = (unsigned) floor((bbox.x1 - bbox.x0) * res / PX_PER_IN);
+    unsigned height =(unsigned) floor((bbox.y1 - bbox.y0) * res / PX_PER_IN);
 
     // 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;
     if (res == PX_PER_IN) { // for default 90 dpi, snap it to pixel grid
-        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
+        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,
-                   bbox.x0, bbox.y0, bbox.x1, bbox.y1,
-                   width, height, res, res,
-                   (guint32) 0xffffff00,
-                   NULL, NULL,
-                   true,  /*bool force_overwrite,*/
-                   items);
+                       bbox.x0, bbox.y0, bbox.x1, bbox.y1,
+                       width, height, res, res,
+                       (guint32) 0xffffff00,
+                       NULL, NULL,
+                       true,  /*bool force_overwrite,*/
+                       items);
 
-    g_slist_free (items);
+    g_slist_free(items);
 
     // Run filter, if any
     if (run) {
-        g_print ("Running external filter: %s\n", run);
+        g_print("Running external filter: %s\n", run);
         int retval;
-        retval = system (run);
+        retval = system(run);
     }
 
     // Import the image back
-    GdkPixbuf *pb = gdk_pixbuf_new_from_file (filepath, NULL);
+    GdkPixbuf *pb = gdk_pixbuf_new_from_file(filepath, NULL);
     if (pb) {
         // Create the repr for the image
         Inkscape::XML::Node * repr = xml_doc->createElement("svg:image");
-        repr->setAttribute("xlink:href", filename);
-        repr->setAttribute("sodipodi:absref", filepath);
+        {
+            repr->setAttribute("sodipodi:absref", filepath);
+            gchar *abs_base = Inkscape::XML::calc_abs_doc_base(document->base);
+            repr->setAttribute("xlink:href", sp_relative_path_from_path(filepath, abs_base));
+            g_free(abs_base);
+        }
         if (res == PX_PER_IN) { // for default 90 dpi, snap it to pixel grid
             sp_repr_set_svg_double(repr, "width", width);
             sp_repr_set_svg_double(repr, "height", height);
@@ -2581,17 +2669,17 @@ sp_selection_create_bitmap_copy ()
 
         // Clean up
         Inkscape::GC::release(repr);
-        gdk_pixbuf_unref (pb);
+        gdk_pixbuf_unref(pb);
 
         // Complete undoable transaction
-        sp_document_done (document, SP_VERB_SELECTION_CREATE_BITMAP,
-                          _("Create bitmap"));
+        sp_document_done(document, SP_VERB_SELECTION_CREATE_BITMAP,
+                         _("Create bitmap"));
     }
 
     desktop->clearWaitingCursor();
 
-    g_free (filename);
-    g_free (filepath);
+    g_free(basename);
+    g_free(filepath);
 }
 
 /**
@@ -2604,9 +2692,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;
 
@@ -2627,7 +2714,7 @@ sp_selection_set_mask(bool apply_clip_path, bool apply_to_layer)
 
     // FIXME: temporary patch to prevent crash!
     // Remove this when bboxes are fixed to not blow up on an item clipped/masked with its own clone
-    bool clone_with_original = selection_contains_both_clone_and_original (selection);
+    bool clone_with_original = selection_contains_both_clone_and_original(selection);
     if (clone_with_original) {
         return; // in this version, you cannot clip/mask an object with its own clone
     }
@@ -2637,58 +2724,59 @@ sp_selection_set_mask(bool apply_clip_path, bool apply_to_layer)
 
     GSList *items = g_slist_copy((GSList *) selection->itemList());
 
-    items = g_slist_sort (items, (GCompareFunc) sp_object_compare_position);
+    items = g_slist_sort(items, (GCompareFunc) sp_object_compare_position);
 
     // create a list of duplicates
     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
-        apply_to_items = g_slist_prepend (apply_to_items, desktop->currentLayer());
+        apply_to_items = g_slist_prepend(apply_to_items, desktop->currentLayer());
 
         for (GSList *i = items; i != NULL; i = i->next) {
-            Inkscape::XML::Node *dup = (SP_OBJECT_REPR (i->data))->duplicate(xml_doc);
-            mask_items = g_slist_prepend (mask_items, dup);
+            Inkscape::XML::Node *dup = (SP_OBJECT_REPR(i->data))->duplicate(xml_doc);
+            mask_items = g_slist_prepend(mask_items, dup);
 
             if (remove_original) {
-                SPObject *item = SP_OBJECT (i->data);
-                items_to_delete = g_slist_prepend (items_to_delete, item);
+                SPObject *item = SP_OBJECT(i->data);
+                items_to_delete = g_slist_prepend(items_to_delete, item);
             }
         }
     } else if (!topmost) {
         // topmost item is used as a mask, which is applied to other items in a selection
         GSList *i = items;
-        Inkscape::XML::Node *dup = (SP_OBJECT_REPR (i->data))->duplicate(xml_doc);
-        mask_items = g_slist_prepend (mask_items, dup);
+        Inkscape::XML::Node *dup = (SP_OBJECT_REPR(i->data))->duplicate(xml_doc);
+        mask_items = g_slist_prepend(mask_items, dup);
 
         if (remove_original) {
-            SPObject *item = SP_OBJECT (i->data);
-            items_to_delete = g_slist_prepend (items_to_delete, item);
+            SPObject *item = SP_OBJECT(i->data);
+            items_to_delete = g_slist_prepend(items_to_delete, item);
         }
 
         for (i = i->next; i != NULL; i = i->next) {
-            apply_to_items = g_slist_prepend (apply_to_items, i->data);
+            apply_to_items = g_slist_prepend(apply_to_items, i->data);
         }
     } else {
         GSList *i = NULL;
         for (i = items; NULL != i->next; i = i->next) {
-            apply_to_items = g_slist_prepend (apply_to_items, i->data);
+            apply_to_items = g_slist_prepend(apply_to_items, i->data);
         }
 
-        Inkscape::XML::Node *dup = (SP_OBJECT_REPR (i->data))->duplicate(xml_doc);
-        mask_items = g_slist_prepend (mask_items, dup);
+        Inkscape::XML::Node *dup = (SP_OBJECT_REPR(i->data))->duplicate(xml_doc);
+        mask_items = g_slist_prepend(mask_items, dup);
 
         if (remove_original) {
-            SPObject *item = SP_OBJECT (i->data);
-            items_to_delete = g_slist_prepend (items_to_delete, item);
+            SPObject *item = SP_OBJECT(i->data);
+            items_to_delete = g_slist_prepend(items_to_delete, item);
         }
     }
 
-    g_slist_free (items);
+    g_slist_free(items);
     items = NULL;
 
     gchar const *attributeName = apply_clip_path ? "clip-path" : "mask";
@@ -2696,12 +2784,12 @@ 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) {
             Inkscape::XML::Node *dup = reinterpret_cast<Inkscape::XML::Node *>(mask_item->data)->duplicate(xml_doc);
-            mask_items_dup = g_slist_prepend (mask_items_dup, dup);
+            mask_items_dup = g_slist_prepend(mask_items_dup, dup);
         }
 
         gchar const *mask_id = NULL;
@@ -2711,29 +2799,28 @@ sp_selection_set_mask(bool apply_clip_path, bool apply_to_layer)
             mask_id = sp_mask_create(mask_items_dup, doc, &maskTransform);
         }
 
-        g_slist_free (mask_items_dup);
+        g_slist_free(mask_items_dup);
         mask_items_dup = NULL;
 
         SP_OBJECT_REPR(i->data)->setAttribute(attributeName, g_strdup_printf("url(#%s)", mask_id));
     }
 
-    g_slist_free (mask_items);
-    g_slist_free (apply_to_items);
+    g_slist_free(mask_items);
+    g_slist_free(apply_to_items);
 
     for (GSList *i = items_to_delete; NULL != i; i = i->next) {
-        SPObject *item = SP_OBJECT (i->data);
-        item->deleteObject (false);
+        SPObject *item = SP_OBJECT(i->data);
+        item->deleteObject(false);
     }
-    g_slist_free (items_to_delete);
+    g_slist_free(items_to_delete);
 
     if (apply_clip_path)
-        sp_document_done (doc, SP_VERB_OBJECT_SET_CLIPPATH, _("Set clipping path"));
+        sp_document_done(doc, SP_VERB_OBJECT_SET_CLIPPATH, _("Set clipping path"));
     else
-        sp_document_done (doc, SP_VERB_OBJECT_SET_MASK, _("Set mask"));
+        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;
 
@@ -2747,12 +2834,13 @@ 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; 
-    // SPObject* refers to a group containing the clipped path or mask itself, 
+    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) {
@@ -2780,9 +2868,9 @@ void sp_selection_unset_mask(bool apply_clip_path) {
         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 
+            // 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);
+            items_to_move = g_slist_prepend(items_to_move, copy);
         }
 
         if (!obj->isReferenced()) {
@@ -2802,22 +2890,22 @@ void sp_selection_unset_mask(bool apply_clip_path) {
             parent->appendChild(repr);
             repr->setPosition((pos + 1) > 0 ? (pos + 1) : 0);
 
-            SPItem *mask_item = (SPItem *) sp_desktop_document (desktop)->getObjectByRepr(repr);
+            SPItem *mask_item = (SPItem *) sp_desktop_document(desktop)->getObjectByRepr(repr);
             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);
         }
 
-        g_slist_free (items_to_move);
+        g_slist_free(items_to_move);
     }
 
     if (apply_clip_path)
-        sp_document_done (doc, SP_VERB_OBJECT_UNSET_CLIPPATH, _("Release clipping path"));
+        sp_document_done(doc, SP_VERB_OBJECT_UNSET_CLIPPATH, _("Release clipping path"));
     else
-        sp_document_done (doc, SP_VERB_OBJECT_UNSET_MASK, _("Release mask"));
+        sp_document_done(doc, SP_VERB_OBJECT_UNSET_MASK, _("Release mask"));
 }
 
 /**
@@ -2836,8 +2924,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 {
@@ -2864,8 +2952,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 {