Code

now that selection description includes style (filtered, clipped), we need to update...
[inkscape.git] / src / selection-chemistry.cpp
index 6bc1c54d33237c1293fd81772268e4851e69d659..badf27ecb45caa4d93541f5e806a371f43b66fd9 100644 (file)
 #include "libnr/nr-matrix-translate-ops.h"
 #include "libnr/nr-scale-ops.h"
 #include <libnr/nr-matrix-ops.h>
-#include <libnr/nr-rotate-ops.h>
-#include "libnr/nr-scale-translate-ops.h"
-#include "libnr/nr-translate-matrix-ops.h"
-#include "libnr/nr-translate-scale-ops.h"
+#include <2geom/transforms.h>
 #include "xml/repr.h"
 #include "style.h"
 #include "document-private.h"
@@ -85,6 +82,7 @@
 #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"
@@ -94,8 +92,8 @@
 
 #include "ui/clipboard.h"
 
-using NR::X;
-using NR::Y;
+using Geom::X;
+using Geom::Y;
 
 /* The clipboard handling is in ui/clipboard.cpp now. There are some legacy functions left here,
 because the layer manipulation code uses them. It should be rewritten specifically
@@ -105,7 +103,7 @@ for that purpose. */
  * Copies repr and its inherited css style elements, along with the accumulated transform 'full_t',
  * then prepends the copy to 'clip'.
  */
-void sp_selection_copy_one (Inkscape::XML::Node *repr, NR::Matrix full_t, GSList **clip, Inkscape::XML::Document* xml_doc)
+void sp_selection_copy_one (Inkscape::XML::Node *repr, Geom::Matrix full_t, GSList **clip, Inkscape::XML::Document* xml_doc)
 {
     Inkscape::XML::Node *copy = repr->duplicate(xml_doc);
 
@@ -150,10 +148,10 @@ GSList *sp_selection_paste_impl (SPDocument *doc, SPObject *parent, GSList **cli
         Inkscape::XML::Node *copy = repr->duplicate(xml_doc);
 
         // premultiply the item transform by the accumulated parent transform in the paste layer
-        NR::Matrix local (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");
-            Geom::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();
@@ -263,7 +261,7 @@ void sp_selection_duplicate(SPDesktop *desktop, bool suppressDone)
     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");
+    bool relink_clones = prefs->getBool("/options/relinkclonesonduplicate/value");
 
     while (reprs) {
         Inkscape::XML::Node *old_repr = (Inkscape::XML::Node *) reprs->data;
@@ -371,9 +369,9 @@ void sp_edit_select_all_full (SPDesktop *dt, bool force_all_layers, bool invert)
     g_return_if_fail(SP_IS_GROUP(dt->currentLayer()));
     
     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);
+    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;
 
@@ -492,7 +490,7 @@ void sp_selection_group(SPDesktop *desktop)
                 // 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 (NR::identity());
+                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())));
@@ -596,6 +594,36 @@ void sp_selection_ungroup(SPDesktop *desktop)
                      _("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)
 {
@@ -617,14 +645,14 @@ sp_item_list_common_parent_group(GSList const *items)
 }
 
 /** Finds out the minimum common bbox of the selected items. */
-static boost::optional<NR::Rect>
+static Geom::OptRect
 enclose_items(GSList const *items)
 {
     g_assert(items != NULL);
 
-    boost::optional<NR::Rect> r;
+    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;
 }
@@ -670,7 +698,7 @@ sp_selection_raise(SPDesktop *desktop)
     rev = g_slist_sort(rev, (GCompareFunc) sp_item_repr_compare_position);
 
     // Determine the common bbox of the selected items.
-    boost::optional<NR::Rect> selected = enclose_items(items);
+    Geom::OptRect selected = enclose_items(items);
 
     // Iterate over all objects in the selection (starting from top).
     if (selected) {
@@ -680,7 +708,7 @@ sp_selection_raise(SPDesktop *desktop)
             for (SPObject *newref = child->next; newref; newref = newref->next) {
                 // if the sibling is an item AND overlaps our selection,
                 if (SP_IS_ITEM(newref)) {
-                    boost::optional<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)) {
@@ -760,7 +788,7 @@ sp_selection_lower(SPDesktop *desktop)
     Inkscape::XML::Node *grepr = SP_OBJECT_REPR(group);
 
     // Determine the common bbox of the selected items.
-    boost::optional<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);
@@ -775,7 +803,7 @@ sp_selection_lower(SPDesktop *desktop)
             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)) {
-                    boost::optional<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)) {
@@ -899,7 +927,7 @@ take_style_from_item (SPItem *item)
     }
 
     // FIXME: also transform gradient/pattern fills, by forking? NO, this must be nondestructive
-    double ex = NR::expansion(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);
     }
@@ -1185,7 +1213,7 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, Geom::Matrix cons
 
         // "clones are unmoved when original is moved" preference
         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
-        int compensation = prefs->getInt("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
+        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);
 
@@ -1219,26 +1247,26 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, Geom::Matrix cons
             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 = sp_item_i2root_affine(SP_ITEM(SP_OBJECT_PARENT (item)));
-            NR::Matrix t = parent_transform * from_2geom(matrix_to_desktop (matrix_from_desktop (affine, item), item)) * parent_transform.inverse();
-            NR::Matrix t_inv =parent_transform * from_2geom(matrix_to_desktop (matrix_from_desktop (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.isTranslation()) {
                 // we need to cancel out the move compensation, too
 
                 // find out the clone move, same as in sp_use_move_compensate
-                NR::Matrix parent = sp_use_get_parent_transform (SP_USE(item));
-                NR::Matrix clone_move = parent.inverse() * t * parent;
+                Geom::Matrix parent = sp_use_get_parent_transform (SP_USE(item));
+                Geom::Matrix clone_move = parent.inverse() * t * parent;
 
                 if (prefs_parallel) {
-                    NR::Matrix move = result * clone_move * t_inv;
+                    Geom::Matrix move = result * clone_move * t_inv;
                     sp_item_write_transform(item, SP_OBJECT_REPR(item), move, &move);
 
                 } else if (prefs_unmoved) {
                     //if (SP_IS_USE(sp_use_get_original(SP_USE(item))))
-                    //    clone_move = NR::identity();
-                    NR::Matrix move = result * clone_move;
+                    //    clone_move = Geom::identity();
+                    Geom::Matrix move = result * clone_move;
                     sp_item_write_transform(item, SP_OBJECT_REPR(item), move, &t);
                 }
 
@@ -1288,18 +1316,18 @@ sp_selection_scale_absolute(Inkscape::Selection *selection,
     if (selection->isEmpty())
         return;
 
-    boost::optional<NR::Rect> const bbox(selection->bounds());
-    if ( !bbox || bbox->isEmpty() ) {
+    Geom::OptRect const bbox(selection->bounds());
+    if ( !bbox ) {
         return;
     }
 
-    NR::translate const p2o(-bbox->min());
+    Geom::Translate const p2o(-bbox->min());
 
-    NR::scale const newSize(x1 - x0,
+    Geom::Scale const newSize(x1 - x0,
                             y1 - y0);
-    NR::scale const scale( newSize / NR::scale(bbox->dimensions()) );
-    NR::translate const o2n(x0, y0);
-    NR::Matrix const final( p2o * scale * o2n );
+    Geom::Scale const scale( newSize * Geom::Scale(bbox->dimensions()).inverse() );
+    Geom::Translate const o2n(x0, y0);
+    Geom::Matrix const final( p2o * scale * o2n );
 
     sp_selection_apply_affine(selection, final);
 }
@@ -1310,15 +1338,15 @@ void sp_selection_scale_relative(Inkscape::Selection *selection, Geom::Point con
     if (selection->isEmpty())
         return;
 
-    boost::optional<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[Geom::X] > 1e6  ||
-         bbox->extent(NR::Y) * scale[Geom::Y] > 1e6 )
+    if ( bbox->dimensions()[Geom::X] * scale[Geom::X] > 1e6  ||
+         bbox->dimensions()[Geom::Y] * scale[Geom::Y] > 1e6 )
     {
         return;
     }
@@ -1353,12 +1381,12 @@ sp_selection_skew_relative(Inkscape::Selection *selection, Geom::Point const &al
 
 void sp_selection_move_relative(Inkscape::Selection *selection, Geom::Point const &move)
 {
-    sp_selection_apply_affine(selection, NR::Matrix(NR::translate(move)));
+    sp_selection_apply_affine(selection, Geom::Matrix(Geom::Translate(move)));
 }
 
 void sp_selection_move_relative(Inkscape::Selection *selection, double dx, double dy)
 {
-    sp_selection_apply_affine(selection, NR::Matrix(NR::translate(dx, dy)));
+    sp_selection_apply_affine(selection, Geom::Matrix(Geom::Translate(dx, dy)));
 }
 
 /**
@@ -1372,7 +1400,7 @@ void sp_selection_rotate_90(SPDesktop *desktop, bool ccw)
         return;
 
     GSList const *l = selection->itemList();
-    Geom::Rotate const rot_90(NR::Point(0, ccw ? 1 : -1)); // pos. or neg. rotation, depending on the value of ccw
+    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_90);
@@ -1404,6 +1432,21 @@ sp_selection_rotate(Inkscape::Selection *selection, gdouble const angle_degrees)
                            _("Rotate"));
 }
 
+// helper function:
+static
+Geom::Point
+cornerFarthestFrom(Geom::Rect const &r, Geom::Point const &p){
+    Geom::Point m = r.midpoint();
+    unsigned i = 0;
+    if (p[X] < m[X]) {
+        i = 1;
+    }
+    if (p[Y] < m[Y]) {
+        i = 3 - i;
+    }
+    return r.corner(i);
+}
+
 /**
 \param  angle   the angle in "angular pixels", i.e. how many visible pixels must move the outermost point of the rotated object
 */
@@ -1413,7 +1456,7 @@ sp_selection_rotate_screen(Inkscape::Selection *selection, gdouble angle)
     if (selection->isEmpty())
         return;
 
-    boost::optional<NR::Rect> const bbox(selection->bounds());
+    Geom::OptRect const bbox(selection->bounds());
     boost::optional<Geom::Point> center = selection->center();
 
     if ( !bbox || !center ) {
@@ -1422,7 +1465,7 @@ sp_selection_rotate_screen(Inkscape::Selection *selection, gdouble angle)
 
     gdouble const zoom = selection->desktop()->current_zoom();
     gdouble const zmove = angle / zoom;
-    gdouble const r = NR::L2(bbox->cornerFarthestFrom(*center) - *center);
+    gdouble const r = Geom::L2(cornerFarthestFrom(*bbox, *center) - *center);
 
     gdouble const zangle = 180 * atan2(zmove, r) / M_PI;
 
@@ -1442,12 +1485,12 @@ sp_selection_scale(Inkscape::Selection *selection, gdouble grow)
     if (selection->isEmpty())
         return;
 
-    boost::optional<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();
@@ -1479,13 +1522,13 @@ sp_selection_scale_times(Inkscape::Selection *selection, gdouble times)
     if (selection->isEmpty())
         return;
 
-    boost::optional<NR::Rect> sel_bbox = selection->bounds();
+    Geom::OptRect sel_bbox = selection->bounds();
 
     if (!sel_bbox) {
         return;
     }
 
-    NR::Point const center(sel_bbox->midpoint());
+    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"));
@@ -1598,9 +1641,9 @@ sp_selection_item_next(SPDesktop *desktop)
     Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     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);
+    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) {
@@ -1628,9 +1671,9 @@ sp_selection_item_prev(SPDesktop *desktop)
     Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     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);
+    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) {
@@ -1686,10 +1729,10 @@ 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);
@@ -1698,8 +1741,8 @@ void sp_selection_edit_clip_or_mask(SPDesktop * dt, bool clip)
                             // take colors from prefs (same as used in outline mode)
                             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
                             np->helperpath_rgba = clip ? 
-                                prefs->getInt("options.wireframecolors", "clips", 0x00ff00ff) : 
-                                prefs->getInt("options.wireframecolors", "masks", 0x0000ffff);
+                                prefs->getInt("/options/wireframecolors/clips", 0x00ff00ff) : 
+                                prefs->getInt("/options/wireframecolors/masks", 0x0000ffff);
                             np->helperpath_width = 1.0;
                             sp_nodepath_show_helperpath(np, true);
                         }
@@ -1802,15 +1845,15 @@ SPItem *next_item(SPDesktop *desktop, GSList *path, SPObject *root,
  */
 void scroll_to_show_item(SPDesktop *desktop, SPItem *item)
 {
-    NR::Rect dbox = desktop->get_display_area();
-    boost::optional<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);
@@ -2032,10 +2075,10 @@ sp_select_clone_original(SPDesktop *desktop)
 
     if (original) {
         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
-        bool highlight = prefs->getBool("options.highlightoriginal", "value");
+        bool highlight = prefs->getBool("/options/highlightoriginal/value");
         if (highlight) {
-            boost::optional<NR::Rect> a = item->getBounds(sp_item_i2d_affine(item));
-            boost::optional<NR::Rect> b = original->getBounds(sp_item_i2d_affine(original));
+            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();
@@ -2076,16 +2119,16 @@ void sp_selection_to_marker(SPDesktop *desktop, bool apply)
     }
 
     sp_document_ensure_up_to_date(doc);
-    boost::optional<NR::Rect> r = selection->bounds();
+    Geom::OptRect r = selection->bounds();
     boost::optional<Geom::Point> c = selection->center();
-    if ( !r || !c || r->isEmpty() ) {
+    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());
 
@@ -2094,7 +2137,7 @@ void sp_selection_to_marker(SPDesktop *desktop, bool apply)
     // bottommost object, after sorting
     SPObject *parent = SP_OBJECT_PARENT (items->data);
 
-    NR::Matrix parent_transform (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();
@@ -2107,7 +2150,7 @@ void sp_selection_to_marker(SPDesktop *desktop, bool apply)
         repr_copies = g_slist_prepend (repr_copies, dup);
     }
 
-    NR::Rect bounds(desktop->dt2doc(r->min()), desktop->dt2doc(r->max()));
+    Geom::Rect bounds(desktop->dt2doc(r->min()), desktop->dt2doc(r->max()));
 
     if (apply) {
         // delete objects so that their clones don't get alerted; this object will be restored shortly
@@ -2121,18 +2164,19 @@ void sp_selection_to_marker(SPDesktop *desktop, bool apply)
     // without disturbing clones.
     // See ActorAlign::on_button_click() in src/ui/dialog/align-and-distribute.cpp
     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);
+    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->setInt("options.clonecompensation", "value", saved_compensation);
+    prefs->setInt("/options/clonecompensation/value", saved_compensation);
 
 
     g_slist_free (items);
@@ -2171,8 +2215,8 @@ void sp_selection_to_guides(SPDesktop *desktop)
     }
     
     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);
+    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);
@@ -2199,15 +2243,15 @@ sp_selection_tile(SPDesktop *desktop, bool apply)
     }
 
     sp_document_ensure_up_to_date(doc);
-    boost::optional<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());
 
@@ -2216,7 +2260,7 @@ sp_selection_tile(SPDesktop *desktop, bool apply)
     // bottommost object, after sorting
     SPObject *parent = SP_OBJECT_PARENT (items->data);
 
-    NR::Matrix parent_transform (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();
@@ -2230,7 +2274,7 @@ sp_selection_tile(SPDesktop *desktop, bool apply)
     // restore the z-order after prepends
     repr_copies = g_slist_reverse (repr_copies);
 
-    NR::Rect bounds(desktop->dt2doc(r->min()), desktop->dt2doc(r->max()));
+    Geom::Rect bounds(desktop->dt2doc(r->min()), desktop->dt2doc(r->max()));
 
     if (apply) {
         // delete objects so that their clones don't get alerted; this object will be restored shortly
@@ -2244,29 +2288,29 @@ sp_selection_tile(SPDesktop *desktop, bool apply)
     // without disturbing clones.
     // See ActorAlign::on_button_click() in src/ui/dialog/align-and-distribute.cpp
     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);
+    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->setInt("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);
@@ -2326,7 +2370,7 @@ sp_selection_untile(SPDesktop *desktop)
 
         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) ) {
@@ -2339,7 +2383,7 @@ sp_selection_untile(SPDesktop *desktop)
             // this is needed to make sure the new item has curve (simply requestDisplayUpdate does not work)
             sp_document_ensure_up_to_date (doc);
 
-            NR::Matrix transform( i->transform * pat_transform );
+            Geom::Matrix transform( i->transform * pat_transform );
             sp_item_write_transform(i, SP_OBJECT_REPR(i), transform);
 
             new_select = g_slist_prepend(new_select, i);
@@ -2473,8 +2517,16 @@ sp_selection_create_bitmap_copy (SPDesktop *desktop)
     // Imagemagick is known not to handle spaces in filenames, so we replace anything but letters,
     // digits, and a few other chars, with "_"
     filename = g_strcanon (filename, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.=+~$#@^&!?", '_');
-    // Build the complete path by adding document->base if set
-    gchar *filepath = g_build_filename (document->base?document->base:"", filename, NULL);
+
+    // Build the complete path by adding document base dir, if set, otherwise home dir
+    gchar * directory = NULL;
+    if (SP_DOCUMENT_URI(document)) {
+        directory = g_dirname(SP_DOCUMENT_URI(document));
+    }
+    if (directory == NULL) {
+        directory = homedir_path(NULL);
+    }
+    gchar *filepath = g_build_filename (directory, filename, NULL);
 
     //g_print ("%s\n", filepath);
 
@@ -2486,8 +2538,8 @@ sp_selection_create_bitmap_copy (SPDesktop *desktop)
     // Calculate resolution
     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
     double res;
-    int const prefs_res = prefs->getInt("options.createbitmap", "resolution", 0);
-    int const prefs_min = prefs->getInt("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;
@@ -2519,11 +2571,11 @@ sp_selection_create_bitmap_copy (SPDesktop *desktop)
 
     // Find out if we have to run an external filter
     gchar const *run = NULL;
-    Glib::ustring filter = prefs->getString("options.createbitmap", "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
-        Glib::ustring param1 = prefs->getString("options.createbitmap", "filter_param1");
+        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
@@ -2542,8 +2594,8 @@ sp_selection_create_bitmap_copy (SPDesktop *desktop)
     }
 
     // Calculate the matrix that will be applied to the image so that it exactly overlaps the source objects
-    NR::Matrix eek (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;
@@ -2551,7 +2603,7 @@ sp_selection_create_bitmap_copy (SPDesktop *desktop)
         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,
@@ -2665,8 +2717,8 @@ sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_la
     GSList *apply_to_items = NULL;
     GSList *items_to_delete = NULL;
     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
-    bool topmost = prefs->getBool("options.maskobject", "topmost", true);
-    bool remove_original = prefs->getBool("options.maskobject", "remove", true);
+    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
@@ -2718,7 +2770,7 @@ sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_la
         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) {
@@ -2769,7 +2821,7 @@ void sp_selection_unset_mask(SPDesktop *desktop, bool apply_clip_path) {
     }
 
     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
-    bool remove_original = prefs->getBool("options.maskobject", "remove", true);
+    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";
@@ -2828,7 +2880,7 @@ void sp_selection_unset_mask(SPDesktop *desktop, bool apply_clip_path) {
             selection->add(repr);
 
             // transform mask, so it is moved the same spot where mask was applied
-            NR::Matrix transform (mask_item->transform);
+            Geom::Matrix transform (mask_item->transform);
             transform *= (*it).second->transform;
             sp_item_write_transform(mask_item, SP_OBJECT_REPR(mask_item), transform);
         }
@@ -2858,8 +2910,8 @@ fit_canvas_to_selection(SPDesktop *desktop)
         desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to fit canvas to."));
         return false;
     }
-    boost::optional<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 {
@@ -2886,8 +2938,8 @@ fit_canvas_to_drawing(SPDocument *doc)
 
     sp_document_ensure_up_to_date(doc);
     SPItem const *const root = SP_ITEM(doc->root);
-    boost::optional<NR::Rect> const bbox(root->getBounds(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 {