Code

i18n. Context cleanup (context|string replaced with C_).
[inkscape.git] / src / ui / dialog / align-and-distribute.cpp
index a7b2aa991ecdc59b9656700fea347e16757c00dd..a88688d80882fc69ffc3b7d40326c8eae33ac1fb 100644 (file)
@@ -1,7 +1,7 @@
-/**
- * \brief Align and Distribute dialog
- *
- * Authors:
+/** @file
+ * @brief Align and Distribute dialog - implementation
+ */
+/* Authors:
  *   Bryce W. Harrington <bryce@bryceharrington.org>
  *   Aubanel MONNIER <aubi@libertysurf.fr>
  *   Frank Felfe <innerspace@iname.com>
 # include <config.h>
 #endif
 
-#include "verbs.h"
-
-#include "dialogs/unclump.h"
-#include "removeoverlap/removeoverlap.h"
-#include "graphlayout/graphlayout.h"
-
 #include <gtkmm/spinbutton.h>
 
-
-
-
-#include "util/glib-list-iterators.h"
-
-#include "widgets/icon.h"
-
-#include "inkscape.h"
-#include "document.h"
-#include "selection.h"
 #include "desktop-handles.h"
+#include "unclump.h"
+#include "document.h"
+#include "enums.h"
+#include "graphlayout.h"
+#include "inkscape.h"
 #include "macros.h"
+#include "preferences.h"
+#include "removeoverlap.h"
+#include "selection.h"
+#include "sp-flowtext.h"
 #include "sp-item-transform.h"
-#include "prefs-utils.h"
-#include "enums.h"
-
 #include "sp-text.h"
-#include "sp-flowtext.h"
 #include "text-editing.h"
-
-#include "node-context.h" //For node align/distribute function
-
 #include "tools-switch.h"
+#include "ui/icon-names.h"
+#include "ui/tool/node-tool.h"
+#include "ui/tool/multi-path-manipulator.h"
+#include "util/glib-list-iterators.h"
+#include "verbs.h"
+#include "widgets/icon.h"
 
 #include "align-and-distribute.h"
 
@@ -98,7 +90,7 @@ class ActionAlign : public Action {
 public :
     struct Coeffs {
        double mx0, mx1, my0, my1;
-       double sx0, sx1, sy0, sy1;
+       double sx0, sx1, sy0, sy1;
     };
     ActionAlign(const Glib::ustring &id,
                 const Glib::ustring &tiptext,
@@ -115,18 +107,21 @@ private :
 
     virtual void on_button_click() {
         //Retreive selected objects
-        SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+        SPDesktop *desktop = _dialog.getDesktop();
         if (!desktop) return;
 
         Inkscape::Selection *selection = sp_desktop_selection(desktop);
         if (!selection) return;
 
+        Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+        bool sel_as_group = prefs->getBool("/dialogs/align/sel-as-groups");
+
         using Inkscape::Util::GSListConstIterator;
         std::list<SPItem *> selected;
         selected.insert<GSListConstIterator<SPItem *> >(selected.end(), selection->itemList(), NULL);
         if (selected.empty()) return;
 
-        NR::Point mp; //Anchor point
+        Geom::Point mp; //Anchor point
         AlignAndDistribute::AlignTarget target = _dialog.getAlignTarget();
         const Coeffs &a= _allCoeffs[_index];
         switch (target)
@@ -150,33 +145,50 @@ private :
                 );
             //remove the master from the selection
             SPItem * thing = *master;
-            selected.erase(master);
+            // TODO: either uncomment or remove the following commented lines, depending on which
+            //       behaviour of moving objects makes most sense; also cf. discussion at
+            //       https://bugs.launchpad.net/inkscape/+bug/255933
+            /*if (!sel_as_group) { */
+                selected.erase(master);
+            /*}*/
             //Compute the anchor point
-            NR::Rect b = sp_item_bbox_desktop (thing);
-            mp = NR::Point(a.mx0 * b.min()[NR::X] + a.mx1 * b.max()[NR::X],
-                           a.my0 * b.min()[NR::Y] + a.my1 * b.max()[NR::Y]);
+            Geom::OptRect b = sp_item_bbox_desktop (thing);
+            if (b) {
+                mp = Geom::Point(a.mx0 * b->min()[Geom::X] + a.mx1 * b->max()[Geom::X],
+                               a.my0 * b->min()[Geom::Y] + a.my1 * b->max()[Geom::Y]);
+            } else {
+                return;
+            }
             break;
         }
 
         case AlignAndDistribute::PAGE:
-            mp = NR::Point(a.mx1 * sp_document_width(sp_desktop_document(desktop)),
+            mp = Geom::Point(a.mx1 * sp_document_width(sp_desktop_document(desktop)),
                            a.my1 * sp_document_height(sp_desktop_document(desktop)));
             break;
 
         case AlignAndDistribute::DRAWING:
         {
-            NR::Rect b = sp_item_bbox_desktop
+            Geom::OptRect b = sp_item_bbox_desktop
                 ( (SPItem *) sp_document_root (sp_desktop_document (desktop)) );
-            mp = NR::Point(a.mx0 * b.min()[NR::X] + a.mx1 * b.max()[NR::X],
-                           a.my0 * b.min()[NR::Y] + a.my1 * b.max()[NR::Y]);
+            if (b) {
+                mp = Geom::Point(a.mx0 * b->min()[Geom::X] + a.mx1 * b->max()[Geom::X],
+                               a.my0 * b->min()[Geom::Y] + a.my1 * b->max()[Geom::Y]);
+            } else {
+                return;
+            }
             break;
         }
 
         case AlignAndDistribute::SELECTION:
         {
-            NR::Rect b =  selection->bounds();
-            mp = NR::Point(a.mx0 * b.min()[NR::X] + a.mx1 * b.max()[NR::X],
-                           a.my0 * b.min()[NR::Y] + a.my1 * b.max()[NR::Y]);
+            Geom::OptRect b =  selection->bounds();
+            if (b) {
+                mp = Geom::Point(a.mx0 * b->min()[Geom::X] + a.mx1 * b->max()[Geom::X],
+                               a.my0 * b->min()[Geom::Y] + a.my1 * b->max()[Geom::Y]);
+            } else {
+                return;
+            }
             break;
         }
 
@@ -191,32 +203,39 @@ private :
         // a selected original, they will be unmoved too, possibly contrary to user's
         // expecation. However this is a minor point compared to making align/distribute always
         // work as expected, and "unmoved" is the default option anyway.
-        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);
+        int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
+        prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
 
         bool changed = false;
-        //Move each item in the selected list
+        Geom::OptRect b;
+        if (sel_as_group)
+            b = selection->bounds();
+
+        //Move each item in the selected list separately
         for (std::list<SPItem *>::iterator it(selected.begin());
              it != selected.end();
              it++)
         {
             sp_document_ensure_up_to_date(sp_desktop_document (desktop));
-            NR::Rect b = sp_item_bbox_desktop (*it);
-            NR::Point const sp(a.sx0 * b.min()[NR::X] + a.sx1 * b.max()[NR::X],
-                               a.sy0 * b.min()[NR::Y] + a.sy1 * b.max()[NR::Y]);
-            NR::Point const mp_rel( mp - sp );
-            if (LInfty(mp_rel) > 1e-9) {
-                sp_item_move_rel(*it, NR::translate(mp_rel));
-                changed = true;
+            if (!sel_as_group)
+                b = sp_item_bbox_desktop (*it);
+            if (b) {
+                Geom::Point const sp(a.sx0 * b->min()[Geom::X] + a.sx1 * b->max()[Geom::X],
+                                     a.sy0 * b->min()[Geom::Y] + a.sy1 * b->max()[Geom::Y]);
+                Geom::Point const mp_rel( mp - sp );
+                if (LInfty(mp_rel) > 1e-9) {
+                    sp_item_move_rel(*it, Geom::Translate(mp_rel));
+                    changed = true;
+                }
             }
         }
 
         // restore compensation setting
-        prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
+        prefs->setInt("/options/clonecompensation/value", saved_compensation);
 
         if (changed) {
             sp_document_done ( sp_desktop_document (desktop) , SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
-                               /* TODO: annotate */ "align-and-distribute.cpp:219" );
+                               _("Align"));
         }
 
 
@@ -240,24 +259,20 @@ ActionAlign::Coeffs const ActionAlign::_allCoeffs[10] = {
     {0., 0., 1., 0., 0., 0., 0., 1.}
 };
 
-struct BBoxSort
-{
-    SPItem *item;
-    float anchor;
-    NR::Rect bbox;
-    BBoxSort(SPItem *pItem, NR::Dim2 orientation, double kBegin, double kEnd) :
+BBoxSort::BBoxSort(SPItem *pItem, Geom::Rect bounds, Geom::Dim2 orientation, double kBegin, double kEnd) :
         item(pItem),
-        bbox (sp_item_bbox_desktop (pItem))
-    {
+        bbox (bounds)
+{
         anchor = kBegin * bbox.min()[orientation] + kEnd * bbox.max()[orientation];
-    }
-    BBoxSort(const BBoxSort &rhs):
+}
+BBoxSort::BBoxSort(const BBoxSort &rhs) :
         //NOTE :  this copy ctor is called O(sort) when sorting the vector
         //this is bad. The vector should be a vector of pointers.
         //But I'll wait the bohem GC before doing that
-        item(rhs.item), anchor(rhs.anchor), bbox(rhs.bbox) {
-    }
-};
+        item(rhs.item), anchor(rhs.anchor), bbox(rhs.bbox) 
+{
+}
+
 bool operator< (const BBoxSort &a, const BBoxSort &b)
 {
     return (a.anchor < b.anchor);
@@ -270,7 +285,7 @@ public :
                      guint row, guint column,
                      AlignAndDistribute &dialog,
                      bool onInterSpace,
-                     NR::Dim2 orientation,
+                     Geom::Dim2 orientation,
                      double kBegin, double kEnd
         ):
         Action(id, tiptext, row, column,
@@ -285,7 +300,7 @@ public :
 private :
     virtual void on_button_click() {
         //Retreive selected objects
-        SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+        SPDesktop *desktop = _dialog.getDesktop();
         if (!desktop) return;
 
         Inkscape::Selection *selection = sp_desktop_selection(desktop);
@@ -307,15 +322,18 @@ private :
             it != selected.end();
             ++it)
         {
-            BBoxSort b (*it, _orientation, _kBegin, _kEnd);
-            sorted.push_back(b);
+            Geom::OptRect bbox = sp_item_bbox_desktop(*it);
+            if (bbox) {
+                sorted.push_back(BBoxSort(*it, *bbox, _orientation, _kBegin, _kEnd));
+            }
         }
         //sort bbox by anchors
         std::sort(sorted.begin(), sorted.end());
 
         // see comment in ActionAlign above
-        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);
 
         unsigned int len = sorted.size();
         bool changed = false;
@@ -328,7 +346,7 @@ private :
             float span = 0;
             for (unsigned int i = 0; i < len; i++)
             {
-                span += sorted[i].bbox.extent(_orientation);
+                span += sorted[i].bbox[_orientation].extent();
             }
             //new distance between each bbox
             float step = (dist - span) / (len - 1);
@@ -338,12 +356,12 @@ private :
                   it ++ )
             {
                 if (!NR_DF_TEST_CLOSE (pos, it->bbox.min()[_orientation], 1e-6)) {
-                    NR::Point t(0.0, 0.0);
+                    Geom::Point t(0.0, 0.0);
                     t[_orientation] = pos - it->bbox.min()[_orientation];
-                    sp_item_move_rel(it->item, NR::translate(t));
+                    sp_item_move_rel(it->item, Geom::Translate(t));
                     changed = true;
                 }
-                pos += it->bbox.extent(_orientation);
+                pos += it->bbox[_orientation].extent();
                 pos += step;
             }
         }
@@ -362,27 +380,27 @@ private :
                 //Don't move if we are really close
                 if (!NR_DF_TEST_CLOSE (pos, it.anchor, 1e-6)) {
                     //Compute translation
-                    NR::Point t(0.0, 0.0);
+                    Geom::Point t(0.0, 0.0);
                     t[_orientation] = pos - it.anchor;
                     //translate
-                    sp_item_move_rel(it.item, NR::translate(t));
+                    sp_item_move_rel(it.item, Geom::Translate(t));
                     changed = true;
                 }
             }
         }
 
         // restore compensation setting
-        prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
+        prefs->setInt("/options/clonecompensation/value", saved_compensation);
 
         if (changed) {
-            sp_document_done ( sp_desktop_document (desktop), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, 
-                               /* TODO: annotate */ "align-and-distribute.cpp:379");
+            sp_document_done ( sp_desktop_document (desktop), SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
+                               _("Distribute"));
         }
     }
     guint _index;
     AlignAndDistribute &_dialog;
     bool _onInterSpace;
-    NR::Dim2 _orientation;
+    Geom::Dim2 _orientation;
 
     double _kBegin;
     double _kEnd;
@@ -396,7 +414,7 @@ public :
                const Glib::ustring &tiptext,
                guint column,
                AlignAndDistribute &dialog,
-               NR::Dim2 orientation, bool distribute):
+               Geom::Dim2 orientation, bool distribute):
         Action(id, tiptext, 0, column,
                dialog.nodes_table(), dialog.tooltips(), dialog),
         _orientation(orientation),
@@ -404,21 +422,20 @@ public :
     {}
 
 private :
-    NR::Dim2 _orientation;
+    Geom::Dim2 _orientation;
     bool _distribute;
     virtual void on_button_click()
     {
 
-        if (!SP_ACTIVE_DESKTOP) return;
-       SPEventContext *event_context = sp_desktop_event_context(SP_ACTIVE_DESKTOP);
-       if (!SP_IS_NODE_CONTEXT (event_context)) return ;
+        if (!_dialog.getDesktop()) return;
+        SPEventContext *event_context = sp_desktop_event_context(_dialog.getDesktop());
+        if (!INK_IS_NODE_TOOL (event_context)) return;
+        InkNodeTool *nt = INK_NODE_TOOL(event_context);
 
-        Inkscape::NodePath::Path *nodepath = SP_NODE_CONTEXT (event_context)->nodepath;
-        if (!nodepath) return;
         if (_distribute)
-            sp_nodepath_selected_distribute(nodepath, _orientation);
+            nt->_multipath->distributeNodes(_orientation);
         else
-            sp_nodepath_selected_align(nodepath, _orientation);
+            nt->_multipath->alignNodes(_orientation);
 
     }
 };
@@ -440,26 +457,26 @@ public:
                dialog.removeOverlap_table(), dialog.tooltips(), dialog)
     {
         dialog.removeOverlap_table().set_col_spacings(3);
-    
+
         removeOverlapXGap.set_digits(1);
         removeOverlapXGap.set_size_request(60, -1);
-        removeOverlapXGap.set_increments(1.0, 5.0);
+        removeOverlapXGap.set_increments(1.0, 0);
         removeOverlapXGap.set_range(-1000.0, 1000.0);
         removeOverlapXGap.set_value(0);
         dialog.tooltips().set_tip(removeOverlapXGap,
                                   _("Minimum horizontal gap (in px units) between bounding boxes"));
-        /* TRANSLATORS: Horizontal gap */
-        removeOverlapXGapLabel.set_label(_("H:"));
+        //TRANSLATORS: "H:" stands for horizontal gap
+        removeOverlapXGapLabel.set_label(C_("Gap", "H:"));
 
         removeOverlapYGap.set_digits(1);
         removeOverlapYGap.set_size_request(60, -1);
-        removeOverlapYGap.set_increments(1.0, 5.0);
+        removeOverlapYGap.set_increments(1.0, 0);
         removeOverlapYGap.set_range(-1000.0, 1000.0);
         removeOverlapYGap.set_value(0);
         dialog.tooltips().set_tip(removeOverlapYGap,
                                   _("Minimum vertical gap (in px units) between bounding boxes"));
         /* TRANSLATORS: Vertical gap */
-        removeOverlapYGapLabel.set_label(_("V:"));
+        removeOverlapYGapLabel.set_label(C_("Gap", "V:"));
 
         dialog.removeOverlap_table().attach(removeOverlapXGapLabel, column, column+1, row, row+1, Gtk::FILL, Gtk::FILL);
         dialog.removeOverlap_table().attach(removeOverlapXGap, column+1, column+2, row, row+1, Gtk::FILL, Gtk::FILL);
@@ -471,23 +488,24 @@ public:
 private :
     virtual void on_button_click()
     {
-        if (!SP_ACTIVE_DESKTOP) return;
+        if (!_dialog.getDesktop()) return;
 
         // see comment in ActionAlign above
-        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);
 
         // xGap and yGap are the minimum space required between bounding rectangles.
         double const xGap = removeOverlapXGap.get_value();
         double const yGap = removeOverlapYGap.get_value();
-        removeoverlap(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList(),
+        removeoverlap(sp_desktop_selection(_dialog.getDesktop())->itemList(),
                       xGap, yGap);
 
         // restore compensation setting
-        prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
+        prefs->setInt("/options/clonecompensation/value", saved_compensation);
 
-        sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, 
-                         /* TODO: annotate */ "align-and-distribute.cpp:490");
+        sp_document_done(sp_desktop_document(_dialog.getDesktop()), SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
+                         _("Remove overlaps"));
     }
 };
 
@@ -498,28 +516,124 @@ public:
                          guint row,
                          guint column,
                          AlignAndDistribute &dialog) :
-        Action(id, tiptext, row, column + 4,
-               dialog.graphLayout_table(), dialog.tooltips(), dialog)
+        Action(id, tiptext, row, column,
+               dialog.rearrange_table(), dialog.tooltips(), dialog)
     {}
 
 private :
     virtual void on_button_click()
     {
-        if (!SP_ACTIVE_DESKTOP) return;
+        if (!_dialog.getDesktop()) return;
 
         // see comment in ActionAlign above
-        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);
+
+        graphlayout(sp_desktop_selection(_dialog.getDesktop())->itemList());
+
+        // restore compensation setting
+        prefs->setInt("/options/clonecompensation/value", saved_compensation);
+
+        sp_document_done(sp_desktop_document(_dialog.getDesktop()), SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
+                         _("Arrange connector network"));
+    }
+};
+
+class ActionExchangePositions : public Action {
+public:
+    enum SortOrder {
+       None,
+       ZOrder,
+       Clockwise
+    };     
+
+    ActionExchangePositions(Glib::ustring const &id,
+                         Glib::ustring const &tiptext,
+                         guint row,
+                         guint column,
+                         AlignAndDistribute &dialog, SortOrder order = None) :
+        Action(id, tiptext, row, column,
+               dialog.rearrange_table(), dialog.tooltips(), dialog),
+        sortOrder(order)
+    {};
+
 
-        graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
+private :
+    const SortOrder sortOrder;
+    static boost::optional<Geom::Point> center;
+
+    static bool sort_compare(const SPItem * a,const SPItem * b) {
+        if (a == NULL) return false;
+        if (b == NULL) return true;
+        if (center) {
+            Geom::Point point_a = a->getCenter() - (*center);
+            Geom::Point point_b = b->getCenter() - (*center);
+            // First criteria: Sort according to the angle to the center point
+            double angle_a = atan2(double(point_a[Geom::Y]), double(point_a[Geom::X]));
+            double angle_b = atan2(double(point_b[Geom::Y]), double(point_b[Geom::X]));
+            if (angle_a != angle_b) return (angle_a < angle_b);
+            // Second criteria: Sort according to the distance the center point
+            Geom::Coord length_a = point_a.length();
+            Geom::Coord length_b = point_b.length();
+            if (length_a != length_b) return (length_a > length_b);
+        }
+        // Last criteria: Sort according to the z-coordinate
+        return (a->isSiblingOf(b));
+    }
+
+    virtual void on_button_click()
+    {
+        SPDesktop *desktop = _dialog.getDesktop();
+        if (!desktop) return;
+
+        Inkscape::Selection *selection = sp_desktop_selection(desktop);
+        if (!selection) return;
+
+        using Inkscape::Util::GSListConstIterator;
+        std::list<SPItem *> selected;
+        selected.insert<GSListConstIterator<SPItem *> >(selected.end(), selection->itemList(), NULL);
+        if (selected.empty()) return;
+
+        //Check 2 or more selected objects
+        if (selected.size() < 2) return;
+
+        // see comment in ActionAlign above
+        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);
+
+        // sort the list
+       if (sortOrder != None) {
+               if (sortOrder == Clockwise) {
+                       center = selection->center();
+               } else { // sorting by ZOrder is outomatically done by not setting the center
+                       center.reset();
+               }
+               selected.sort(ActionExchangePositions::sort_compare);
+       }
+       std::list<SPItem *>::iterator it(selected.begin());
+       Geom::Point p1 =  (*it)->getCenter();
+       for (++it ;it != selected.end(); ++it)
+       {
+               Geom::Point p2 = (*it)->getCenter();
+               Geom::Point delta = p1 - p2;
+               sp_item_move_rel((*it),Geom::Translate(delta[Geom::X],delta[Geom::Y] ));
+               p1 = p2;
+       }
+       Geom::Point p2 = selected.front()->getCenter();
+       Geom::Point delta = p1 - p2;
+       sp_item_move_rel(selected.front(),Geom::Translate(delta[Geom::X],delta[Geom::Y] ));
 
         // restore compensation setting
-        prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
+        prefs->setInt("/options/clonecompensation/value", saved_compensation);
 
-        sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, 
-                         /* TODO: annotate */ "align-and-distribute.cpp:520");
+       sp_document_done(sp_desktop_document(_dialog.getDesktop()), SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
+                        _("Exchange Positions"));
     }
 };
+// instantiae the private static member
+boost::optional<Geom::Point> ActionExchangePositions::center;
 
 class ActionUnclump : public Action {
 public :
@@ -529,25 +643,26 @@ public :
                guint column,
                AlignAndDistribute &dialog):
         Action(id, tiptext, row, column,
-               dialog.distribute_table(), dialog.tooltips(), dialog)
+               dialog.rearrange_table(), dialog.tooltips(), dialog)
     {}
 
 private :
     virtual void on_button_click()
     {
-        if (!SP_ACTIVE_DESKTOP) return;
+        if (!_dialog.getDesktop()) return;
 
         // see comment in ActionAlign above
-        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);
 
-        unclump ((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
+        unclump ((GSList *) sp_desktop_selection(_dialog.getDesktop())->itemList());
 
         // restore compensation setting
-        prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
+        prefs->setInt("/options/clonecompensation/value", saved_compensation);
 
-        sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, 
-                          /* TODO: annotate */ "align-and-distribute.cpp:550");
+        sp_document_done (sp_desktop_document (_dialog.getDesktop()), SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
+                          _("Unclump"));
     }
 };
 
@@ -559,13 +674,13 @@ public :
                guint column,
                AlignAndDistribute &dialog):
         Action(id, tiptext, row, column,
-               dialog.distribute_table(), dialog.tooltips(), dialog)
+               dialog.rearrange_table(), dialog.tooltips(), dialog)
     {}
 
 private :
     virtual void on_button_click()
     {
-        SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+        SPDesktop *desktop = _dialog.getDesktop();
         if (!desktop) return;
 
         Inkscape::Selection *selection = sp_desktop_selection(desktop);
@@ -579,48 +694,55 @@ private :
         //Check 2 or more selected objects
         if (selected.size() < 2) return;
 
+        Geom::OptRect sel_bbox = selection->bounds();
+        if (!sel_bbox) {
+            return;
+        }
+
         // This bbox is cached between calls to randomize, so that there's no growth nor shrink
         // nor drift on sequential randomizations. Discard cache on global (or better active
         // desktop's) selection_change signal.
-        if (!_dialog.randomize_bbox_set) {
-            _dialog.randomize_bbox = selection->bounds();
-            _dialog.randomize_bbox_set = true;
+        if (!_dialog.randomize_bbox) {
+            _dialog.randomize_bbox = *sel_bbox;
         }
 
         // see comment in ActionAlign above
-        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);
 
         for (std::list<SPItem *>::iterator it(selected.begin());
             it != selected.end();
             ++it)
         {
             sp_document_ensure_up_to_date(sp_desktop_document (desktop));
-            NR::Rect item_box = sp_item_bbox_desktop (*it);
-            // find new center, staying within bbox 
-            double x = _dialog.randomize_bbox.min()[NR::X] + item_box.extent(NR::X)/2 +
-                g_random_double_range (0, _dialog.randomize_bbox.extent(NR::X) - item_box.extent(NR::X));
-            double y = _dialog.randomize_bbox.min()[NR::Y] + item_box.extent(NR::Y)/2 +
-                g_random_double_range (0, _dialog.randomize_bbox.extent(NR::Y) - item_box.extent(NR::Y));
-            // displacement is the new center minus old:
-            NR::Point t = NR::Point (x, y) - 0.5*(item_box.max() + item_box.min());
-            sp_item_move_rel(*it, NR::translate(t));
+            Geom::OptRect item_box = sp_item_bbox_desktop (*it);
+            if (item_box) {
+                // find new center, staying within bbox
+                double x = _dialog.randomize_bbox->min()[Geom::X] + (*item_box)[Geom::X].extent() /2 +
+                    g_random_double_range (0, (*_dialog.randomize_bbox)[Geom::X].extent() - (*item_box)[Geom::X].extent());
+                double y = _dialog.randomize_bbox->min()[Geom::Y] + (*item_box)[Geom::Y].extent()/2 +
+                    g_random_double_range (0, (*_dialog.randomize_bbox)[Geom::Y].extent() - (*item_box)[Geom::Y].extent());
+                // displacement is the new center minus old:
+                Geom::Point t = Geom::Point (x, y) - 0.5*(item_box->max() + item_box->min());
+                sp_item_move_rel(*it, Geom::Translate(t));
+            }
         }
 
         // restore compensation setting
-        prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
+        prefs->setInt("/options/clonecompensation/value", saved_compensation);
 
-        sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, 
-                          /* TODO: annotate */ "align-and-distribute.cpp:614");
+        sp_document_done (sp_desktop_document (desktop), SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
+                          _("Randomize positions"));
     }
 };
 
 struct Baselines
 {
     SPItem *_item;
-    NR::Point _base;
-    NR::Dim2 _orientation;
-    Baselines(SPItem *item, NR::Point base, NR::Dim2 orientation) :
+    Geom::Point _base;
+    Geom::Dim2 _orientation;
+    Baselines(SPItem *item, Geom::Point base, Geom::Dim2 orientation) :
         _item (item),
         _base (base),
         _orientation (orientation)
@@ -640,7 +762,7 @@ public :
                guint column,
                AlignAndDistribute &dialog,
                Gtk::Table &table,
-               NR::Dim2 orientation, bool distribute):
+               Geom::Dim2 orientation, bool distribute):
         Action(id, tiptext, row, column,
                table, dialog.tooltips(), dialog),
         _orientation(orientation),
@@ -648,11 +770,11 @@ public :
     {}
 
 private :
-    NR::Dim2 _orientation;
+    Geom::Dim2 _orientation;
     bool _distribute;
     virtual void on_button_click()
     {
-        SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+        SPDesktop *desktop = _dialog.getDesktop();
         if (!desktop) return;
 
         Inkscape::Selection *selection = sp_desktop_selection(desktop);
@@ -666,8 +788,8 @@ private :
         //Check 2 or more selected objects
         if (selected.size() < 2) return;
 
-        NR::Point b_min = NR::Point (HUGE_VAL, HUGE_VAL);
-        NR::Point b_max = NR::Point (-HUGE_VAL, -HUGE_VAL);
+        Geom::Point b_min = Geom::Point (HUGE_VAL, HUGE_VAL);
+        Geom::Point b_max = Geom::Point (-HUGE_VAL, -HUGE_VAL);
 
         std::vector<Baselines> sorted;
 
@@ -677,14 +799,16 @@ private :
         {
             if (SP_IS_TEXT (*it) || SP_IS_FLOWTEXT (*it)) {
                 Inkscape::Text::Layout const *layout = te_get_layout(*it);
-                NR::Point base = layout->characterAnchorPoint(layout->begin()) * sp_item_i2d_affine(*it);
-                if (base[NR::X] < b_min[NR::X]) b_min[NR::X] = base[NR::X];
-                if (base[NR::Y] < b_min[NR::Y]) b_min[NR::Y] = base[NR::Y];
-                if (base[NR::X] > b_max[NR::X]) b_max[NR::X] = base[NR::X];
-                if (base[NR::Y] > b_max[NR::Y]) b_max[NR::Y] = base[NR::Y];
-
-                Baselines b (*it, base, _orientation);
-                sorted.push_back(b);
+                boost::optional<Geom::Point> pt = layout->baselineAnchorPoint();
+                if (pt) {
+                    Geom::Point base = *pt * sp_item_i2d_affine(*it);
+                    if (base[Geom::X] < b_min[Geom::X]) b_min[Geom::X] = base[Geom::X];
+                    if (base[Geom::Y] < b_min[Geom::Y]) b_min[Geom::Y] = base[Geom::Y];
+                    if (base[Geom::X] > b_max[Geom::X]) b_max[Geom::X] = base[Geom::X];
+                    if (base[Geom::Y] > b_max[Geom::Y]) b_max[Geom::Y] = base[Geom::Y];
+                    Baselines b (*it, base, _orientation);
+                    sorted.push_back(b);
+                }
             }
         }
 
@@ -699,13 +823,18 @@ private :
             double step = (b_max[_orientation] - b_min[_orientation])/(sorted.size() - 1);
             for (unsigned int i = 0; i < sorted.size(); i++) {
                 SPItem *item = sorted[i]._item;
-                NR::Point base = sorted[i]._base;
-                NR::Point t(0.0, 0.0);
+                Geom::Point base = sorted[i]._base;
+                Geom::Point t(0.0, 0.0);
                 t[_orientation] = b_min[_orientation] + step * i - base[_orientation];
-                sp_item_move_rel(item, NR::translate(t));
+                sp_item_move_rel(item, Geom::Translate(t));
                 changed = true;
             }
 
+            if (changed) {
+                sp_document_done (sp_desktop_document (desktop), SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
+                                  _("Distribute text baselines"));
+            }
+
         } else {
             for (std::list<SPItem *>::iterator it(selected.begin());
                  it != selected.end();
@@ -713,34 +842,37 @@ private :
             {
                 if (SP_IS_TEXT (*it) || SP_IS_FLOWTEXT (*it)) {
                     Inkscape::Text::Layout const *layout = te_get_layout(*it);
-                    NR::Point base = layout->characterAnchorPoint(layout->begin()) * sp_item_i2d_affine(*it);
-                    NR::Point t(0.0, 0.0);
-                    t[_orientation] = b_min[_orientation] - base[_orientation];
-                    sp_item_move_rel(*it, NR::translate(t));
-                    changed = true;
+                    boost::optional<Geom::Point> pt = layout->baselineAnchorPoint();
+                    if (pt) {
+                        Geom::Point base = *pt * sp_item_i2d_affine(*it);
+                        Geom::Point t(0.0, 0.0);
+                        t[_orientation] = b_min[_orientation] - base[_orientation];
+                        sp_item_move_rel(*it, Geom::Translate(t));
+                        changed = true;
+                    }
                 }
             }
-        }
 
-        if (changed) {
-            sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, 
-                              /* TODO: annotate */ "align-and-distribute.cpp:727");
+            if (changed) {
+                sp_document_done (sp_desktop_document (desktop), SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
+                                  _("Align text baselines"));
+            }
         }
     }
 };
 
 
 
-void on_tool_changed(Inkscape::Application *inkscape, SPEventContext *context, AlignAndDistribute *daad)
+void on_tool_changed(Inkscape::Application */*inkscape*/, SPEventContext */*context*/, AlignAndDistribute *daad)
 {
     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (desktop && sp_desktop_event_context(desktop))
         daad->setMode(tools_active(desktop) == TOOLS_NODES);
 }
 
-void on_selection_changed(Inkscape::Application *inkscape, Inkscape::Selection *selection, AlignAndDistribute *daad)
+void on_selection_changed(Inkscape::Application */*inkscape*/, Inkscape::Selection */*selection*/, AlignAndDistribute *daad)
 {
-    daad->randomize_bbox_set = false;
+    daad->randomize_bbox = Geom::OptRect();
 }
 
 /////////////////////////////////////////////////////////
