Code

Pot and Dutch translation update
[inkscape.git] / src / ui / dialog / align-and-distribute.cpp
1 /** @file
2  * @brief Align and Distribute dialog - implementation
3  */
4 /* Authors:
5  *   Bryce W. Harrington <bryce@bryceharrington.org>
6  *   Aubanel MONNIER <aubi@libertysurf.fr>
7  *   Frank Felfe <innerspace@iname.com>
8  *   Lauris Kaplinski <lauris@kaplinski.com>
9  *   Tim Dwyer <tgdwyer@gmail.com>
10  *
11  * Copyright (C) 1999-2004, 2005 Authors
12  *
13  * Released under GNU GPL.  Read the file 'COPYING' for more information.
14  */
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
21 #include <gtkmm/spinbutton.h>
23 #include "desktop-handles.h"
24 #include "unclump.h"
25 #include "document.h"
26 #include "enums.h"
27 #include "graphlayout.h"
28 #include "inkscape.h"
29 #include "macros.h"
30 #include "preferences.h"
31 #include "removeoverlap.h"
32 #include "selection.h"
33 #include "sp-flowtext.h"
34 #include "sp-item-transform.h"
35 #include "sp-text.h"
36 #include "text-editing.h"
37 #include "tools-switch.h"
38 #include "ui/icon-names.h"
39 #include "ui/tool/node-tool.h"
40 #include "ui/tool/multi-path-manipulator.h"
41 #include "util/glib-list-iterators.h"
42 #include "verbs.h"
43 #include "widgets/icon.h"
45 #include "align-and-distribute.h"
47 namespace Inkscape {
48 namespace UI {
49 namespace Dialog {
51 /////////helper classes//////////////////////////////////
53 class Action {
54 public :
55     Action(const Glib::ustring &id,
56            const Glib::ustring &tiptext,
57            guint row, guint column,
58            Gtk::Table &parent,
59            Gtk::Tooltips &tooltips,
60            AlignAndDistribute &dialog):
61         _dialog(dialog),
62         _id(id),
63         _parent(parent)
64     {
65         Gtk::Widget*  pIcon = Gtk::manage( sp_icon_get_icon( _id, Inkscape::ICON_SIZE_LARGE_TOOLBAR) );
66         Gtk::Button * pButton = Gtk::manage(new Gtk::Button());
67         pButton->set_relief(Gtk::RELIEF_NONE);
68         pIcon->show();
69         pButton->add(*pIcon);
70         pButton->show();
72         pButton->signal_clicked()
73             .connect(sigc::mem_fun(*this, &Action::on_button_click));
74         tooltips.set_tip(*pButton, tiptext);
75         parent.attach(*pButton, column, column+1, row, row+1, Gtk::FILL, Gtk::FILL);
76     }
77     virtual ~Action(){}
79     AlignAndDistribute &_dialog;
81 private :
82     virtual void on_button_click(){}
84     Glib::ustring _id;
85     Gtk::Table &_parent;
86 };
89 class ActionAlign : public Action {
90 public :
91     struct Coeffs {
92        double mx0, mx1, my0, my1;
93        double sx0, sx1, sy0, sy1;
94     };
95     ActionAlign(const Glib::ustring &id,
96                 const Glib::ustring &tiptext,
97                 guint row, guint column,
98                 AlignAndDistribute &dialog,
99                 guint coeffIndex):
100         Action(id, tiptext, row, column,
101                dialog.align_table(), dialog.tooltips(), dialog),
102         _index(coeffIndex),
103         _dialog(dialog)
104     {}
106 private :
108     virtual void on_button_click() {
109         //Retreive selected objects
110         SPDesktop *desktop = _dialog.getDesktop();
111         if (!desktop) return;
113         Inkscape::Selection *selection = sp_desktop_selection(desktop);
114         if (!selection) return;
116         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
117         bool sel_as_group = prefs->getBool("/dialogs/align/sel-as-groups");
119         using Inkscape::Util::GSListConstIterator;
120         std::list<SPItem *> selected;
121         selected.insert<GSListConstIterator<SPItem *> >(selected.end(), selection->itemList(), NULL);
122         if (selected.empty()) return;
124         Geom::Point mp; //Anchor point
125         AlignAndDistribute::AlignTarget target = _dialog.getAlignTarget();
126         const Coeffs &a= _allCoeffs[_index];
127         switch (target)
128         {
129         case AlignAndDistribute::LAST:
130         case AlignAndDistribute::FIRST:
131         case AlignAndDistribute::BIGGEST:
132         case AlignAndDistribute::SMALLEST:
133         {
134             //Check 2 or more selected objects
135             std::list<SPItem *>::iterator second(selected.begin());
136             ++second;
137             if (second == selected.end())
138                 return;
139             //Find the master (anchor on which the other objects are aligned)
140             std::list<SPItem *>::iterator master(
141                 _dialog.find_master (
142                     selected,
143                     (a.mx0 != 0.0) ||
144                     (a.mx1 != 0.0) )
145                 );
146             //remove the master from the selection
147             SPItem * thing = *master;
148             // TODO: either uncomment or remove the following commented lines, depending on which
149             //       behaviour of moving objects makes most sense; also cf. discussion at
150             //       https://bugs.launchpad.net/inkscape/+bug/255933
151             /*if (!sel_as_group) { */
152                 selected.erase(master);
153             /*}*/
154             //Compute the anchor point
155             Geom::OptRect b = sp_item_bbox_desktop (thing);
156             if (b) {
157                 mp = Geom::Point(a.mx0 * b->min()[Geom::X] + a.mx1 * b->max()[Geom::X],
158                                a.my0 * b->min()[Geom::Y] + a.my1 * b->max()[Geom::Y]);
159             } else {
160                 return;
161             }
162             break;
163         }
165         case AlignAndDistribute::PAGE:
166             mp = Geom::Point(a.mx1 * sp_document_width(sp_desktop_document(desktop)),
167                            a.my1 * sp_document_height(sp_desktop_document(desktop)));
168             break;
170         case AlignAndDistribute::DRAWING:
171         {
172             Geom::OptRect b = sp_item_bbox_desktop
173                 ( (SPItem *) sp_document_root (sp_desktop_document (desktop)) );
174             if (b) {
175                 mp = Geom::Point(a.mx0 * b->min()[Geom::X] + a.mx1 * b->max()[Geom::X],
176                                a.my0 * b->min()[Geom::Y] + a.my1 * b->max()[Geom::Y]);
177             } else {
178                 return;
179             }
180             break;
181         }
183         case AlignAndDistribute::SELECTION:
184         {
185             Geom::OptRect b =  selection->bounds();
186             if (b) {
187                 mp = Geom::Point(a.mx0 * b->min()[Geom::X] + a.mx1 * b->max()[Geom::X],
188                                a.my0 * b->min()[Geom::Y] + a.my1 * b->max()[Geom::Y]);
189             } else {
190                 return;
191             }
192             break;
193         }
195         default:
196             g_assert_not_reached ();
197             break;
198         };  // end of switch
200         // Top hack: temporarily set clone compensation to unmoved, so that we can align/distribute
201         // clones with their original (and the move of the original does not disturb the
202         // clones). The only problem with this is that if there are outside-of-selection clones of
203         // a selected original, they will be unmoved too, possibly contrary to user's
204         // expecation. However this is a minor point compared to making align/distribute always
205         // work as expected, and "unmoved" is the default option anyway.
206         int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
207         prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
209         bool changed = false;
210         Geom::OptRect b;
211         if (sel_as_group)
212             b = selection->bounds();
214         //Move each item in the selected list separately
215         for (std::list<SPItem *>::iterator it(selected.begin());
216              it != selected.end();
217              it++)
218         {
219             sp_document_ensure_up_to_date(sp_desktop_document (desktop));
220             if (!sel_as_group)
221                 b = sp_item_bbox_desktop (*it);
222             if (b) {
223                 Geom::Point const sp(a.sx0 * b->min()[Geom::X] + a.sx1 * b->max()[Geom::X],
224                                      a.sy0 * b->min()[Geom::Y] + a.sy1 * b->max()[Geom::Y]);
225                 Geom::Point const mp_rel( mp - sp );
226                 if (LInfty(mp_rel) > 1e-9) {
227                     sp_item_move_rel(*it, Geom::Translate(mp_rel));
228                     changed = true;
229                 }
230             }
231         }
233         // restore compensation setting
234         prefs->setInt("/options/clonecompensation/value", saved_compensation);
236         if (changed) {
237             sp_document_done ( sp_desktop_document (desktop) , SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
238                                _("Align"));
239         }
242     }
243     guint _index;
244     AlignAndDistribute &_dialog;
246     static const Coeffs _allCoeffs[10];
248 };
249 ActionAlign::Coeffs const ActionAlign::_allCoeffs[10] = {
250     {1., 0., 0., 0., 0., 1., 0., 0.},
251     {1., 0., 0., 0., 1., 0., 0., 0.},
252     {.5, .5, 0., 0., .5, .5, 0., 0.},
253     {0., 1., 0., 0., 0., 1., 0., 0.},
254     {0., 1., 0., 0., 1., 0., 0., 0.},
255     {0., 0., 0., 1., 0., 0., 1., 0.},
256     {0., 0., 0., 1., 0., 0., 0., 1.},
257     {0., 0., .5, .5, 0., 0., .5, .5},
258     {0., 0., 1., 0., 0., 0., 1., 0.},
259     {0., 0., 1., 0., 0., 0., 0., 1.}
260 };
262 BBoxSort::BBoxSort(SPItem *pItem, Geom::Rect bounds, Geom::Dim2 orientation, double kBegin, double kEnd) :
263         item(pItem),
264         bbox (bounds)
266         anchor = kBegin * bbox.min()[orientation] + kEnd * bbox.max()[orientation];
268 BBoxSort::BBoxSort(const BBoxSort &rhs) :
269         //NOTE :  this copy ctor is called O(sort) when sorting the vector
270         //this is bad. The vector should be a vector of pointers.
271         //But I'll wait the bohem GC before doing that
272         item(rhs.item), anchor(rhs.anchor), bbox(rhs.bbox) 
276 bool operator< (const BBoxSort &a, const BBoxSort &b)
278     return (a.anchor < b.anchor);
281 class ActionDistribute : public Action {
282 public :
283     ActionDistribute(const Glib::ustring &id,
284                      const Glib::ustring &tiptext,
285                      guint row, guint column,
286                      AlignAndDistribute &dialog,
287                      bool onInterSpace,
288                      Geom::Dim2 orientation,
289                      double kBegin, double kEnd
290         ):
291         Action(id, tiptext, row, column,
292                dialog.distribute_table(), dialog.tooltips(), dialog),
293         _dialog(dialog),
294         _onInterSpace(onInterSpace),
295         _orientation(orientation),
296         _kBegin(kBegin),
297         _kEnd( kEnd)
298     {}
300 private :
301     virtual void on_button_click() {
302         //Retreive selected objects
303         SPDesktop *desktop = _dialog.getDesktop();
304         if (!desktop) return;
306         Inkscape::Selection *selection = sp_desktop_selection(desktop);
307         if (!selection) return;
309         using Inkscape::Util::GSListConstIterator;
310         std::list<SPItem *> selected;
311         selected.insert<GSListConstIterator<SPItem *> >(selected.end(), selection->itemList(), NULL);
312         if (selected.empty()) return;
314         //Check 2 or more selected objects
315         std::list<SPItem *>::iterator second(selected.begin());
316         ++second;
317         if (second == selected.end()) return;
320         std::vector< BBoxSort  > sorted;
321         for (std::list<SPItem *>::iterator it(selected.begin());
322             it != selected.end();
323             ++it)
324         {
325             Geom::OptRect bbox = sp_item_bbox_desktop(*it);
326             if (bbox) {
327                 sorted.push_back(BBoxSort(*it, *bbox, _orientation, _kBegin, _kEnd));
328             }
329         }
330         //sort bbox by anchors
331         std::sort(sorted.begin(), sorted.end());
333         // see comment in ActionAlign above
334         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
335         int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
336         prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
338         unsigned int len = sorted.size();
339         bool changed = false;
340         if (_onInterSpace)
341         {
342             //overall bboxes span
343             float dist = (sorted.back().bbox.max()[_orientation] -
344                           sorted.front().bbox.min()[_orientation]);
345             //space eaten by bboxes
346             float span = 0;
347             for (unsigned int i = 0; i < len; i++)
348             {
349                 span += sorted[i].bbox[_orientation].extent();
350             }
351             //new distance between each bbox
352             float step = (dist - span) / (len - 1);
353             float pos = sorted.front().bbox.min()[_orientation];
354             for ( std::vector<BBoxSort> ::iterator it (sorted.begin());
355                   it < sorted.end();
356                   it ++ )
357             {
358                 if (!NR_DF_TEST_CLOSE (pos, it->bbox.min()[_orientation], 1e-6)) {
359                     Geom::Point t(0.0, 0.0);
360                     t[_orientation] = pos - it->bbox.min()[_orientation];
361                     sp_item_move_rel(it->item, Geom::Translate(t));
362                     changed = true;
363                 }
364                 pos += it->bbox[_orientation].extent();
365                 pos += step;
366             }
367         }
368         else
369         {
370             //overall anchor span
371             float dist = sorted.back().anchor - sorted.front().anchor;
372             //distance between anchors
373             float step = dist / (len - 1);
375             for ( unsigned int i = 0; i < len ; i ++ )
376             {
377                 BBoxSort & it(sorted[i]);
378                 //new anchor position
379                 float pos = sorted.front().anchor + i * step;
380                 //Don't move if we are really close
381                 if (!NR_DF_TEST_CLOSE (pos, it.anchor, 1e-6)) {
382                     //Compute translation
383                     Geom::Point t(0.0, 0.0);
384                     t[_orientation] = pos - it.anchor;
385                     //translate
386                     sp_item_move_rel(it.item, Geom::Translate(t));
387                     changed = true;
388                 }
389             }
390         }
392         // restore compensation setting
393         prefs->setInt("/options/clonecompensation/value", saved_compensation);
395         if (changed) {
396             sp_document_done ( sp_desktop_document (desktop), SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
397                                _("Distribute"));
398         }
399     }
400     guint _index;
401     AlignAndDistribute &_dialog;
402     bool _onInterSpace;
403     Geom::Dim2 _orientation;
405     double _kBegin;
406     double _kEnd;
408 };
411 class ActionNode : public Action {
412 public :
413     ActionNode(const Glib::ustring &id,
414                const Glib::ustring &tiptext,
415                guint column,
416                AlignAndDistribute &dialog,
417                Geom::Dim2 orientation, bool distribute):
418         Action(id, tiptext, 0, column,
419                dialog.nodes_table(), dialog.tooltips(), dialog),
420         _orientation(orientation),
421         _distribute(distribute)
422     {}
424 private :
425     Geom::Dim2 _orientation;
426     bool _distribute;
427     virtual void on_button_click()
428     {
430         if (!_dialog.getDesktop()) return;
431         SPEventContext *event_context = sp_desktop_event_context(_dialog.getDesktop());
432         if (!INK_IS_NODE_TOOL (event_context)) return;
433         InkNodeTool *nt = INK_NODE_TOOL(event_context);
435         if (_distribute)
436             nt->_multipath->distributeNodes(_orientation);
437         else
438             nt->_multipath->alignNodes(_orientation);
440     }
441 };
443 class ActionRemoveOverlaps : public Action {
444 private:
445     Gtk::Label removeOverlapXGapLabel;
446     Gtk::Label removeOverlapYGapLabel;
447     Gtk::SpinButton removeOverlapXGap;
448     Gtk::SpinButton removeOverlapYGap;
450 public:
451     ActionRemoveOverlaps(Glib::ustring const &id,
452                          Glib::ustring const &tiptext,
453                          guint row,
454                          guint column,
455                          AlignAndDistribute &dialog) :
456         Action(id, tiptext, row, column + 4,
457                dialog.removeOverlap_table(), dialog.tooltips(), dialog)
458     {
459         dialog.removeOverlap_table().set_col_spacings(3);
461         removeOverlapXGap.set_digits(1);
462         removeOverlapXGap.set_size_request(60, -1);
463         removeOverlapXGap.set_increments(1.0, 0);
464         removeOverlapXGap.set_range(-1000.0, 1000.0);
465         removeOverlapXGap.set_value(0);
466         dialog.tooltips().set_tip(removeOverlapXGap,
467                                   _("Minimum horizontal gap (in px units) between bounding boxes"));
468         //TRANSLATORS: "H:" stands for horizontal gap
469         removeOverlapXGapLabel.set_label(C_("Gap", "H:"));
471         removeOverlapYGap.set_digits(1);
472         removeOverlapYGap.set_size_request(60, -1);
473         removeOverlapYGap.set_increments(1.0, 0);
474         removeOverlapYGap.set_range(-1000.0, 1000.0);
475         removeOverlapYGap.set_value(0);
476         dialog.tooltips().set_tip(removeOverlapYGap,
477                                   _("Minimum vertical gap (in px units) between bounding boxes"));
478         /* TRANSLATORS: Vertical gap */
479         removeOverlapYGapLabel.set_label(C_("Gap", "V:"));
481         dialog.removeOverlap_table().attach(removeOverlapXGapLabel, column, column+1, row, row+1, Gtk::FILL, Gtk::FILL);
482         dialog.removeOverlap_table().attach(removeOverlapXGap, column+1, column+2, row, row+1, Gtk::FILL, Gtk::FILL);
483         dialog.removeOverlap_table().attach(removeOverlapYGapLabel, column+2, column+3, row, row+1, Gtk::FILL, Gtk::FILL);
484         dialog.removeOverlap_table().attach(removeOverlapYGap, column+3, column+4, row, row+1, Gtk::FILL, Gtk::FILL);
486     }
488 private :
489     virtual void on_button_click()
490     {
491         if (!_dialog.getDesktop()) return;
493         // see comment in ActionAlign above
494         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
495         int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
496         prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
498         // xGap and yGap are the minimum space required between bounding rectangles.
499         double const xGap = removeOverlapXGap.get_value();
500         double const yGap = removeOverlapYGap.get_value();
501         removeoverlap(sp_desktop_selection(_dialog.getDesktop())->itemList(),
502                       xGap, yGap);
504         // restore compensation setting
505         prefs->setInt("/options/clonecompensation/value", saved_compensation);
507         sp_document_done(sp_desktop_document(_dialog.getDesktop()), SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
508                          _("Remove overlaps"));
509     }
510 };
512 class ActionGraphLayout : public Action {
513 public:
514     ActionGraphLayout(Glib::ustring const &id,
515                          Glib::ustring const &tiptext,
516                          guint row,
517                          guint column,
518                          AlignAndDistribute &dialog) :
519         Action(id, tiptext, row, column,
520                dialog.rearrange_table(), dialog.tooltips(), dialog)
521     {}
523 private :
524     virtual void on_button_click()
525     {
526         if (!_dialog.getDesktop()) return;
528         // see comment in ActionAlign above
529         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
530         int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
531         prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
533         graphlayout(sp_desktop_selection(_dialog.getDesktop())->itemList());
535         // restore compensation setting
536         prefs->setInt("/options/clonecompensation/value", saved_compensation);
538         sp_document_done(sp_desktop_document(_dialog.getDesktop()), SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
539                          _("Arrange connector network"));
540     }
541 };
543 class ActionExchangePositions : public Action {
544 public:
545     enum SortOrder {
546         None,
547         ZOrder,
548         Clockwise
549     };      
551     ActionExchangePositions(Glib::ustring const &id,
552                          Glib::ustring const &tiptext,
553                          guint row,
554                          guint column,
555                          AlignAndDistribute &dialog, SortOrder order = None) :
556         Action(id, tiptext, row, column,
557                dialog.rearrange_table(), dialog.tooltips(), dialog),
558         sortOrder(order)
559     {};
562 private :
563     const SortOrder sortOrder;
564     static boost::optional<Geom::Point> center;
566     static bool sort_compare(const SPItem * a,const SPItem * b) {
567         if (a == NULL) return false;
568         if (b == NULL) return true;
569         if (center) {
570             Geom::Point point_a = a->getCenter() - (*center);
571             Geom::Point point_b = b->getCenter() - (*center);
572             // First criteria: Sort according to the angle to the center point
573             double angle_a = atan2(double(point_a[Geom::Y]), double(point_a[Geom::X]));
574             double angle_b = atan2(double(point_b[Geom::Y]), double(point_b[Geom::X]));
575             if (angle_a != angle_b) return (angle_a < angle_b);
576             // Second criteria: Sort according to the distance the center point
577             Geom::Coord length_a = point_a.length();
578             Geom::Coord length_b = point_b.length();
579             if (length_a != length_b) return (length_a > length_b);
580         }
581         // Last criteria: Sort according to the z-coordinate
582         return (a->isSiblingOf(b));
583     }
585     virtual void on_button_click()
586     {
587         SPDesktop *desktop = _dialog.getDesktop();
588         if (!desktop) return;
590         Inkscape::Selection *selection = sp_desktop_selection(desktop);
591         if (!selection) return;
593         using Inkscape::Util::GSListConstIterator;
594         std::list<SPItem *> selected;
595         selected.insert<GSListConstIterator<SPItem *> >(selected.end(), selection->itemList(), NULL);
596         if (selected.empty()) return;
598         //Check 2 or more selected objects
599         if (selected.size() < 2) return;
601         // see comment in ActionAlign above
602         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
603         int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
604         prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
606         // sort the list
607         if (sortOrder != None) {
608                 if (sortOrder == Clockwise) {
609                         center = selection->center();
610                 } else { // sorting by ZOrder is outomatically done by not setting the center
611                         center.reset();
612                 }
613                 selected.sort(ActionExchangePositions::sort_compare);
614         }
615         std::list<SPItem *>::iterator it(selected.begin());
616         Geom::Point p1 =  (*it)->getCenter();
617         for (++it ;it != selected.end(); ++it)
618         {
619                 Geom::Point p2 = (*it)->getCenter();
620                 Geom::Point delta = p1 - p2;
621                 sp_item_move_rel((*it),Geom::Translate(delta[Geom::X],delta[Geom::Y] ));
622                 p1 = p2;
623         }
624         Geom::Point p2 = selected.front()->getCenter();
625         Geom::Point delta = p1 - p2;
626         sp_item_move_rel(selected.front(),Geom::Translate(delta[Geom::X],delta[Geom::Y] ));
628         // restore compensation setting
629         prefs->setInt("/options/clonecompensation/value", saved_compensation);
631         sp_document_done(sp_desktop_document(_dialog.getDesktop()), SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
632                         _("Exchange Positions"));
633     }
634 };
635 // instantiae the private static member
636 boost::optional<Geom::Point> ActionExchangePositions::center;
638 class ActionUnclump : public Action {
639 public :
640     ActionUnclump(const Glib::ustring &id,
641                const Glib::ustring &tiptext,
642                guint row,
643                guint column,
644                AlignAndDistribute &dialog):
645         Action(id, tiptext, row, column,
646                dialog.rearrange_table(), dialog.tooltips(), dialog)
647     {}
649 private :
650     virtual void on_button_click()
651     {
652         if (!_dialog.getDesktop()) return;
654         // see comment in ActionAlign above
655         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
656         int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
657         prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
659         unclump ((GSList *) sp_desktop_selection(_dialog.getDesktop())->itemList());
661         // restore compensation setting
662         prefs->setInt("/options/clonecompensation/value", saved_compensation);
664         sp_document_done (sp_desktop_document (_dialog.getDesktop()), SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
665                           _("Unclump"));
666     }
667 };
669 class ActionRandomize : public Action {
670 public :
671     ActionRandomize(const Glib::ustring &id,
672                const Glib::ustring &tiptext,
673                guint row,
674                guint column,
675                AlignAndDistribute &dialog):
676         Action(id, tiptext, row, column,
677                dialog.rearrange_table(), dialog.tooltips(), dialog)
678     {}
680 private :
681     virtual void on_button_click()
682     {
683         SPDesktop *desktop = _dialog.getDesktop();
684         if (!desktop) return;
686         Inkscape::Selection *selection = sp_desktop_selection(desktop);
687         if (!selection) return;
689         using Inkscape::Util::GSListConstIterator;
690         std::list<SPItem *> selected;
691         selected.insert<GSListConstIterator<SPItem *> >(selected.end(), selection->itemList(), NULL);
692         if (selected.empty()) return;
694         //Check 2 or more selected objects
695         if (selected.size() < 2) return;
697         Geom::OptRect sel_bbox = selection->bounds();
698         if (!sel_bbox) {
699             return;
700         }
702         // This bbox is cached between calls to randomize, so that there's no growth nor shrink
703         // nor drift on sequential randomizations. Discard cache on global (or better active
704         // desktop's) selection_change signal.
705         if (!_dialog.randomize_bbox) {
706             _dialog.randomize_bbox = *sel_bbox;
707         }
709         // see comment in ActionAlign above
710         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
711         int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
712         prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
714         for (std::list<SPItem *>::iterator it(selected.begin());
715             it != selected.end();
716             ++it)
717         {
718             sp_document_ensure_up_to_date(sp_desktop_document (desktop));
719             Geom::OptRect item_box = sp_item_bbox_desktop (*it);
720             if (item_box) {
721                 // find new center, staying within bbox
722                 double x = _dialog.randomize_bbox->min()[Geom::X] + (*item_box)[Geom::X].extent() /2 +
723                     g_random_double_range (0, (*_dialog.randomize_bbox)[Geom::X].extent() - (*item_box)[Geom::X].extent());
724                 double y = _dialog.randomize_bbox->min()[Geom::Y] + (*item_box)[Geom::Y].extent()/2 +
725                     g_random_double_range (0, (*_dialog.randomize_bbox)[Geom::Y].extent() - (*item_box)[Geom::Y].extent());
726                 // displacement is the new center minus old:
727                 Geom::Point t = Geom::Point (x, y) - 0.5*(item_box->max() + item_box->min());
728                 sp_item_move_rel(*it, Geom::Translate(t));
729             }
730         }
732         // restore compensation setting
733         prefs->setInt("/options/clonecompensation/value", saved_compensation);
735         sp_document_done (sp_desktop_document (desktop), SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
736                           _("Randomize positions"));
737     }
738 };
740 struct Baselines
742     SPItem *_item;
743     Geom::Point _base;
744     Geom::Dim2 _orientation;
745     Baselines(SPItem *item, Geom::Point base, Geom::Dim2 orientation) :
746         _item (item),
747         _base (base),
748         _orientation (orientation)
749     {}
750 };
752 bool operator< (const Baselines &a, const Baselines &b)
754     return (a._base[a._orientation] < b._base[b._orientation]);
757 class ActionBaseline : public Action {
758 public :
759     ActionBaseline(const Glib::ustring &id,
760                const Glib::ustring &tiptext,
761                guint row,
762                guint column,
763                AlignAndDistribute &dialog,
764                Gtk::Table &table,
765                Geom::Dim2 orientation, bool distribute):
766         Action(id, tiptext, row, column,
767                table, dialog.tooltips(), dialog),
768         _orientation(orientation),
769         _distribute(distribute)
770     {}
772 private :
773     Geom::Dim2 _orientation;
774     bool _distribute;
775     virtual void on_button_click()
776     {
777         SPDesktop *desktop = _dialog.getDesktop();
778         if (!desktop) return;
780         Inkscape::Selection *selection = sp_desktop_selection(desktop);
781         if (!selection) return;
783         using Inkscape::Util::GSListConstIterator;
784         std::list<SPItem *> selected;
785         selected.insert<GSListConstIterator<SPItem *> >(selected.end(), selection->itemList(), NULL);
786         if (selected.empty()) return;
788         //Check 2 or more selected objects
789         if (selected.size() < 2) return;
791         Geom::Point b_min = Geom::Point (HUGE_VAL, HUGE_VAL);
792         Geom::Point b_max = Geom::Point (-HUGE_VAL, -HUGE_VAL);
794         std::vector<Baselines> sorted;
796         for (std::list<SPItem *>::iterator it(selected.begin());
797             it != selected.end();
798             ++it)
799         {
800             if (SP_IS_TEXT (*it) || SP_IS_FLOWTEXT (*it)) {
801                 Inkscape::Text::Layout const *layout = te_get_layout(*it);
802                 boost::optional<Geom::Point> pt = layout->baselineAnchorPoint();
803                 if (pt) {
804                     Geom::Point base = *pt * sp_item_i2d_affine(*it);
805                     if (base[Geom::X] < b_min[Geom::X]) b_min[Geom::X] = base[Geom::X];
806                     if (base[Geom::Y] < b_min[Geom::Y]) b_min[Geom::Y] = base[Geom::Y];
807                     if (base[Geom::X] > b_max[Geom::X]) b_max[Geom::X] = base[Geom::X];
808                     if (base[Geom::Y] > b_max[Geom::Y]) b_max[Geom::Y] = base[Geom::Y];
809                     Baselines b (*it, base, _orientation);
810                     sorted.push_back(b);
811                 }
812             }
813         }
815         if (sorted.size() <= 1) return;
817         //sort baselines
818         std::sort(sorted.begin(), sorted.end());
820         bool changed = false;
822         if (_distribute) {
823             double step = (b_max[_orientation] - b_min[_orientation])/(sorted.size() - 1);
824             for (unsigned int i = 0; i < sorted.size(); i++) {
825                 SPItem *item = sorted[i]._item;
826                 Geom::Point base = sorted[i]._base;
827                 Geom::Point t(0.0, 0.0);
828                 t[_orientation] = b_min[_orientation] + step * i - base[_orientation];
829                 sp_item_move_rel(item, Geom::Translate(t));
830                 changed = true;
831             }
833             if (changed) {
834                 sp_document_done (sp_desktop_document (desktop), SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
835                                   _("Distribute text baselines"));
836             }
838         } else {
839             for (std::list<SPItem *>::iterator it(selected.begin());
840                  it != selected.end();
841                  ++it)
842             {
843                 if (SP_IS_TEXT (*it) || SP_IS_FLOWTEXT (*it)) {
844                     Inkscape::Text::Layout const *layout = te_get_layout(*it);
845                     boost::optional<Geom::Point> pt = layout->baselineAnchorPoint();
846                     if (pt) {
847                         Geom::Point base = *pt * sp_item_i2d_affine(*it);
848                         Geom::Point t(0.0, 0.0);
849                         t[_orientation] = b_min[_orientation] - base[_orientation];
850                         sp_item_move_rel(*it, Geom::Translate(t));
851                         changed = true;
852                     }
853                 }
854             }
856             if (changed) {
857                 sp_document_done (sp_desktop_document (desktop), SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
858                                   _("Align text baselines"));
859             }
860         }
861     }
862 };
866 void on_tool_changed(Inkscape::Application */*inkscape*/, SPEventContext */*context*/, AlignAndDistribute *daad)
868     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
869     if (desktop && sp_desktop_event_context(desktop))
870         daad->setMode(tools_active(desktop) == TOOLS_NODES);
873 void on_selection_changed(Inkscape::Application */*inkscape*/, Inkscape::Selection */*selection*/, AlignAndDistribute *daad)
875     daad->randomize_bbox = Geom::OptRect();
878 /////////////////////////////////////////////////////////
883 AlignAndDistribute::AlignAndDistribute()
884     : UI::Widget::Panel ("", "/dialogs/align", SP_VERB_DIALOG_ALIGN_DISTRIBUTE),
885       randomize_bbox(),
886       _alignFrame(_("Align")),
887       _distributeFrame(_("Distribute")),
888       _rearrangeFrame(_("Rearrange")),
889       _removeOverlapFrame(_("Remove overlaps")),
890       _nodesFrame(_("Nodes")),
891       _alignTable(2, 6, true),
892       _distributeTable(2, 6, true),
893       _rearrangeTable(1, 5, false),
894       _removeOverlapTable(1, 5, false),
895       _nodesTable(1, 4, true),
896       _anchorLabel(_("Relative to: ")),
897       _selgrpLabel(_("Treat selection as group: "))
899     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
901     //Instanciate the align buttons
902     addAlignButton(INKSCAPE_ICON_ALIGN_HORIZONTAL_RIGHT_TO_ANCHOR,
903                    _("Align right edges of objects to the left edge of the anchor"),
904                    0, 0);
905     addAlignButton(INKSCAPE_ICON_ALIGN_HORIZONTAL_LEFT,
906                    _("Align left edges"),
907                    0, 1);
908     addAlignButton(INKSCAPE_ICON_ALIGN_HORIZONTAL_CENTER,
909                    _("Center on vertical axis"),
910                    0, 2);
911     addAlignButton(INKSCAPE_ICON_ALIGN_HORIZONTAL_RIGHT,
912                    _("Align right sides"),
913                    0, 3);
914     addAlignButton(INKSCAPE_ICON_ALIGN_HORIZONTAL_LEFT_TO_ANCHOR,
915                    _("Align left edges of objects to the right edge of the anchor"),
916                    0, 4);
917     addAlignButton(INKSCAPE_ICON_ALIGN_VERTICAL_BOTTOM_TO_ANCHOR,
918                    _("Align bottom edges of objects to the top edge of the anchor"),
919                    1, 0);
920     addAlignButton(INKSCAPE_ICON_ALIGN_VERTICAL_TOP,
921                    _("Align top edges"),
922                    1, 1);
923     addAlignButton(INKSCAPE_ICON_ALIGN_VERTICAL_CENTER,
924                    _("Center on horizontal axis"),
925                    1, 2);
926     addAlignButton(INKSCAPE_ICON_ALIGN_VERTICAL_BOTTOM,
927                    _("Align bottom edges"),
928                    1, 3);
929     addAlignButton(INKSCAPE_ICON_ALIGN_VERTICAL_TOP_TO_ANCHOR,
930                    _("Align top edges of objects to the bottom edge of the anchor"),
931                    1, 4);
933     //Baseline aligns
934     addBaselineButton(INKSCAPE_ICON_ALIGN_HORIZONTAL_BASELINE,
935                    _("Align baseline anchors of texts horizontally"),
936                       0, 5, this->align_table(), Geom::X, false);
937     addBaselineButton(INKSCAPE_ICON_ALIGN_VERTICAL_BASELINE,
938                    _("Align baselines of texts"),
939                      1, 5, this->align_table(), Geom::Y, false);
941     //The distribute buttons
942     addDistributeButton(INKSCAPE_ICON_DISTRIBUTE_HORIZONTAL_GAPS,
943                         _("Make horizontal gaps between objects equal"),
944                         0, 4, true, Geom::X, .5, .5);
946     addDistributeButton(INKSCAPE_ICON_DISTRIBUTE_HORIZONTAL_LEFT,
947                         _("Distribute left edges equidistantly"),
948                         0, 1, false, Geom::X, 1., 0.);
949     addDistributeButton(INKSCAPE_ICON_DISTRIBUTE_HORIZONTAL_CENTER,
950                         _("Distribute centers equidistantly horizontally"),
951                         0, 2, false, Geom::X, .5, .5);
952     addDistributeButton(INKSCAPE_ICON_DISTRIBUTE_HORIZONTAL_RIGHT,
953                         _("Distribute right edges equidistantly"),
954                         0, 3, false, Geom::X, 0., 1.);
956     addDistributeButton(INKSCAPE_ICON_DISTRIBUTE_VERTICAL_GAPS,
957                         _("Make vertical gaps between objects equal"),
958                         1, 4, true, Geom::Y, .5, .5);
960     addDistributeButton(INKSCAPE_ICON_DISTRIBUTE_VERTICAL_TOP,
961                         _("Distribute top edges equidistantly"),
962                         1, 1, false, Geom::Y, 0, 1);
963     addDistributeButton(INKSCAPE_ICON_DISTRIBUTE_VERTICAL_CENTER,
964                         _("Distribute centers equidistantly vertically"),
965                         1, 2, false, Geom::Y, .5, .5);
966     addDistributeButton(INKSCAPE_ICON_DISTRIBUTE_VERTICAL_BOTTOM,
967                         _("Distribute bottom edges equidistantly"),
968                         1, 3, false, Geom::Y, 1., 0.);
970     //Baseline distribs
971     addBaselineButton(INKSCAPE_ICON_DISTRIBUTE_HORIZONTAL_BASELINE,
972                    _("Distribute baseline anchors of texts horizontally"),
973                       0, 5, this->distribute_table(), Geom::X, true);
974     addBaselineButton(INKSCAPE_ICON_DISTRIBUTE_VERTICAL_BASELINE,
975                    _("Distribute baselines of texts vertically"),
976                      1, 5, this->distribute_table(), Geom::Y, true);
978     // Rearrange
979     //Graph Layout
980     addGraphLayoutButton(INKSCAPE_ICON_DISTRIBUTE_GRAPH,
981                             _("Nicely arrange selected connector network"),
982                             0, 0);
983     addExchangePositionsButton(INKSCAPE_ICON_EXCHANGE_POSITIONS,
984                             _("Exchange positions of selected objects - selection order"),
985                             0, 1);
986     addExchangePositionsByZOrderButton(INKSCAPE_ICON_EXCHANGE_POSITIONS_ZORDER,
987                             _("Exchange positions of selected objects - stacking order"),
988                             0, 2);
989     addExchangePositionsClockwiseButton(INKSCAPE_ICON_EXCHANGE_POSITIONS_CLOCKWISE,
990                             _("Exchange positions of selected objects - clockwise rotate"),
991                             0, 3);
992                             
993     //Randomize & Unclump
994     addRandomizeButton(INKSCAPE_ICON_DISTRIBUTE_RANDOMIZE,
995                         _("Randomize centers in both dimensions"),
996                         0, 4);
997     addUnclumpButton(INKSCAPE_ICON_DISTRIBUTE_UNCLUMP,
998                         _("Unclump objects: try to equalize edge-to-edge distances"),
999                         0, 5);
1001     //Remove overlaps
1002     addRemoveOverlapsButton(INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
1003                             _("Move objects as little as possible so that their bounding boxes do not overlap"),
1004                             0, 0);
1006     //Node Mode buttons
1007     // NOTE: "align nodes vertically" means "move nodes vertically until they align on a common
1008     // _horizontal_ line". This is analogous to what the "align-vertical-center" icon means.
1009     // There is no doubt some ambiguity. For this reason the descriptions are different.
1010     addNodeButton(INKSCAPE_ICON_ALIGN_VERTICAL_NODES,
1011                   _("Align selected nodes to a common horizontal line"),
1012                   0, Geom::X, false);
1013     addNodeButton(INKSCAPE_ICON_ALIGN_HORIZONTAL_NODES,
1014                   _("Align selected nodes to a common vertical line"),
1015                   1, Geom::Y, false);
1016     addNodeButton(INKSCAPE_ICON_DISTRIBUTE_HORIZONTAL_NODE,
1017                   _("Distribute selected nodes horizontally"),
1018                   2, Geom::X, true);
1019     addNodeButton(INKSCAPE_ICON_DISTRIBUTE_VERTICAL_NODE,
1020                   _("Distribute selected nodes vertically"),
1021                   3, Geom::Y, true);
1023     //Rest of the widgetry
1025     _combo.append_text(_("Last selected"));
1026     _combo.append_text(_("First selected"));
1027     _combo.append_text(_("Biggest object"));
1028     _combo.append_text(_("Smallest object"));
1029     _combo.append_text(_("Page"));
1030     _combo.append_text(_("Drawing"));
1031     _combo.append_text(_("Selection"));
1033     _combo.set_active(prefs->getInt("/dialogs/align/align-to", 6));
1034     _combo.signal_changed().connect(sigc::mem_fun(*this, &AlignAndDistribute::on_ref_change));
1036     _anchorBox.pack_start(_anchorLabel);
1037     _anchorBox.pack_start(_combo);
1039     _selgrpBox.pack_start(_selgrpLabel);
1040     _selgrpBox.pack_start(_selgrp);
1041     _selgrp.set_active(prefs->getBool("/dialogs/align/sel-as-groups"));
1042     _selgrp.signal_toggled().connect(sigc::mem_fun(*this, &AlignAndDistribute::on_selgrp_toggled));
1044     _alignBox.pack_start(_anchorBox);
1045     _alignBox.pack_start(_selgrpBox);
1046     _alignBox.pack_start(_alignTable);
1048     _alignFrame.add(_alignBox);
1049     _distributeFrame.add(_distributeTable);
1050     _rearrangeFrame.add(_rearrangeTable);
1051     _removeOverlapFrame.add(_removeOverlapTable);
1052     _nodesFrame.add(_nodesTable);
1054     Gtk::Box *contents = _getContents();
1055     contents->set_spacing(4);
1057     // Notebook for individual transformations
1059     contents->pack_start(_alignFrame, true, true);
1060     contents->pack_start(_distributeFrame, true, true);
1061     contents->pack_start(_rearrangeFrame, true, true);
1062     contents->pack_start(_removeOverlapFrame, true, true);
1063     contents->pack_start(_nodesFrame, true, true);
1065     //Connect to the global tool change signal
1066     g_signal_connect (G_OBJECT (INKSCAPE), "set_eventcontext", G_CALLBACK (on_tool_changed), this);
1068     // Connect to the global selection change, to invalidate cached randomize_bbox
1069     g_signal_connect (G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (on_selection_changed), this);
1070     randomize_bbox = Geom::OptRect();
1072     show_all_children();
1074     on_tool_changed (NULL, NULL, this); // set current mode
1077 AlignAndDistribute::~AlignAndDistribute()
1079     sp_signal_disconnect_by_data (G_OBJECT (INKSCAPE), this);
1081     for (std::list<Action *>::iterator it = _actionList.begin();
1082          it != _actionList.end();
1083          it ++)
1084         delete *it;
1087 void AlignAndDistribute::on_ref_change(){
1088     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1089     prefs->setInt("/dialogs/align/align-to", _combo.get_active_row_number());
1091     //Make blink the master
1094 void AlignAndDistribute::on_selgrp_toggled(){
1095     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1096     prefs->setInt("/dialogs/align/sel-as-groups", _selgrp.get_active());
1098     //Make blink the master
1104 void AlignAndDistribute::setMode(bool nodeEdit)
1106     //Act on widgets used in node mode
1107     void ( Gtk::Widget::*mNode) ()  = nodeEdit ?
1108         &Gtk::Widget::show_all : &Gtk::Widget::hide_all;
1110     //Act on widgets used in selection mode
1111   void ( Gtk::Widget::*mSel) ()  = nodeEdit ?
1112       &Gtk::Widget::hide_all : &Gtk::Widget::show_all;
1115     ((_alignFrame).*(mSel))();
1116     ((_distributeFrame).*(mSel))();
1117     ((_rearrangeFrame).*(mSel))();
1118     ((_removeOverlapFrame).*(mSel))();
1119     ((_nodesFrame).*(mNode))();
1122 void AlignAndDistribute::addAlignButton(const Glib::ustring &id, const Glib::ustring tiptext,
1123                                  guint row, guint col)
1125     _actionList.push_back(
1126         new ActionAlign(
1127             id, tiptext, row, col,
1128             *this , col + row * 5));
1130 void AlignAndDistribute::addDistributeButton(const Glib::ustring &id, const Glib::ustring tiptext,
1131                                       guint row, guint col, bool onInterSpace,
1132                                       Geom::Dim2 orientation, float kBegin, float kEnd)
1134     _actionList.push_back(
1135         new ActionDistribute(
1136             id, tiptext, row, col, *this ,
1137             onInterSpace, orientation,
1138             kBegin, kEnd
1139             )
1140         );
1143 void AlignAndDistribute::addNodeButton(const Glib::ustring &id, const Glib::ustring tiptext,
1144                    guint col, Geom::Dim2 orientation, bool distribute)
1146     _actionList.push_back(
1147         new ActionNode(
1148             id, tiptext, col,
1149             *this, orientation, distribute));
1152 void AlignAndDistribute::addRemoveOverlapsButton(const Glib::ustring &id, const Glib::ustring tiptext,
1153                                       guint row, guint col)
1155     _actionList.push_back(
1156         new ActionRemoveOverlaps(
1157             id, tiptext, row, col, *this)
1158         );
1161 void AlignAndDistribute::addGraphLayoutButton(const Glib::ustring &id, const Glib::ustring tiptext,
1162                                       guint row, guint col)
1164     _actionList.push_back(
1165         new ActionGraphLayout(
1166             id, tiptext, row, col, *this)
1167         );
1170 void AlignAndDistribute::addExchangePositionsButton(const Glib::ustring &id, const Glib::ustring tiptext,
1171                                       guint row, guint col)
1173     _actionList.push_back(
1174         new ActionExchangePositions(
1175             id, tiptext, row, col, *this)
1176         );
1179 void AlignAndDistribute::addExchangePositionsByZOrderButton(const Glib::ustring &id, const Glib::ustring tiptext,
1180                                       guint row, guint col)
1182     _actionList.push_back(
1183         new ActionExchangePositions(
1184             id, tiptext, row, col, *this, ActionExchangePositions::ZOrder)
1185         );
1188 void AlignAndDistribute::addExchangePositionsClockwiseButton(const Glib::ustring &id, const Glib::ustring tiptext,
1189                                       guint row, guint col)
1191     _actionList.push_back(
1192         new ActionExchangePositions(
1193             id, tiptext, row, col, *this, ActionExchangePositions::Clockwise)
1194         );
1197 void AlignAndDistribute::addUnclumpButton(const Glib::ustring &id, const Glib::ustring tiptext,
1198                                       guint row, guint col)
1200     _actionList.push_back(
1201         new ActionUnclump(
1202             id, tiptext, row, col, *this)
1203         );
1206 void AlignAndDistribute::addRandomizeButton(const Glib::ustring &id, const Glib::ustring tiptext,
1207                                       guint row, guint col)
1209     _actionList.push_back(
1210         new ActionRandomize(
1211             id, tiptext, row, col, *this)
1212         );
1215 void AlignAndDistribute::addBaselineButton(const Glib::ustring &id, const Glib::ustring tiptext,
1216                                     guint row, guint col, Gtk::Table &table, Geom::Dim2 orientation, bool distribute)
1218     _actionList.push_back(
1219         new ActionBaseline(
1220             id, tiptext, row, col,
1221             *this, table, orientation, distribute));
1227 std::list<SPItem *>::iterator AlignAndDistribute::find_master( std::list<SPItem *> &list, bool horizontal){
1228     std::list<SPItem *>::iterator master = list.end();
1229     switch (getAlignTarget()) {
1230     case LAST:
1231         return list.begin();
1232         break;
1234     case FIRST:
1235         return --(list.end());
1236         break;
1238     case BIGGEST:
1239     {
1240         gdouble max = -1e18;
1241         for (std::list<SPItem *>::iterator it = list.begin(); it != list.end(); it++) {
1242             Geom::OptRect b = sp_item_bbox_desktop (*it);
1243             if (b) {
1244                 gdouble dim = (*b)[horizontal ? Geom::X : Geom::Y].extent();
1245                 if (dim > max) {
1246                     max = dim;
1247                     master = it;
1248                 }
1249             }
1250         }
1251         return master;
1252         break;
1253     }
1255     case SMALLEST:
1256     {
1257         gdouble max = 1e18;
1258         for (std::list<SPItem *>::iterator it = list.begin(); it != list.end(); it++) {
1259             Geom::OptRect b = sp_item_bbox_desktop (*it);
1260             if (b) {
1261                 gdouble dim = (*b)[horizontal ? Geom::X : Geom::Y].extent();
1262                 if (dim < max) {
1263                     max = dim;
1264                     master = it;
1265                 }
1266             }
1267         }
1268         return master;
1269         break;
1270     }
1272     default:
1273         g_assert_not_reached ();
1274         break;
1276     } // end of switch statement
1277     return master;
1280 AlignAndDistribute::AlignTarget AlignAndDistribute::getAlignTarget()const {
1281     return AlignTarget(_combo.get_active_row_number());
1286 } // namespace Dialog
1287 } // namespace UI
1288 } // namespace Inkscape
1290 /*
1291   Local Variables:
1292   mode:c++
1293   c-file-style:"stroustrup"
1294   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1295   indent-tabs-mode:nil
1296   fill-column:99
1297   End:
1298 */
1299 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :