Code

Merge from trunk
[inkscape.git] / src / dialogs / tiledialog.cpp
1 /*
2  * A simple dialog for creating grid type arrangements of selected objects
3  *
4  * Authors:
5  *   Bob Jamison ( based off trace dialog)
6  *   John Cliff
7  *   Other dudes from The Inkscape Organization
8  *
9  * Copyright (C) 2004 Bob Jamison
10  * Copyright (C) 2004 John Cliff
11  *
12  * Released under GNU GPL, read the file 'COPYING' for more information
13  */
14 //#define DEBUG_GRID_ARRANGE 1
16 #ifdef HAVE_CONFIG_H
17 # include <config.h>
18 #endif
21 #include <gtk/gtkdialog.h> //for GTK_RESPONSE* types
22 #include <gtk/gtksizegroup.h>
23 #include <glibmm/i18n.h>
24 #include <gtkmm/stock.h>
26 #include "verbs.h"
27 #include "preferences.h"
28 #include "inkscape.h"
29 #include "desktop-handles.h"
30 #include "selection.h"
31 #include "document.h"
32 #include "sp-item.h"
33 #include "widgets/icon.h"
34 #include "tiledialog.h"
38 /*
39  *    Sort items by their x co-ordinates, taking account of y (keeps rows intact)
40  *
41  *    <0 *elem1 goes before *elem2
42  *    0  *elem1 == *elem2
43  *    >0  *elem1 goes after *elem2
44  */
45 int
46 sp_compare_x_position(SPItem *first, SPItem *second)
47 {
48     using Geom::X;
49     using Geom::Y;
51     boost::optional<Geom::Rect> a = first->getBounds(sp_item_i2doc_affine(first));
52     boost::optional<Geom::Rect> b = second->getBounds(sp_item_i2doc_affine(second));
54     if ( !a || !b ) {
55         // FIXME?
56         return 0;
57     }
59     double const a_height = a->dimensions()[Y];
60     double const b_height = b->dimensions()[Y];
61     
62     bool a_in_b_vert = false;
63     if ((a->min()[Y] < b->min()[Y] + 0.1) && (a->min()[Y] > b->min()[Y] - b_height)) {
64         a_in_b_vert = true;
65     } else if ((b->min()[Y] < a->min()[Y] + 0.1) && (b->min()[Y] > a->min()[Y] - a_height)) {
66         a_in_b_vert = true;
67     } else if (b->min()[Y] == a->min()[Y]) {
68         a_in_b_vert = true;
69     } else {
70         a_in_b_vert = false;
71     }
73     if (!a_in_b_vert) {
74         return -1;
75     }
76     if (a_in_b_vert && a->min()[X] > b->min()[X]) {
77         return 1;
78     }
79     if (a_in_b_vert && a->min()[X] < b->min()[X]) {
80         return -1;
81     }
82     return 0;
83 }
85 /*
86  *    Sort items by their y co-ordinates.
87  */
88 int
89 sp_compare_y_position(SPItem *first, SPItem *second)
90 {
91     boost::optional<Geom::Rect> a = first->getBounds(sp_item_i2doc_affine(first));
92     boost::optional<Geom::Rect> b = second->getBounds(sp_item_i2doc_affine(second));
94     if ( !a || !b ) {
95         // FIXME?
96         return 0;
97     }
99     if (a->min()[Geom::Y] > b->min()[Geom::Y]) {
100         return 1;
101     }
102     if (a->min()[Geom::Y] < b->min()[Geom::Y]) {
103         return -1;
104     }
105     
106     return 0;
109 namespace Inkscape {
110 namespace UI {
111 namespace Dialog {
114 //#########################################################################
115 //## E V E N T S
116 //#########################################################################
118 /*
119  *
120  * This arranges the selection in a grid pattern.
121  *
122  */
124 void TileDialog::Grid_Arrange ()
127     int cnt,row_cnt,col_cnt,a,row,col;
128     double grid_left,grid_top,col_width,row_height,paddingx,paddingy,width, height, new_x, new_y,cx,cy;
129     double total_col_width,total_row_height;
130     col_width = 0;
131     row_height = 0;
132     total_col_width=0;
133     total_row_height=0;
135     // check for correct numbers in the row- and col-spinners
136     on_col_spinbutton_changed();
137     on_row_spinbutton_changed();
139     // set padding to manual values
140     paddingx = XPadSpinner.get_value();
141     paddingy = YPadSpinner.get_value();
143     std::vector<double> row_heights;
144     std::vector<double> col_widths;
145     std::vector<double> row_ys;
146     std::vector<double> col_xs;
148     int NoOfCols = NoOfColsSpinner.get_value_as_int();
149     int NoOfRows = NoOfRowsSpinner.get_value_as_int();
151     width = 0;
152     for (a=0;a<NoOfCols; a++){
153         col_widths.push_back(width);
154     }
156     height = 0;
157     for (a=0;a<NoOfRows; a++){
158         row_heights.push_back(height);
159     }
160     grid_left = 99999;
161     grid_top = 99999;
163     SPDesktop *desktop = getDesktop();
164     sp_document_ensure_up_to_date(sp_desktop_document(desktop));
166     Inkscape::Selection *selection = sp_desktop_selection (desktop);
167     const GSList *items = selection->itemList();
168     cnt=0;
169     for (; items != NULL; items = items->next) {
170         SPItem *item = SP_ITEM(items->data);
171         boost::optional<Geom::Rect> b = item->getBounds(sp_item_i2doc_affine(item));
172         if (!b) {
173             continue;
174         }
176         width = b->dimensions()[Geom::X];
177         height = b->dimensions()[Geom::Y];
179         cx = b->midpoint()[Geom::X];
180         cy = b->midpoint()[Geom::Y];
182         if (b->min()[Geom::X] < grid_left) {
183             grid_left = b->min()[Geom::X];
184         }
185         if (b->min()[Geom::Y] < grid_top) {
186             grid_top = b->min()[Geom::Y];
187         }
188         if (width > col_width) {
189             col_width = width;
190         }
191         if (height > row_height) {
192             row_height = height;
193         }
194     }
197     // require the sorting done before we can calculate row heights etc.
199     const GSList *items2 = selection->itemList();
200     GSList *rev = g_slist_copy((GSList *) items2);
201     GSList *sorted = NULL;
202     rev = g_slist_sort(rev, (GCompareFunc) sp_compare_y_position);
203     sorted = g_slist_sort(rev, (GCompareFunc) sp_compare_x_position);
206     // Calculate individual Row and Column sizes if necessary
209         cnt=0;
210         const GSList *sizes = sorted;
211         for (; sizes != NULL; sizes = sizes->next) {
212             SPItem *item = SP_ITEM(sizes->data);
213             boost::optional<Geom::Rect> b = item->getBounds(sp_item_i2doc_affine(item));
214             if (b) {
215                 width = b->dimensions()[Geom::X];
216                 height = b->dimensions()[Geom::Y];
217                 if (width > col_widths[(cnt % NoOfCols)]) {
218                     col_widths[(cnt % NoOfCols)] = width;
219                 }
220                 if (height > row_heights[(cnt / NoOfCols)]) {
221                     row_heights[(cnt / NoOfCols)] = height;
222                 }
223             }
225             cnt++;
226         }
229     /// Make sure the top and left of the grid dont move by compensating for align values.
230     if (RowHeightButton.get_active()){
231         grid_top = grid_top - (((row_height - row_heights[0]) / 2)*(VertAlign));
232     }
233     if (ColumnWidthButton.get_active()){
234         grid_left = grid_left - (((col_width - col_widths[0]) /2)*(HorizAlign));
235     }
237     #ifdef DEBUG_GRID_ARRANGE
238      g_print("\n cx = %f cy= %f gridleft=%f",cx,cy,grid_left);
239     #endif
241     // Calculate total widths and heights, allowing for columns and rows non uniformly sized.
243     if (ColumnWidthButton.get_active()){
244         total_col_width = col_width * NoOfCols;
245         col_widths.clear();
246         for (a=0;a<NoOfCols; a++){
247             col_widths.push_back(col_width);
248         }
249     } else {
250         for (a = 0; a < (int)col_widths.size(); a++)
251         {
252           total_col_width += col_widths[a] ;
253         }
254     }
256     if (RowHeightButton.get_active()){
257         total_row_height = row_height * NoOfRows;
258         row_heights.clear();
259         for (a=0;a<NoOfRows; a++){
260             row_heights.push_back(row_height);
261         }
262     } else {
263         for (a = 0; a < (int)row_heights.size(); a++)
264         {
265           total_row_height += row_heights[a] ;
266         }
267     }
270     boost::optional<Geom::Rect> sel_bbox = selection->bounds();
271     // Fit to bbox, calculate padding between rows accordingly.
272     if ( sel_bbox && !SpaceManualRadioButton.get_active() ){
273 #ifdef DEBUG_GRID_ARRANGE
274 g_print("\n row = %f     col = %f selection x= %f selection y = %f", total_row_height,total_col_width, b.extent(Geom::X), b.extent(Geom::Y));
275 #endif
276         paddingx = (sel_bbox->width() - total_col_width) / (NoOfCols -1);
277         paddingy = (sel_bbox->height() - total_row_height) / (NoOfRows -1);
278     }
280 /*
281     Horizontal align  - Left    = 0
282                         Centre  = 1
283                         Right   = 2
285     Vertical align    - Top     = 0
286                         Middle  = 1
287                         Bottom  = 2
289     X position is calculated by taking the grids left co-ord, adding the distance to the column,
290    then adding 1/2 the spacing multiplied by the align variable above,
291    Y position likewise, takes the top of the grid, adds the y to the current row then adds the padding in to align it.
293 */
295     // Calculate row and column x and y coords required to allow for columns and rows which are non uniformly sized.
297     for (a=0;a<NoOfCols; a++){
298         if (a<1) col_xs.push_back(0);
299         else col_xs.push_back(col_widths[a-1]+paddingx+col_xs[a-1]);
300     }
303     for (a=0;a<NoOfRows; a++){
304         if (a<1) row_ys.push_back(0);
305         else row_ys.push_back(row_heights[a-1]+paddingy+row_ys[a-1]);
306     }
308     cnt=0;
309   for (row_cnt=0; ((sorted != NULL) && (row_cnt<NoOfRows)); row_cnt++) {
311              GSList *current_row = NULL;
312              for (col_cnt = 0; ((sorted != NULL) && (col_cnt<NoOfCols)); col_cnt++) {
313                  current_row = g_slist_append (current_row, sorted->data);
314                  sorted = sorted->next;
315              }
317              for (; current_row != NULL; current_row = current_row->next) {
318                  SPItem *item=SP_ITEM(current_row->data);
319                  Inkscape::XML::Node *repr = SP_OBJECT_REPR(item);
320                  boost::optional<Geom::Rect> b = item->getBounds(sp_item_i2doc_affine(item));
321                  Geom::Point min;
322                  if (b) {
323                      width = b->dimensions()[Geom::X];
324                      height = b->dimensions()[Geom::Y];
325                      min = b->min();
326                  } else {
327                      width = height = 0;
328                      min = Geom::Point(0, 0);
329                  }
331                  row = cnt / NoOfCols;
332                  col = cnt % NoOfCols;
334                  new_x = grid_left + (((col_widths[col] - width)/2)*HorizAlign) + col_xs[col];
335                  new_y = grid_top + (((row_heights[row] - height)/2)*VertAlign) + row_ys[row];
337                  // signs are inverted between x and y due to y inversion
338                  Geom::Point move = Geom::Point(new_x - min[Geom::X], min[Geom::Y] - new_y);
339                  Geom::Matrix const affine = Geom::Matrix(Geom::Translate(move));
340                  sp_item_set_i2d_affine(item, sp_item_i2d_affine(item) * affine);
341                  sp_item_write_transform(item, repr, item->transform,  NULL);
342                  SP_OBJECT (current_row->data)->updateRepr();
343                  cnt +=1;
344              }
345              g_slist_free (current_row);
346     }
348     sp_document_done (sp_desktop_document (desktop), SP_VERB_SELECTION_GRIDTILE, 
349                       _("Arrange in a grid"));
354 //#########################################################################
355 //## E V E N T S
356 //#########################################################################
359 void TileDialog::_apply()
361         Grid_Arrange();
365 /**
366  * changed value in # of columns spinbox.
367  */
368 void TileDialog::on_row_spinbutton_changed()
370     // quit if run by the attr_changed listener
371     if (updating) {
372             return;
373         }
375     // in turn, prevent listener from responding
376     updating = true;
377     SPDesktop *desktop = getDesktop();
379     Inkscape::Selection *selection = sp_desktop_selection (desktop);
381     GSList const *items = selection->itemList();
382     int selcount = g_slist_length((GSList *)items);
384     double PerCol = ceil(selcount / NoOfColsSpinner.get_value());
385     NoOfRowsSpinner.set_value(PerCol);
386     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
387     prefs->setDouble("/dialogs/gridtiler/NoOfCols", NoOfColsSpinner.get_value());
388     updating=false;
391 /**
392  * changed value in # of rows spinbox.
393  */
394 void TileDialog::on_col_spinbutton_changed()
396     // quit if run by the attr_changed listener
397     if (updating) {
398             return;
399         }
401     // in turn, prevent listener from responding
402     updating = true;
403     SPDesktop *desktop = getDesktop();
404     Inkscape::Selection *selection = sp_desktop_selection (desktop);
406     GSList const *items = selection->itemList();
407     int selcount = g_slist_length((GSList *)items);
409     double PerRow = ceil(selcount / NoOfRowsSpinner.get_value());
410     NoOfColsSpinner.set_value(PerRow);
411     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
412     prefs->setDouble("/dialogs/gridtiler/NoOfCols", PerRow);
414     updating=false;
417 /**
418  * changed value in x padding spinbox.
419  */
420 void TileDialog::on_xpad_spinbutton_changed()
422     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
423     prefs->setDouble("/dialogs/gridtiler/XPad", XPadSpinner.get_value());
427 /**
428  * changed value in y padding spinbox.
429  */
430 void TileDialog::on_ypad_spinbutton_changed()
432     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
433     prefs->setDouble("/dialogs/gridtiler/YPad", YPadSpinner.get_value());
437 /**
438  * checked/unchecked autosize Rows button.
439  */
440 void TileDialog::on_RowSize_checkbutton_changed()
442     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
443     if (RowHeightButton.get_active()) {
444         prefs->setDouble("/dialogs/gridtiler/AutoRowSize", 20);
445     } else {
446         prefs->setDouble("/dialogs/gridtiler/AutoRowSize", -20);
447     }
448     RowHeightBox.set_sensitive ( !RowHeightButton.get_active());
451 /**
452  * checked/unchecked autosize Rows button.
453  */
454 void TileDialog::on_ColSize_checkbutton_changed()
456     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
457     if (ColumnWidthButton.get_active()) {
458         prefs->setDouble("/dialogs/gridtiler/AutoColSize", 20);
459     } else {
460         prefs->setDouble("/dialogs/gridtiler/AutoColSize", -20);
461     }
462     ColumnWidthBox.set_sensitive ( !ColumnWidthButton.get_active());
465 /**
466  * changed value in columns spinbox.
467  */
468 void TileDialog::on_rowSize_spinbutton_changed()
470     // quit if run by the attr_changed listener
471     if (updating) {
472             return;
473         }
475     // in turn, prevent listener from responding
476     updating = true;
477     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
478     prefs->setDouble("/dialogs/gridtiler/RowHeight", RowHeightSpinner.get_value());
479     updating=false;
483 /**
484  * changed value in rows spinbox.
485  */
486 void TileDialog::on_colSize_spinbutton_changed()
488     // quit if run by the attr_changed listener
489     if (updating) {
490             return;
491         }
493     // in turn, prevent listener from responding
494     updating = true;
495     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
496     prefs->setDouble("/dialogs/gridtiler/ColWidth", ColumnWidthSpinner.get_value());
497     updating=false;
501 /**
502  * changed Radio button in Spacing group.
503  */
504 void TileDialog::Spacing_button_changed()
506     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
507     if (SpaceManualRadioButton.get_active()) {
508         prefs->setDouble("/dialogs/gridtiler/SpacingType", 20);
509     } else {
510         prefs->setDouble("/dialogs/gridtiler/SpacingType", -20);
511     }
513     SizesHBox.set_sensitive ( SpaceManualRadioButton.get_active());
516 /**
517  * changed Radio button in Vertical Align group.
518  */
519 void TileDialog::VertAlign_changed()
521     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
522     if (VertTopRadioButton.get_active()) {
523         VertAlign = 0;
524         prefs->setInt("/dialogs/gridtiler/VertAlign", 0);
525     } else if (VertCentreRadioButton.get_active()){
526         VertAlign = 1;
527         prefs->setInt("/dialogs/gridtiler/VertAlign", 1);
528     } else if (VertBotRadioButton.get_active()){
529         VertAlign = 2;
530         prefs->setInt("/dialogs/gridtiler/VertAlign", 2);
531     }
534 /**
535  * changed Radio button in Vertical Align group.
536  */
537 void TileDialog::HorizAlign_changed()
539     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
540     if (HorizLeftRadioButton.get_active()) {
541         HorizAlign = 0;
542         prefs->setInt("/dialogs/gridtiler/HorizAlign", 0);
543     } else if (HorizCentreRadioButton.get_active()){
544         HorizAlign = 1;
545         prefs->setInt("/dialogs/gridtiler/HorizAlign", 1);
546     } else if (HorizRightRadioButton.get_active()){
547         HorizAlign = 2;
548         prefs->setInt("/dialogs/gridtiler/HorizAlign", 2);
549     }
552 /**
553  * Desktop selection changed
554  */
555 void TileDialog::updateSelection()
557     // quit if run by the attr_changed listener
558     if (updating) {
559         return;
560     }
562     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
563     double col_width = 0;
564     double row_height = 0;
565     // in turn, prevent listener from responding
566     updating = true;
567     SPDesktop *desktop = getDesktop();
568     Inkscape::Selection *selection = sp_desktop_selection (desktop);
569     const GSList *items = selection->itemList();
570     int selcount = g_slist_length((GSList *)items);
572     if (NoOfColsSpinner.get_value()>1 && NoOfRowsSpinner.get_value()>1){
573         // Update the number of rows assuming number of columns wanted remains same.
574         double NoOfRows = ceil(selcount / NoOfColsSpinner.get_value());
575         NoOfRowsSpinner.set_value(NoOfRows);
577         // if the selection has less than the number set for one row, reduce it appropriately
578         if (selcount<NoOfColsSpinner.get_value()) {
579             double NoOfCols = ceil(selcount / NoOfRowsSpinner.get_value());
580             NoOfColsSpinner.set_value(NoOfCols);
581             prefs->setInt("/dialogs/gridtiler/NoOfCols", NoOfCols);
582         }
583     } else {
584         double PerRow = ceil(sqrt(selcount));
585         double PerCol = ceil(sqrt(selcount));
586         NoOfRowsSpinner.set_value(PerRow);
587         NoOfColsSpinner.set_value(PerCol);
588         prefs->setInt("/dialogs/gridtiler/NoOfCols", static_cast<int>(PerCol));
590     }
592     updating=false;
598 /*##########################
599 ## Experimental
600 ##########################*/
602 static void updateSelectionCallback(Inkscape::Application */*inkscape*/, Inkscape::Selection */*selection*/, TileDialog *dlg)
604     TileDialog *tledlg = (TileDialog *) dlg;
605     tledlg->updateSelection();
609 //#########################################################################
610 //## C O N S T R U C T O R    /    D E S T R U C T O R
611 //#########################################################################
612 /**
613  * Constructor
614  */
615 TileDialog::TileDialog()
616     : UI::Widget::Panel("", "/dialogs/gridtiler", SP_VERB_SELECTION_GRIDTILE)
618      // bool used by spin button callbacks to stop loops where they change each other.
619     updating = false;
620     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
622     // could not do this in gtkmm - there's no Gtk::SizeGroup public constructor (!)
623     GtkSizeGroup *_col1 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
624     GtkSizeGroup *_col2 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
625     GtkSizeGroup *_col3 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
627     {
628         // Selection Change signal
629         g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (updateSelectionCallback), this);
630     }
632     Gtk::Box *contents = _getContents();
634 #define MARGIN 2
636     //##Set up the panel
638     SPDesktop *desktop = getDesktop();
640     Inkscape::Selection *selection = sp_desktop_selection (desktop);
641     int selcount = 1;
642     if (!selection->isEmpty()) {
643         GSList const *items = selection->itemList();
644         selcount = g_slist_length((GSList *)items);
645     }
648     /*#### Number of Rows ####*/
650     double PerRow = ceil(sqrt(selcount));
651     double PerCol = ceil(sqrt(selcount));
653     #ifdef DEBUG_GRID_ARRANGE
654         g_print("/n PerRox = %f PerCol = %f selcount = %d",PerRow,PerCol,selcount);
655     #endif
657     NoOfRowsLabel.set_label(_("Rows:"));
658     NoOfRowsBox.pack_start(NoOfRowsLabel, false, false, MARGIN);
660     NoOfRowsSpinner.set_digits(0);
661     NoOfRowsSpinner.set_increments(1, 5);
662     NoOfRowsSpinner.set_range(1.0, 100.0);
663     NoOfRowsSpinner.set_value(PerCol);
664     NoOfRowsSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_col_spinbutton_changed));
665     tips.set_tip(NoOfRowsSpinner, _("Number of rows"));
666     NoOfRowsBox.pack_start(NoOfRowsSpinner, false, false, MARGIN);
667     gtk_size_group_add_widget(_col1, (GtkWidget *) NoOfRowsBox.gobj());
669     RowHeightButton.set_label(_("Equal height"));
670     double AutoRow = prefs->getDouble("/dialogs/gridtiler/AutoRowSize", 15);
671     if (AutoRow>0)
672          AutoRowSize=true;
673     else
674          AutoRowSize=false;
675     RowHeightButton.set_active(AutoRowSize);
677     NoOfRowsBox.pack_start(RowHeightButton, false, false, MARGIN);
679     tips.set_tip(RowHeightButton, _("If not set, each row has the height of the tallest object in it"));
680     RowHeightButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::on_RowSize_checkbutton_changed));
682  {
683         /*#### Radio buttons to control vertical alignment ####*/
685         VertAlignLabel.set_label(_("Align:"));
686         VertAlignHBox.pack_start(VertAlignLabel, false, false, MARGIN);
688         VertTopRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
689         VertAlignGroup = VertTopRadioButton.get_group();
690         VertAlignVBox.pack_start(VertTopRadioButton, false, false, 0);
692         VertCentreRadioButton.set_group(VertAlignGroup);
693         VertCentreRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
694         VertAlignVBox.pack_start(VertCentreRadioButton, false, false, 0);
696         VertBotRadioButton.set_group(VertAlignGroup);
697         VertBotRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
698         VertAlignVBox.pack_start(VertBotRadioButton, false, false, 0);
700         VertAlign = prefs->getInt("/dialogs/gridtiler/VertAlign", 1);
701         if (VertAlign == 0) {
702             VertTopRadioButton.set_active(TRUE);
703         }
704         else if (VertAlign == 1) {
705             VertCentreRadioButton.set_active(TRUE);
706         }
707         else if (VertAlign == 2){
708             VertBotRadioButton.set_active(TRUE);
709         }
710         VertAlignHBox.pack_start(VertAlignVBox, false, false, MARGIN);
711         NoOfRowsBox.pack_start(VertAlignHBox, false, false, MARGIN);
712     }
714     SpinsHBox.pack_start(NoOfRowsBox, false, false, MARGIN);
717     /*#### Label for X ####*/
718     padXByYLabel.set_label(" ");
719     XByYLabelVBox.pack_start(padXByYLabel, false, false, MARGIN);
720     XByYLabel.set_markup(" &#215; ");
721     XByYLabelVBox.pack_start(XByYLabel, false, false, MARGIN);
722     SpinsHBox.pack_start(XByYLabelVBox, false, false, MARGIN);
723     gtk_size_group_add_widget(_col2, (GtkWidget *) XByYLabelVBox.gobj());
725     /*#### Number of columns ####*/
727     NoOfColsLabel.set_label(_("Columns:"));
728     NoOfColsBox.pack_start(NoOfColsLabel, false, false, MARGIN);
730     NoOfColsSpinner.set_digits(0);
731     NoOfColsSpinner.set_increments(1, 5);
732     NoOfColsSpinner.set_range(1.0, 100.0);
733     NoOfColsSpinner.set_value(PerRow);
734     NoOfColsSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_row_spinbutton_changed));
735     tips.set_tip(NoOfColsSpinner, _("Number of columns"));
736     NoOfColsBox.pack_start(NoOfColsSpinner, false, false, MARGIN);
737     gtk_size_group_add_widget(_col3, (GtkWidget *) NoOfColsBox.gobj());
739     ColumnWidthButton.set_label(_("Equal width"));
740     double AutoCol = prefs->getDouble("/dialogs/gridtiler/AutoColSize", 15);
741     if (AutoCol>0)
742          AutoColSize=true;
743     else
744          AutoColSize=false;
745     ColumnWidthButton.set_active(AutoColSize);
746     NoOfColsBox.pack_start(ColumnWidthButton, false, false, MARGIN);
748     tips.set_tip(ColumnWidthButton, _("If not set, each column has the width of the widest object in it"));
749     ColumnWidthButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::on_ColSize_checkbutton_changed));
752     {
753         /*#### Radio buttons to control horizontal alignment ####*/
755         HorizAlignLabel.set_label(_("Align:"));
756         HorizAlignVBox.pack_start(HorizAlignLabel, false, false, MARGIN);
758         HorizAlignHBox.pack_start(*(new Gtk::HBox()), true, true, 0); // centering strut
760         HorizLeftRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
761         HorizAlignGroup = HorizLeftRadioButton.get_group();
762         HorizAlignHBox.pack_start(HorizLeftRadioButton, false, false, 0);
764         HorizCentreRadioButton.set_group(HorizAlignGroup);
765         HorizCentreRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
766         HorizAlignHBox.pack_start(HorizCentreRadioButton, false, false, 0);
768         HorizRightRadioButton.set_group(HorizAlignGroup);
769         HorizRightRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
770         HorizAlignHBox.pack_start(HorizRightRadioButton, false, false, 0);
772         HorizAlignHBox.pack_start(*(new Gtk::HBox()), true, true, 0); // centering strut
774         HorizAlign = prefs->getInt("/dialogs/gridtiler/HorizAlign", 1);
775         if (HorizAlign == 0) {
776             HorizLeftRadioButton.set_active(TRUE);
777         }
778         else if (HorizAlign == 1) {
779             HorizCentreRadioButton.set_active(TRUE);
780         }
781         else if (HorizAlign == 2) {
782             HorizRightRadioButton.set_active(TRUE);
783         }
784         HorizAlignVBox.pack_start(HorizAlignHBox, false, false, MARGIN);
785         NoOfColsBox.pack_start(HorizAlignVBox, false, false, MARGIN);
786     }
788     SpinsHBox.pack_start(NoOfColsBox, false, false, MARGIN);
790     TileBox.pack_start(SpinsHBox, false, false, MARGIN);
792     {
793         /*#### Radio buttons to control spacing manually or to fit selection bbox ####*/
794         SpaceByBBoxRadioButton.set_label(_("Fit into selection box"));
795         SpaceByBBoxRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::Spacing_button_changed));
796         SpacingGroup = SpaceByBBoxRadioButton.get_group();
798         SpacingVBox.pack_start(SpaceByBBoxRadioButton, false, false, MARGIN);
800         SpaceManualRadioButton.set_label(_("Set spacing:"));
801         SpaceManualRadioButton.set_group(SpacingGroup);
802         SpaceManualRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::Spacing_button_changed));
803         SpacingVBox.pack_start(SpaceManualRadioButton, false, false, MARGIN);
805         TileBox.pack_start(SpacingVBox, false, false, MARGIN);
806     }
808     {
809         /*#### Y Padding ####*/
811         GtkWidget *i = sp_icon_new (Inkscape::ICON_SIZE_MENU, "clonetiler_per_row");
812         YPadBox.pack_start (*(Glib::wrap(i)), false, false, MARGIN);
814         YPadSpinner.set_digits(1);
815         YPadSpinner.set_increments(0.2, 2);
816         YPadSpinner.set_range(-10000, 10000);
817         double YPad = prefs->getDouble("/dialogs/gridtiler/YPad", 15);
818         YPadSpinner.set_value(YPad);
819         YPadBox.pack_start(YPadSpinner, true, true, MARGIN);
820         tips.set_tip(YPadSpinner, _("Vertical spacing between rows (px units)"));
821         YPadSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_ypad_spinbutton_changed));
822         gtk_size_group_add_widget(_col1, (GtkWidget *) YPadBox.gobj());
824         SizesHBox.pack_start(YPadBox, false, false, MARGIN);
825     }
827     {
828     Gtk::HBox *spacer = new Gtk::HBox;
829     SizesHBox.pack_start(*spacer, false, false, 0);
830     gtk_size_group_add_widget(_col2, (GtkWidget *) spacer->gobj());
831     }
833     {
834         /*#### X padding ####*/
836         GtkWidget *i = sp_icon_new (Inkscape::ICON_SIZE_MENU, "clonetiler_per_column");
837         XPadBox.pack_start (*(Glib::wrap(i)), false, false, MARGIN);
839         XPadSpinner.set_digits(1);
840         XPadSpinner.set_increments(0.2, 2);
841         XPadSpinner.set_range(-10000, 10000);
842         double XPad = prefs->getDouble("/dialogs/gridtiler/XPad", 15);
843         XPadSpinner.set_value(XPad);
844         XPadBox.pack_start(XPadSpinner, true, true, MARGIN);
845         tips.set_tip(XPadSpinner, _("Horizontal spacing between columns (px units)"));
846         XPadSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_xpad_spinbutton_changed));
847         gtk_size_group_add_widget(_col3, (GtkWidget *) XPadBox.gobj());
849         SizesHBox.pack_start(XPadBox, false, false, MARGIN);
850     }
853     TileBox.pack_start(SizesHBox, false, false, MARGIN);
855     contents->pack_start(TileBox);
857     double SpacingType = prefs->getDouble("/dialogs/gridtiler/SpacingType", 15);
858     if (SpacingType>0) {
859         ManualSpacing=true;
860     } else {
861         ManualSpacing=false;
862     }
863     SpaceManualRadioButton.set_active(ManualSpacing);
864     SpaceByBBoxRadioButton.set_active(!ManualSpacing);
865     SizesHBox.set_sensitive (ManualSpacing);
867     //## The OK button
868     TileOkButton = addResponseButton(_("Arrange"), GTK_RESPONSE_APPLY);
869     tips.set_tip((*TileOkButton), _("Arrange selected objects"));
871     show_all_children();
874 } //namespace Dialog
875 } //namespace UI
876 } //namespace Inkscape
878 /*
879   Local Variables:
880   mode:c++
881   c-file-style:"stroustrup"
882   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
883   indent-tabs-mode:nil
884   fill-column:99
885   End:
886 */
887 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 ::