@@ -748,181 +880,201 @@ void on_selection_changed(Inkscape::Application *inkscape, Inkscape::Selection *
 
 
 
-AlignAndDistribute::AlignAndDistribute() 
-    : Dialog ("dialogs.align", SP_VERB_DIALOG_ALIGN_DISTRIBUTE),
-      randomize_bbox (NR::Point (0, 0), NR::Point (0, 0)),
+AlignAndDistribute::AlignAndDistribute()
+    : UI::Widget::Panel ("", "/dialogs/align", SP_VERB_DIALOG_ALIGN_DISTRIBUTE),
+      randomize_bbox(),
       _alignFrame(_("Align")),
       _distributeFrame(_("Distribute")),
+      _rearrangeFrame(_("Rearrange")),
       _removeOverlapFrame(_("Remove overlaps")),
-      _graphLayoutFrame(_("Connector network layout")),
       _nodesFrame(_("Nodes")),
       _alignTable(2, 6, true),
-      _distributeTable(3, 6, true),
+      _distributeTable(2, 6, true),
+      _rearrangeTable(1, 5, false),
       _removeOverlapTable(1, 5, false),
-      _graphLayoutTable(1, 5, false),
       _nodesTable(1, 4, true),
-      _anchorLabel(_("Relative to: "))
+      _anchorLabel(_("Relative to: ")),
+      _selgrpLabel(_("Treat selection as group: "))
 {
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
 
     //Instanciate the align buttons
-    addAlignButton("al_left_out",
-                   _("Align right sides of objects to left side of anchor"),
+    addAlignButton(INKSCAPE_ICON_ALIGN_HORIZONTAL_RIGHT_TO_ANCHOR,
+                   _("Align right edges of objects to the left edge of the anchor"),
                    0, 0);
-    addAlignButton("al_left_in",
-                   _("Align left sides"),
+    addAlignButton(INKSCAPE_ICON_ALIGN_HORIZONTAL_LEFT,
+                   _("Align left edges"),
                    0, 1);
-    addAlignButton("al_center_hor",
+    addAlignButton(INKSCAPE_ICON_ALIGN_HORIZONTAL_CENTER,
                    _("Center on vertical axis"),
                    0, 2);
-    addAlignButton("al_right_in",
+    addAlignButton(INKSCAPE_ICON_ALIGN_HORIZONTAL_RIGHT,
                    _("Align right sides"),
                    0, 3);
-    addAlignButton("al_right_out",
-                   _("Align left sides of objects to right side of anchor"),
+    addAlignButton(INKSCAPE_ICON_ALIGN_HORIZONTAL_LEFT_TO_ANCHOR,
+                   _("Align left edges of objects to the right edge of the anchor"),
                    0, 4);
-    addAlignButton("al_top_out",
-                   _("Align bottoms of objects to top of anchor"),
+    addAlignButton(INKSCAPE_ICON_ALIGN_VERTICAL_BOTTOM_TO_ANCHOR,
+                   _("Align bottom edges of objects to the top edge of the anchor"),
                    1, 0);
-    addAlignButton("al_top_in",
-                   _("Align tops"),
+    addAlignButton(INKSCAPE_ICON_ALIGN_VERTICAL_TOP,
+                   _("Align top edges"),
                    1, 1);
-    addAlignButton("al_center_ver",
+    addAlignButton(INKSCAPE_ICON_ALIGN_VERTICAL_CENTER,
                    _("Center on horizontal axis"),
                    1, 2);
-    addAlignButton("al_bottom_in",
-                   _("Align bottoms"),
+    addAlignButton(INKSCAPE_ICON_ALIGN_VERTICAL_BOTTOM,
+                   _("Align bottom edges"),
                    1, 3);
-    addAlignButton("al_bottom_out",
-                   _("Align tops of objects to bottom of anchor"),
+    addAlignButton(INKSCAPE_ICON_ALIGN_VERTICAL_TOP_TO_ANCHOR,
+                   _("Align top edges of objects to the bottom edge of the anchor"),
                    1, 4);
 
     //Baseline aligns
-    addBaselineButton("al_baselines_vert",
-                   _("Align baseline anchors of texts vertically"),
-                      0, 5, this->align_table(), NR::X, false);
-    addBaselineButton("al_baselines_hor",
+    addBaselineButton(INKSCAPE_ICON_ALIGN_HORIZONTAL_BASELINE,
                    _("Align baseline anchors of texts horizontally"),
-                     1, 5, this->align_table(), NR::Y, false);
+                      0, 5, this->align_table(), Geom::X, false);
+    addBaselineButton(INKSCAPE_ICON_ALIGN_VERTICAL_BASELINE,
+                   _("Align baselines of texts"),
+                     1, 5, this->align_table(), Geom::Y, false);
 
     //The distribute buttons
-    addDistributeButton("distribute_hdist",
+    addDistributeButton(INKSCAPE_ICON_DISTRIBUTE_HORIZONTAL_GAPS,
                         _("Make horizontal gaps between objects equal"),
-                        0, 4, true, NR::X, .5, .5);
+                        0, 4, true, Geom::X, .5, .5);
 
-    addDistributeButton("distribute_left",
-                        _("Distribute left sides equidistantly"),
-                        0, 1, false, NR::X, 1., 0.);
-    addDistributeButton("distribute_hcentre",
+    addDistributeButton(INKSCAPE_ICON_DISTRIBUTE_HORIZONTAL_LEFT,
+                        _("Distribute left edges equidistantly"),
+                        0, 1, false, Geom::X, 1., 0.);
+    addDistributeButton(INKSCAPE_ICON_DISTRIBUTE_HORIZONTAL_CENTER,
                         _("Distribute centers equidistantly horizontally"),
-                        0, 2, false, NR::X, .5, .5);
-    addDistributeButton("distribute_right",
-                        _("Distribute right sides equidistantly"),
-                        0, 3, false, NR::X, 0., 1.);
+                        0, 2, false, Geom::X, .5, .5);
+    addDistributeButton(INKSCAPE_ICON_DISTRIBUTE_HORIZONTAL_RIGHT,
+                        _("Distribute right edges equidistantly"),
+                        0, 3, false, Geom::X, 0., 1.);
 
-    addDistributeButton("distribute_vdist",
+    addDistributeButton(INKSCAPE_ICON_DISTRIBUTE_VERTICAL_GAPS,
                         _("Make vertical gaps between objects equal"),
-                        1, 4, true, NR::Y, .5, .5);
+                        1, 4, true, Geom::Y, .5, .5);
 
-    addDistributeButton("distribute_top",
-                        _("Distribute tops equidistantly"),
-                        1, 1, false, NR::Y, 0, 1);
-    addDistributeButton("distribute_vcentre",
+    addDistributeButton(INKSCAPE_ICON_DISTRIBUTE_VERTICAL_TOP,
+                        _("Distribute top edges equidistantly"),
+                        1, 1, false, Geom::Y, 0, 1);
+    addDistributeButton(INKSCAPE_ICON_DISTRIBUTE_VERTICAL_CENTER,
                         _("Distribute centers equidistantly vertically"),
-                        1, 2, false, NR::Y, .5, .5);
-    addDistributeButton("distribute_bottom",
-                        _("Distribute bottoms equidistantly"),
-                        1, 3, false, NR::Y, 1., 0.);
+                        1, 2, false, Geom::Y, .5, .5);
+    addDistributeButton(INKSCAPE_ICON_DISTRIBUTE_VERTICAL_BOTTOM,
+                        _("Distribute bottom edges equidistantly"),
+                        1, 3, false, Geom::Y, 1., 0.);
 
     //Baseline distribs
-    addBaselineButton("distribute_baselines_hor",
+    addBaselineButton(INKSCAPE_ICON_DISTRIBUTE_HORIZONTAL_BASELINE,
                    _("Distribute baseline anchors of texts horizontally"),
-                      0, 5, this->distribute_table(), NR::X, true);
-    addBaselineButton("distribute_baselines_vert",
-                   _("Distribute baseline anchors of texts vertically"),
-                     1, 5, this->distribute_table(), NR::Y, true);
+                      0, 5, this->distribute_table(), Geom::X, true);
+    addBaselineButton(INKSCAPE_ICON_DISTRIBUTE_VERTICAL_BASELINE,
+                   _("Distribute baselines of texts vertically"),
+                     1, 5, this->distribute_table(), Geom::Y, true);
 
+    // Rearrange
+    //Graph Layout
+    addGraphLayoutButton(INKSCAPE_ICON_DISTRIBUTE_GRAPH,
+                            _("Nicely arrange selected connector network"),
+                            0, 0);
+    addExchangePositionsButton(INKSCAPE_ICON_EXCHANGE_POSITIONS,
+                            _("Exchange positions of selected objects - selection order"),
+                            0, 1);
+    addExchangePositionsByZOrderButton(INKSCAPE_ICON_EXCHANGE_POSITIONS_ZORDER,
+                            _("Exchange positions of selected objects - stacking order"),
+                            0, 2);
+    addExchangePositionsClockwiseButton(INKSCAPE_ICON_EXCHANGE_POSITIONS_CLOCKWISE,
+                            _("Exchange positions of selected objects - clockwise rotate"),
+                            0, 3);
+                           
     //Randomize & Unclump
-    addRandomizeButton("distribute_randomize",
+    addRandomizeButton(INKSCAPE_ICON_DISTRIBUTE_RANDOMIZE,
                         _("Randomize centers in both dimensions"),
-                        2, 2);
-    addUnclumpButton("unclump",
+                        0, 4);
+    addUnclumpButton(INKSCAPE_ICON_DISTRIBUTE_UNCLUMP,
                         _("Unclump objects: try to equalize edge-to-edge distances"),
-                        2, 4);
+                        0, 5);
 
     //Remove overlaps
-    addRemoveOverlapsButton("remove_overlaps",
+    addRemoveOverlapsButton(INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
                             _("Move objects as little as possible so that their bounding boxes do not overlap"),
                             0, 0);
-    //Graph Layout
-    addGraphLayoutButton("graph_layout",
-                            _("Nicely arrange selected connector network"),
-                            0, 0);
 
     //Node Mode buttons
-    addNodeButton("node_halign",
-                  _("Align selected nodes horizontally"),
-                  0, NR::X, false);
-    addNodeButton("node_valign",
-                  _("Align selected nodes vertically"),
-                  1, NR::Y, false);
-    addNodeButton("node_hdistribute",
+    // NOTE: "align nodes vertically" means "move nodes vertically until they align on a common
+    // _horizontal_ line". This is analogous to what the "align-vertical-center" icon means.
+    // There is no doubt some ambiguity. For this reason the descriptions are different.
+    addNodeButton(INKSCAPE_ICON_ALIGN_VERTICAL_NODES,
+                  _("Align selected nodes to a common horizontal line"),
+                  0, Geom::X, false);
+    addNodeButton(INKSCAPE_ICON_ALIGN_HORIZONTAL_NODES,
+                  _("Align selected nodes to a common vertical line"),
+                  1, Geom::Y, false);
+    addNodeButton(INKSCAPE_ICON_DISTRIBUTE_HORIZONTAL_NODE,
                   _("Distribute selected nodes horizontally"),
-                  2, NR::X, true);
-    addNodeButton("node_vdistribute",
+                  2, Geom::X, true);
+    addNodeButton(INKSCAPE_ICON_DISTRIBUTE_VERTICAL_NODE,
                   _("Distribute selected nodes vertically"),
-                  3, NR::Y, true);
+                  3, Geom::Y, true);
 
     //Rest of the widgetry
 
     _combo.append_text(_("Last selected"));
     _combo.append_text(_("First selected"));
-    _combo.append_text(_("Biggest item"));
-    _combo.append_text(_("Smallest item"));
+    _combo.append_text(_("Biggest object"));
+    _combo.append_text(_("Smallest object"));
     _combo.append_text(_("Page"));
     _combo.append_text(_("Drawing"));
     _combo.append_text(_("Selection"));
 
-    _combo.set_active(6);
+    _combo.set_active(prefs->getInt("/dialogs/align/align-to", 6));
     _combo.signal_changed().connect(sigc::mem_fun(*this, &AlignAndDistribute::on_ref_change));
 
     _anchorBox.pack_start(_anchorLabel);
     _anchorBox.pack_start(_combo);
 
+    _selgrpBox.pack_start(_selgrpLabel);
+    _selgrpBox.pack_start(_selgrp);
+    _selgrp.set_active(prefs->getBool("/dialogs/align/sel-as-groups"));
+    _selgrp.signal_toggled().connect(sigc::mem_fun(*this, &AlignAndDistribute::on_selgrp_toggled));
+
     _alignBox.pack_start(_anchorBox);
+    _alignBox.pack_start(_selgrpBox);
     _alignBox.pack_start(_alignTable);
 
     _alignFrame.add(_alignBox);
     _distributeFrame.add(_distributeTable);
+    _rearrangeFrame.add(_rearrangeTable);
     _removeOverlapFrame.add(_removeOverlapTable);
-    _graphLayoutFrame.add(_graphLayoutTable);
     _nodesFrame.add(_nodesTable);
 
-    // Top level vbox
-    Gtk::VBox *vbox = get_vbox();
-    vbox->set_spacing(4);
+    Gtk::Box *contents = _getContents();
+    contents->set_spacing(4);
 
     // Notebook for individual transformations
 
-    vbox->pack_start(_alignFrame, true, true);
-    vbox->pack_start(_distributeFrame, true, true);
-    vbox->pack_start(_removeOverlapFrame, true, true);
-    vbox->pack_start(_graphLayoutFrame, true, true);
-    vbox->pack_start(_nodesFrame, true, true);
+    contents->pack_start(_alignFrame, true, true);
+    contents->pack_start(_distributeFrame, true, true);
+    contents->pack_start(_rearrangeFrame, true, true);
+    contents->pack_start(_removeOverlapFrame, true, true);
+    contents->pack_start(_nodesFrame, true, true);
 
     //Connect to the global tool change signal
     g_signal_connect (G_OBJECT (INKSCAPE), "set_eventcontext", G_CALLBACK (on_tool_changed), this);
 
     // Connect to the global selection change, to invalidate cached randomize_bbox
     g_signal_connect (G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (on_selection_changed), this);
-    randomize_bbox = NR::Rect (NR::Point (0, 0), NR::Point (0, 0));
-    randomize_bbox_set = false;
+    randomize_bbox = Geom::OptRect();
 
     show_all_children();
 
     on_tool_changed (NULL, NULL, this); // set current mode
 }
 
-AlignAndDistribute::~AlignAndDistribute() 
+AlignAndDistribute::~AlignAndDistribute()
 {
     sp_signal_disconnect_by_data (G_OBJECT (INKSCAPE), this);
 
@@ -933,7 +1085,17 @@ AlignAndDistribute::~AlignAndDistribute()
 }
 
 void AlignAndDistribute::on_ref_change(){
-//Make blink the master
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+    prefs->setInt("/dialogs/align/align-to", _combo.get_active_row_number());
+
+    //Make blink the master
+}
+
+void AlignAndDistribute::on_selgrp_toggled(){
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+    prefs->setInt("/dialogs/align/sel-as-groups", _selgrp.get_active());
+
+    //Make blink the master
 }
 
 
@@ -952,8 +1114,8 @@ void AlignAndDistribute::setMode(bool nodeEdit)
 
     ((_alignFrame).*(mSel))();
     ((_distributeFrame).*(mSel))();
+    ((_rearrangeFrame).*(mSel))();
     ((_removeOverlapFrame).*(mSel))();
-    ((_graphLayoutFrame).*(mSel))();
     ((_nodesFrame).*(mNode))();
 
 }
@@ -967,7 +1129,7 @@ void AlignAndDistribute::addAlignButton(const Glib::ustring &id, const Glib::ust
 }
 void AlignAndDistribute::addDistributeButton(const Glib::ustring &id, const Glib::ustring tiptext,
                                       guint row, guint col, bool onInterSpace,
-                                      NR::Dim2 orientation, float kBegin, float kEnd)
+                                      Geom::Dim2 orientation, float kBegin, float kEnd)
 {
     _actionList.push_back(
         new ActionDistribute(
@@ -979,7 +1141,7 @@ void AlignAndDistribute::addDistributeButton(const Glib::ustring &id, const Glib
 }
 
 void AlignAndDistribute::addNodeButton(const Glib::ustring &id, const Glib::ustring tiptext,
-                   guint col, NR::Dim2 orientation, bool distribute)
+                   guint col, Geom::Dim2 orientation, bool distribute)
 {
     _actionList.push_back(
         new ActionNode(
@@ -1005,6 +1167,33 @@ void AlignAndDistribute::addGraphLayoutButton(const Glib::ustring &id, const Gli
         );
 }
 
+void AlignAndDistribute::addExchangePositionsButton(const Glib::ustring &id, const Glib::ustring tiptext,
+                                      guint row, guint col)
+{
+    _actionList.push_back(
+        new ActionExchangePositions(
+            id, tiptext, row, col, *this)
+        );
+}
+
+void AlignAndDistribute::addExchangePositionsByZOrderButton(const Glib::ustring &id, const Glib::ustring tiptext,
+                                      guint row, guint col)
+{
+    _actionList.push_back(
+        new ActionExchangePositions(
+            id, tiptext, row, col, *this, ActionExchangePositions::ZOrder)
+        );
+}
+
+void AlignAndDistribute::addExchangePositionsClockwiseButton(const Glib::ustring &id, const Glib::ustring tiptext,
+                                      guint row, guint col)
+{
+    _actionList.push_back(
+        new ActionExchangePositions(
+            id, tiptext, row, col, *this, ActionExchangePositions::Clockwise)
+        );
+}
+
 void AlignAndDistribute::addUnclumpButton(const Glib::ustring &id, const Glib::ustring tiptext,
                                       guint row, guint col)
 {
@@ -1024,11 +1213,11 @@ void AlignAndDistribute::addRandomizeButton(const Glib::ustring &id, const Glib:
 }
 
 void AlignAndDistribute::addBaselineButton(const Glib::ustring &id, const Glib::ustring tiptext,
-                                    guint row, guint col, Gtk::Table &table, NR::Dim2 orientation, bool distribute)
+                                    guint row, guint col, Gtk::Table &table, Geom::Dim2 orientation, bool distribute)
 {
     _actionList.push_back(
         new ActionBaseline(
-            id, tiptext, row, col, 
+            id, tiptext, row, col,
             *this, table, orientation, distribute));
 }
 
@@ -1050,11 +1239,13 @@ std::list<SPItem *>::iterator AlignAndDistribute::find_master( std::list<SPItem
     {
         gdouble max = -1e18;
         for (std::list<SPItem *>::iterator it = list.begin(); it != list.end(); it++) {
-            NR::Rect b = sp_item_bbox_desktop (*it);
-            gdouble dim = b.extent(horizontal ? NR::X : NR::Y);
-            if (dim > max) {
-                max = dim;
-                master = it;
+            Geom::OptRect b = sp_item_bbox_desktop (*it);
+            if (b) {
+                gdouble dim = (*b)[horizontal ? Geom::X : Geom::Y].extent();
+                if (dim > max) {
+                    max = dim;
+                    master = it;
+                }
             }
         }
         return master;
@@ -1065,11 +1256,13 @@ std::list<SPItem *>::iterator AlignAndDistribute::find_master( std::list<SPItem
     {
         gdouble max = 1e18;
         for (std::list<SPItem *>::iterator it = list.begin(); it != list.end(); it++) {
-            NR::Rect b = sp_item_bbox_desktop (*it);
-            gdouble dim = b.extent(horizontal ? NR::X : NR::Y);
-            if (dim < max) {
-                max = dim;
-                master = it;
+            Geom::OptRect b = sp_item_bbox_desktop (*it);
+            if (b) {
+                gdouble dim = (*b)[horizontal ? Geom::X : Geom::Y].extent();
+                if (dim < max) {
+                    max = dim;
+                    master = it;
+                }
             }
         }
         return master;