Code

Merging 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 "prefs-utils.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     prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", NoOfColsSpinner.get_value());
387     updating=false;
390 /**
391  * changed value in # of rows spinbox.
392  */
393 void TileDialog::on_col_spinbutton_changed()
395     // quit if run by the attr_changed listener
396     if (updating) {
397             return;
398         }
400     // in turn, prevent listener from responding
401     updating = true;
402     SPDesktop *desktop = getDesktop();
403     Inkscape::Selection *selection = sp_desktop_selection (desktop);
405     GSList const *items = selection->itemList();
406     int selcount = g_slist_length((GSList *)items);
408     double PerRow = ceil(selcount / NoOfRowsSpinner.get_value());
409     NoOfColsSpinner.set_value(PerRow);
410     prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", PerRow);
412     updating=false;
415 /**
416  * changed value in x padding spinbox.
417  */
418 void TileDialog::on_xpad_spinbutton_changed()
421     prefs_set_double_attribute ("dialogs.gridtiler", "XPad", XPadSpinner.get_value());
425 /**
426  * changed value in y padding spinbox.
427  */
428 void TileDialog::on_ypad_spinbutton_changed()
431     prefs_set_double_attribute ("dialogs.gridtiler", "YPad", YPadSpinner.get_value());
436 /**
437  * checked/unchecked autosize Rows button.
438  */
439 void TileDialog::on_RowSize_checkbutton_changed()
442    if (RowHeightButton.get_active()) {
443        prefs_set_double_attribute ("dialogs.gridtiler", "AutoRowSize", 20);
444    } else {
445        prefs_set_double_attribute ("dialogs.gridtiler", "AutoRowSize", -20);
446    }
447    RowHeightBox.set_sensitive ( !RowHeightButton.get_active());
450 /**
451  * checked/unchecked autosize Rows button.
452  */
453 void TileDialog::on_ColSize_checkbutton_changed()
456    if (ColumnWidthButton.get_active()) {
457        prefs_set_double_attribute ("dialogs.gridtiler", "AutoColSize", 20);
458    } else {
459        prefs_set_double_attribute ("dialogs.gridtiler", "AutoColSize", -20);
460    }
461    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     prefs_set_double_attribute ("dialogs.gridtiler", "RowHeight", RowHeightSpinner.get_value());
478     updating=false;
482 /**
483  * changed value in rows spinbox.
484  */
485 void TileDialog::on_colSize_spinbutton_changed()
487     // quit if run by the attr_changed listener
488     if (updating) {
489             return;
490         }
492     // in turn, prevent listener from responding
493     updating = true;
494     prefs_set_double_attribute ("dialogs.gridtiler", "ColWidth", ColumnWidthSpinner.get_value());
495     updating=false;
499 /**
500  * changed Radio button in Spacing group.
501  */
502 void TileDialog::Spacing_button_changed()
504    if (SpaceManualRadioButton.get_active()) {
505        prefs_set_double_attribute ("dialogs.gridtiler", "SpacingType", 20);
506    } else {
507        prefs_set_double_attribute ("dialogs.gridtiler", "SpacingType", -20);
508    }
510    SizesHBox.set_sensitive ( SpaceManualRadioButton.get_active());
513 /**
514  * changed Radio button in Vertical Align group.
515  */
516 void TileDialog::VertAlign_changed()
518    if (VertTopRadioButton.get_active()) {
519        VertAlign = 0;
520        prefs_set_double_attribute ("dialogs.gridtiler", "VertAlign", 0);
521    } else if (VertCentreRadioButton.get_active()){
522        VertAlign = 1;
523        prefs_set_double_attribute ("dialogs.gridtiler", "VertAlign", 1);
524    } else if (VertBotRadioButton.get_active()){
525        VertAlign = 2;
526        prefs_set_double_attribute ("dialogs.gridtiler", "VertAlign", 2);
527    }
531 /**
532  * changed Radio button in Vertical Align group.
533  */
534 void TileDialog::HorizAlign_changed()
536    if (HorizLeftRadioButton.get_active()) {
537        HorizAlign = 0;
538        prefs_set_double_attribute ("dialogs.gridtiler", "HorizAlign", 0);
539    } else if (HorizCentreRadioButton.get_active()){
540        HorizAlign = 1;
541        prefs_set_double_attribute ("dialogs.gridtiler", "HorizAlign", 1);
542    } else if (HorizRightRadioButton.get_active()){
543        HorizAlign = 2;
544        prefs_set_double_attribute ("dialogs.gridtiler", "HorizAlign", 2);
545    }
549 /**
550  * Desktop selection changed
551  */
552 void TileDialog::updateSelection()
554     double col_width, row_height;
555     // quit if run by the attr_changed listener
556     if (updating) {
557             return;
558         }
560     col_width=0;
561     row_height=0;
562     // in turn, prevent listener from responding
563     updating = true;
564     SPDesktop *desktop = getDesktop();
565     Inkscape::Selection *selection = sp_desktop_selection (desktop);
566     const GSList *items = selection->itemList();
567     int selcount = g_slist_length((GSList *)items);
569     if (NoOfColsSpinner.get_value()>1 && NoOfRowsSpinner.get_value()>1){
570         // Update the number of rows assuming number of columns wanted remains same.
571         double NoOfRows = ceil(selcount / NoOfColsSpinner.get_value());
572         NoOfRowsSpinner.set_value(NoOfRows);
574         // if the selection has less than the number set for one row, reduce it appropriately
575         if (selcount<NoOfColsSpinner.get_value()) {
576             double NoOfCols = ceil(selcount / NoOfRowsSpinner.get_value());
577             NoOfColsSpinner.set_value(NoOfCols);
578             prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", NoOfCols);
579         }
580     } else {
581         double PerRow = ceil(sqrt(selcount));
582         double PerCol = ceil(sqrt(selcount));
583         NoOfRowsSpinner.set_value(PerRow);
584         NoOfColsSpinner.set_value(PerCol);
585         prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", PerCol);
587     }
589     updating=false;
595 /*##########################
596 ## Experimental
597 ##########################*/
599 static void updateSelectionCallback(Inkscape::Application */*inkscape*/, Inkscape::Selection */*selection*/, TileDialog *dlg)
601     TileDialog *tledlg = (TileDialog *) dlg;
602     tledlg->updateSelection();
606 //#########################################################################
607 //## C O N S T R U C T O R    /    D E S T R U C T O R
608 //#########################################################################
609 /**
610  * Constructor
611  */
612 TileDialog::TileDialog()
613     : UI::Widget::Panel("", "dialogs.gridtiler", SP_VERB_SELECTION_GRIDTILE)
615      // bool used by spin button callbacks to stop loops where they change each other.
616     updating = false;
618     // could not do this in gtkmm - there's no Gtk::SizeGroup public constructor (!)
619     GtkSizeGroup *_col1 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
620     GtkSizeGroup *_col2 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
621     GtkSizeGroup *_col3 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
623     {
624         // Selection Change signal
625         g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (updateSelectionCallback), this);
626     }
628     Gtk::Box *contents = _getContents();
630 #define MARGIN 2
632     //##Set up the panel
634     SPDesktop *desktop = getDesktop();
636     Inkscape::Selection *selection = sp_desktop_selection (desktop);
637     int selcount = 1;
638     if (!selection->isEmpty()) {
639         GSList const *items = selection->itemList();
640         selcount = g_slist_length((GSList *)items);
641     }
644     /*#### Number of Rows ####*/
646     double PerRow = ceil(sqrt(selcount));
647     double PerCol = ceil(sqrt(selcount));
649     #ifdef DEBUG_GRID_ARRANGE
650         g_print("/n PerRox = %f PerCol = %f selcount = %d",PerRow,PerCol,selcount);
651     #endif
653     NoOfRowsLabel.set_label(_("Rows:"));
654     NoOfRowsBox.pack_start(NoOfRowsLabel, false, false, MARGIN);
656     NoOfRowsSpinner.set_digits(0);
657     NoOfRowsSpinner.set_increments(1, 5);
658     NoOfRowsSpinner.set_range(1.0, 100.0);
659     NoOfRowsSpinner.set_value(PerCol);
660     NoOfRowsSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_col_spinbutton_changed));
661     tips.set_tip(NoOfRowsSpinner, _("Number of rows"));
662     NoOfRowsBox.pack_start(NoOfRowsSpinner, false, false, MARGIN);
663     gtk_size_group_add_widget(_col1, (GtkWidget *) NoOfRowsBox.gobj());
665     RowHeightButton.set_label(_("Equal height"));
666     double AutoRow = prefs_get_double_attribute ("dialogs.gridtiler", "AutoRowSize", 15);
667     if (AutoRow>0)
668          AutoRowSize=true;
669     else
670          AutoRowSize=false;
671     RowHeightButton.set_active(AutoRowSize);
673     NoOfRowsBox.pack_start(RowHeightButton, false, false, MARGIN);
675     tips.set_tip(RowHeightButton, _("If not set, each row has the height of the tallest object in it"));
676     RowHeightButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::on_RowSize_checkbutton_changed));
678  {
679         /*#### Radio buttons to control vertical alignment ####*/
681         VertAlignLabel.set_label(_("Align:"));
682         VertAlignHBox.pack_start(VertAlignLabel, false, false, MARGIN);
684         VertTopRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
685         VertAlignGroup = VertTopRadioButton.get_group();
686         VertAlignVBox.pack_start(VertTopRadioButton, false, false, 0);
688         VertCentreRadioButton.set_group(VertAlignGroup);
689         VertCentreRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
690         VertAlignVBox.pack_start(VertCentreRadioButton, false, false, 0);
692         VertBotRadioButton.set_group(VertAlignGroup);
693         VertBotRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
694         VertAlignVBox.pack_start(VertBotRadioButton, false, false, 0);
696         VertAlign = prefs_get_double_attribute ("dialogs.gridtiler", "VertAlign", 1);
697         if (VertAlign == 0) {
698             VertTopRadioButton.set_active(TRUE);
699         }
700         else if (VertAlign == 1) {
701             VertCentreRadioButton.set_active(TRUE);
702         }
703         else if (VertAlign == 2){
704             VertBotRadioButton.set_active(TRUE);
705         }
706         VertAlignHBox.pack_start(VertAlignVBox, false, false, MARGIN);
707         NoOfRowsBox.pack_start(VertAlignHBox, false, false, MARGIN);
708     }
710     SpinsHBox.pack_start(NoOfRowsBox, false, false, MARGIN);
713     /*#### Label for X ####*/
714     padXByYLabel.set_label(" ");
715     XByYLabelVBox.pack_start(padXByYLabel, false, false, MARGIN);
716     XByYLabel.set_markup(" &#215; ");
717     XByYLabelVBox.pack_start(XByYLabel, false, false, MARGIN);
718     SpinsHBox.pack_start(XByYLabelVBox, false, false, MARGIN);
719     gtk_size_group_add_widget(_col2, (GtkWidget *) XByYLabelVBox.gobj());
721     /*#### Number of columns ####*/
723     NoOfColsLabel.set_label(_("Columns:"));
724     NoOfColsBox.pack_start(NoOfColsLabel, false, false, MARGIN);
726     NoOfColsSpinner.set_digits(0);
727     NoOfColsSpinner.set_increments(1, 5);
728     NoOfColsSpinner.set_range(1.0, 100.0);
729     NoOfColsSpinner.set_value(PerRow);
730     NoOfColsSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_row_spinbutton_changed));
731     tips.set_tip(NoOfColsSpinner, _("Number of columns"));
732     NoOfColsBox.pack_start(NoOfColsSpinner, false, false, MARGIN);
733     gtk_size_group_add_widget(_col3, (GtkWidget *) NoOfColsBox.gobj());
735     ColumnWidthButton.set_label(_("Equal width"));
736     double AutoCol = prefs_get_double_attribute ("dialogs.gridtiler", "AutoColSize", 15);
737     if (AutoCol>0)
738          AutoColSize=true;
739     else
740          AutoColSize=false;
741     ColumnWidthButton.set_active(AutoColSize);
742     NoOfColsBox.pack_start(ColumnWidthButton, false, false, MARGIN);
744     tips.set_tip(ColumnWidthButton, _("If not set, each column has the width of the widest object in it"));
745     ColumnWidthButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::on_ColSize_checkbutton_changed));
748     {
749         /*#### Radio buttons to control horizontal alignment ####*/
751         HorizAlignLabel.set_label(_("Align:"));
752         HorizAlignVBox.pack_start(HorizAlignLabel, false, false, MARGIN);
754         HorizAlignHBox.pack_start(*(new Gtk::HBox()), true, true, 0); // centering strut
756         HorizLeftRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
757         HorizAlignGroup = HorizLeftRadioButton.get_group();
758         HorizAlignHBox.pack_start(HorizLeftRadioButton, false, false, 0);
760         HorizCentreRadioButton.set_group(HorizAlignGroup);
761         HorizCentreRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
762         HorizAlignHBox.pack_start(HorizCentreRadioButton, false, false, 0);
764         HorizRightRadioButton.set_group(HorizAlignGroup);
765         HorizRightRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
766         HorizAlignHBox.pack_start(HorizRightRadioButton, false, false, 0);
768         HorizAlignHBox.pack_start(*(new Gtk::HBox()), true, true, 0); // centering strut
770         HorizAlign = prefs_get_double_attribute ("dialogs.gridtiler", "HorizAlign", 1);
771         if (HorizAlign == 0) {
772             HorizLeftRadioButton.set_active(TRUE);
773         }
774         else if (HorizAlign == 1) {
775             HorizCentreRadioButton.set_active(TRUE);
776         }
777         else if (HorizAlign == 2) {
778             HorizRightRadioButton.set_active(TRUE);
779         }
780         HorizAlignVBox.pack_start(HorizAlignHBox, false, false, MARGIN);
781         NoOfColsBox.pack_start(HorizAlignVBox, false, false, MARGIN);
782     }
784     SpinsHBox.pack_start(NoOfColsBox, false, false, MARGIN);
786     TileBox.pack_start(SpinsHBox, false, false, MARGIN);
788     {
789         /*#### Radio buttons to control spacing manually or to fit selection bbox ####*/
790         SpaceByBBoxRadioButton.set_label(_("Fit into selection box"));
791         SpaceByBBoxRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::Spacing_button_changed));
792         SpacingGroup = SpaceByBBoxRadioButton.get_group();
794         SpacingVBox.pack_start(SpaceByBBoxRadioButton, false, false, MARGIN);
796         SpaceManualRadioButton.set_label(_("Set spacing:"));
797         SpaceManualRadioButton.set_group(SpacingGroup);
798         SpaceManualRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::Spacing_button_changed));
799         SpacingVBox.pack_start(SpaceManualRadioButton, false, false, MARGIN);
801         TileBox.pack_start(SpacingVBox, false, false, MARGIN);
802     }
804     {
805         /*#### Y Padding ####*/
807         GtkWidget *i = sp_icon_new (Inkscape::ICON_SIZE_MENU, "clonetiler_per_row");
808         YPadBox.pack_start (*(Glib::wrap(i)), false, false, MARGIN);
810         YPadSpinner.set_digits(1);
811         YPadSpinner.set_increments(0.2, 2);
812         YPadSpinner.set_range(-10000, 10000);
813         double YPad = prefs_get_double_attribute ("dialogs.gridtiler", "YPad", 15);
814         YPadSpinner.set_value(YPad);
815         YPadBox.pack_start(YPadSpinner, true, true, MARGIN);
816         tips.set_tip(YPadSpinner, _("Vertical spacing between rows (px units)"));
817         YPadSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_ypad_spinbutton_changed));
818         gtk_size_group_add_widget(_col1, (GtkWidget *) YPadBox.gobj());
820         SizesHBox.pack_start(YPadBox, false, false, MARGIN);
821     }
823     {
824     Gtk::HBox *spacer = new Gtk::HBox;
825     SizesHBox.pack_start(*spacer, false, false, 0);
826     gtk_size_group_add_widget(_col2, (GtkWidget *) spacer->gobj());
827     }
829     {
830         /*#### X padding ####*/
832         GtkWidget *i = sp_icon_new (Inkscape::ICON_SIZE_MENU, "clonetiler_per_column");
833         XPadBox.pack_start (*(Glib::wrap(i)), false, false, MARGIN);
835         XPadSpinner.set_digits(1);
836         XPadSpinner.set_increments(0.2, 2);
837         XPadSpinner.set_range(-10000, 10000);
838         double XPad = prefs_get_double_attribute ("dialogs.gridtiler", "XPad", 15);
839         XPadSpinner.set_value(XPad);
840         XPadBox.pack_start(XPadSpinner, true, true, MARGIN);
841         tips.set_tip(XPadSpinner, _("Horizontal spacing between columns (px units)"));
842         XPadSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_xpad_spinbutton_changed));
843         gtk_size_group_add_widget(_col3, (GtkWidget *) XPadBox.gobj());
845         SizesHBox.pack_start(XPadBox, false, false, MARGIN);
846     }
849     TileBox.pack_start(SizesHBox, false, false, MARGIN);
851     contents->pack_start(TileBox);
853     double SpacingType = prefs_get_double_attribute ("dialogs.gridtiler", "SpacingType", 15);
854     if (SpacingType>0) {
855         ManualSpacing=true;
856     } else {
857         ManualSpacing=false;
858     }
859     SpaceManualRadioButton.set_active(ManualSpacing);
860     SpaceByBBoxRadioButton.set_active(!ManualSpacing);
861     SizesHBox.set_sensitive (ManualSpacing);
863     //## The OK button
864     TileOkButton = addResponseButton(_("Arrange"), GTK_RESPONSE_APPLY);
865     tips.set_tip((*TileOkButton), _("Arrange selected objects"));
867     show_all_children();
875 } //namespace Dialog
876 } //namespace UI
877 } //namespace Inkscape
879 //#########################################################################
880 //## E N D    O F    F I L E
881 //#########################################################################
884 /*
885   Local Variables:
886   mode:c++
887   c-file-style:"stroustrup"
888   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
889   indent-tabs-mode:nil
890   fill-column:99
891   End:
892 */
893 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :