X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fui%2Fdialog%2Falign-and-distribute.cpp;h=a88688d80882fc69ffc3b7d40326c8eae33ac1fb;hb=752bd54d33076c171ca18f54c41c21fb0fa2ef35;hp=99d3fdda3e521c705952f686f97ce68baad3d66b;hpb=5c6771ad614c8c281c8fcf8f6c1aa727e981a823;p=inkscape.git diff --git a/src/ui/dialog/align-and-distribute.cpp b/src/ui/dialog/align-and-distribute.cpp index 99d3fdda3..a88688d80 100644 --- a/src/ui/dialog/align-and-distribute.cpp +++ b/src/ui/dialog/align-and-distribute.cpp @@ -1,7 +1,7 @@ -/** - * \brief Align and Distribute dialog - * - * Authors: +/** @file + * @brief Align and Distribute dialog - implementation + */ +/* Authors: * Bryce W. Harrington * Aubanel MONNIER * Frank Felfe @@ -18,38 +18,29 @@ # include #endif -#include "verbs.h" - -#include "dialogs/unclump.h" -#include "removeoverlap/removeoverlap.h" -#include "graphlayout/graphlayout.h" - #include - - - -#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 access to ShapeEditor -#include "shape-editor.h" //For node align/distribute methods - #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" @@ -122,7 +113,8 @@ private : Inkscape::Selection *selection = sp_desktop_selection(desktop); if (!selection) return; - bool sel_as_group = (prefs_get_int_attribute("dialogs.align", "sel-as-groups", 0) != 0); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool sel_as_group = prefs->getBool("/dialogs/align/sel-as-groups"); using Inkscape::Util::GSListConstIterator; std::list selected; @@ -160,7 +152,7 @@ private : selected.erase(master); /*}*/ //Compute the anchor point - boost::optional b = sp_item_bbox_desktop (thing); + 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]); @@ -177,7 +169,7 @@ private : case AlignAndDistribute::DRAWING: { - boost::optional b = sp_item_bbox_desktop + Geom::OptRect b = sp_item_bbox_desktop ( (SPItem *) sp_document_root (sp_desktop_document (desktop)) ); if (b) { mp = Geom::Point(a.mx0 * b->min()[Geom::X] + a.mx1 * b->max()[Geom::X], @@ -190,7 +182,7 @@ private : case AlignAndDistribute::SELECTION: { - boost::optional b = selection->bounds(); + 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]); @@ -211,11 +203,11 @@ 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; - boost::optional b; + Geom::OptRect b; if (sel_as_group) b = selection->bounds(); @@ -239,7 +231,7 @@ private : } // 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, @@ -330,17 +322,18 @@ private : it != selected.end(); ++it) { - boost::optional bbox = sp_item_bbox_desktop(*it); + Geom::OptRect bbox = sp_item_bbox_desktop(*it); if (bbox) { - sorted.push_back(BBoxSort(*it, to_2geom(*bbox), _orientation, _kBegin, _kEnd)); + 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; @@ -397,7 +390,7 @@ private : } // 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, @@ -436,12 +429,13 @@ private : if (!_dialog.getDesktop()) return; SPEventContext *event_context = sp_desktop_event_context(_dialog.getDesktop()); - if (!SP_IS_NODE_CONTEXT (event_context)) return ; + if (!INK_IS_NODE_TOOL (event_context)) return; + InkNodeTool *nt = INK_NODE_TOOL(event_context); if (_distribute) - SP_NODE_CONTEXT (event_context)->shape_editor->distribute((Geom::Dim2)_orientation); + nt->_multipath->distributeNodes(_orientation); else - SP_NODE_CONTEXT (event_context)->shape_editor->align((Geom::Dim2)_orientation); + nt->_multipath->alignNodes(_orientation); } }; @@ -466,23 +460,23 @@ public: 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. Only put "H:" equivalent in the translation */ - removeOverlapXGapLabel.set_label(Q_("gap|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); @@ -497,8 +491,9 @@ private : 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(); @@ -507,7 +502,7 @@ private : 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(_dialog.getDesktop()), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Remove overlaps")); @@ -521,8 +516,8 @@ 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 : @@ -531,19 +526,115 @@ private : 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_set_int_attribute("options.clonecompensation", "value", saved_compensation); + 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) + {}; + + +private : + const SortOrder sortOrder; + static boost::optional 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 selected; + selected.insert >(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::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->setInt("/options/clonecompensation/value", saved_compensation); + + sp_document_done(sp_desktop_document(_dialog.getDesktop()), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, + _("Exchange Positions")); + } +}; +// instantiae the private static member +boost::optional ActionExchangePositions::center; + class ActionUnclump : public Action { public : ActionUnclump(const Glib::ustring &id, @@ -552,7 +643,7 @@ public : guint column, AlignAndDistribute &dialog): Action(id, tiptext, row, column, - dialog.distribute_table(), dialog.tooltips(), dialog) + dialog.rearrange_table(), dialog.tooltips(), dialog) {} private : @@ -561,13 +652,14 @@ private : 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(_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 (_dialog.getDesktop()), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Unclump")); @@ -582,7 +674,7 @@ public : guint column, AlignAndDistribute &dialog): Action(id, tiptext, row, column, - dialog.distribute_table(), dialog.tooltips(), dialog) + dialog.rearrange_table(), dialog.tooltips(), dialog) {} private : @@ -602,7 +694,7 @@ private : //Check 2 or more selected objects if (selected.size() < 2) return; - boost::optional sel_bbox = selection->bounds(); + Geom::OptRect sel_bbox = selection->bounds(); if (!sel_bbox) { return; } @@ -611,33 +703,34 @@ private : // nor drift on sequential randomizations. Discard cache on global (or better active // desktop's) selection_change signal. if (!_dialog.randomize_bbox) { - _dialog.randomize_bbox = to_2geom(*sel_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::iterator it(selected.begin()); it != selected.end(); ++it) { sp_document_ensure_up_to_date(sp_desktop_document (desktop)); - boost::optional item_box = sp_item_bbox_desktop (*it); + 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).extent(Geom::X)/2 + - g_random_double_range (0, (*_dialog.randomize_bbox)[Geom::X].extent() - (*item_box).extent(Geom::X)); - double y = _dialog.randomize_bbox->min()[Geom::Y] + (*item_box).extent(Geom::Y)/2 + - g_random_double_range (0, (*_dialog.randomize_bbox)[Geom::Y].extent() - (*item_box).extent(Geom::Y)); + 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: - NR::Point t = NR::Point (x, y) - 0.5*(item_box->max() + item_box->min()); + 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 (desktop), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Randomize positions")); @@ -706,14 +799,16 @@ private : { if (SP_IS_TEXT (*it) || SP_IS_FLOWTEXT (*it)) { Inkscape::Text::Layout const *layout = te_get_layout(*it); - Geom::Point base = layout->characterAnchorPoint(layout->begin()) * 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); + boost::optional 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); + } } } @@ -747,11 +842,14 @@ private : { if (SP_IS_TEXT (*it) || SP_IS_FLOWTEXT (*it)) { Inkscape::Text::Layout const *layout = te_get_layout(*it); - Geom::Point base = layout->characterAnchorPoint(layout->begin()) * 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; + boost::optional 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; + } } } @@ -774,7 +872,7 @@ void on_tool_changed(Inkscape::Application */*inkscape*/, SPEventContext */*cont void on_selection_changed(Inkscape::Application */*inkscape*/, Inkscape::Selection */*selection*/, AlignAndDistribute *daad) { - daad->randomize_bbox = boost::optional(); + daad->randomize_bbox = Geom::OptRect(); } ///////////////////////////////////////////////////////// @@ -783,127 +881,142 @@ void on_selection_changed(Inkscape::Application */*inkscape*/, Inkscape::Selecti AlignAndDistribute::AlignAndDistribute() - : UI::Widget::Panel ("", "dialogs.align", SP_VERB_DIALOG_ALIGN_DISTRIBUTE), + : 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: ")), _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(), Geom::X, false); - addBaselineButton("al_baselines_hor", + addBaselineButton(INKSCAPE_ICON_ALIGN_HORIZONTAL_BASELINE, _("Align baseline anchors of texts horizontally"), + 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, Geom::X, .5, .5); - addDistributeButton("distribute_left", - _("Distribute left sides equidistantly"), + addDistributeButton(INKSCAPE_ICON_DISTRIBUTE_HORIZONTAL_LEFT, + _("Distribute left edges equidistantly"), 0, 1, false, Geom::X, 1., 0.); - addDistributeButton("distribute_hcentre", + addDistributeButton(INKSCAPE_ICON_DISTRIBUTE_HORIZONTAL_CENTER, _("Distribute centers equidistantly horizontally"), 0, 2, false, Geom::X, .5, .5); - addDistributeButton("distribute_right", - _("Distribute right sides equidistantly"), + 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, Geom::Y, .5, .5); - addDistributeButton("distribute_top", - _("Distribute tops equidistantly"), + addDistributeButton(INKSCAPE_ICON_DISTRIBUTE_VERTICAL_TOP, + _("Distribute top edges equidistantly"), 1, 1, false, Geom::Y, 0, 1); - addDistributeButton("distribute_vcentre", + addDistributeButton(INKSCAPE_ICON_DISTRIBUTE_VERTICAL_CENTER, _("Distribute centers equidistantly vertically"), 1, 2, false, Geom::Y, .5, .5); - addDistributeButton("distribute_bottom", - _("Distribute bottoms equidistantly"), + 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(), Geom::X, true); - addBaselineButton("distribute_baselines_vert", - _("Distribute baseline anchors of texts vertically"), + 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"), + // 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("node_valign", - _("Align selected nodes vertically"), + addNodeButton(INKSCAPE_ICON_ALIGN_HORIZONTAL_NODES, + _("Align selected nodes to a common vertical line"), 1, Geom::Y, false); - addNodeButton("node_hdistribute", + addNodeButton(INKSCAPE_ICON_DISTRIBUTE_HORIZONTAL_NODE, _("Distribute selected nodes horizontally"), 2, Geom::X, true); - addNodeButton("node_vdistribute", + addNodeButton(INKSCAPE_ICON_DISTRIBUTE_VERTICAL_NODE, _("Distribute selected nodes vertically"), 3, Geom::Y, true); @@ -911,13 +1024,13 @@ AlignAndDistribute::AlignAndDistribute() _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(prefs_get_int_attribute("dialogs.align", "align-to", 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); @@ -925,7 +1038,7 @@ AlignAndDistribute::AlignAndDistribute() _selgrpBox.pack_start(_selgrpLabel); _selgrpBox.pack_start(_selgrp); - _selgrp.set_active(prefs_get_int_attribute("dialogs.align", "sel-as-groups", 0)); + _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); @@ -934,8 +1047,8 @@ AlignAndDistribute::AlignAndDistribute() _alignFrame.add(_alignBox); _distributeFrame.add(_distributeTable); + _rearrangeFrame.add(_rearrangeTable); _removeOverlapFrame.add(_removeOverlapTable); - _graphLayoutFrame.add(_graphLayoutTable); _nodesFrame.add(_nodesTable); Gtk::Box *contents = _getContents(); @@ -945,8 +1058,8 @@ AlignAndDistribute::AlignAndDistribute() 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(_graphLayoutFrame, true, true); contents->pack_start(_nodesFrame, true, true); //Connect to the global tool change signal @@ -954,7 +1067,7 @@ AlignAndDistribute::AlignAndDistribute() // 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 = boost::optional(); + randomize_bbox = Geom::OptRect(); show_all_children(); @@ -972,15 +1085,15 @@ AlignAndDistribute::~AlignAndDistribute() } void AlignAndDistribute::on_ref_change(){ - - prefs_set_int_attribute("dialogs.align", "align-to", _combo.get_active_row_number()); + 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(){ - - prefs_set_int_attribute("dialogs.align", "sel-as-groups", _selgrp.get_active()); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setInt("/dialogs/align/sel-as-groups", _selgrp.get_active()); //Make blink the master } @@ -1001,8 +1114,8 @@ void AlignAndDistribute::setMode(bool nodeEdit) ((_alignFrame).*(mSel))(); ((_distributeFrame).*(mSel))(); + ((_rearrangeFrame).*(mSel))(); ((_removeOverlapFrame).*(mSel))(); - ((_graphLayoutFrame).*(mSel))(); ((_nodesFrame).*(mNode))(); } @@ -1054,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) { @@ -1099,9 +1239,9 @@ std::list::iterator AlignAndDistribute::find_master( std::list::iterator it = list.begin(); it != list.end(); it++) { - boost::optional b = sp_item_bbox_desktop (*it); + Geom::OptRect b = sp_item_bbox_desktop (*it); if (b) { - gdouble dim = (*b).extent(horizontal ? Geom::X : Geom::Y); + gdouble dim = (*b)[horizontal ? Geom::X : Geom::Y].extent(); if (dim > max) { max = dim; master = it; @@ -1116,9 +1256,9 @@ std::list::iterator AlignAndDistribute::find_master( std::list::iterator it = list.begin(); it != list.end(); it++) { - boost::optional b = sp_item_bbox_desktop (*it); + Geom::OptRect b = sp_item_bbox_desktop (*it); if (b) { - gdouble dim = (*b).extent(horizontal ? Geom::X : Geom::Y); + gdouble dim = (*b)[horizontal ? Geom::X : Geom::Y].extent(); if (dim < max) { max = dim; master = it;