Code

switch from invokeBbox to getBounds (need to fix problems with empty
[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 "libnr/nr-matrix-ops.h"
27 #include "verbs.h"
28 #include "prefs-utils.h"
29 #include "inkscape.h"
30 #include "desktop-handles.h"
31 #include "selection.h"
32 #include "document.h"
33 #include "sp-item.h"
34 #include "widgets/icon.h"
35 #include "tiledialog.h"
39 /*
40  *    Sort items by their x co-ordinates, taking account of y (keeps rows intact)
41  *
42  *    <0 *elem1 goes before *elem2
43  *    0  *elem1 == *elem2
44  *    >0  *elem1 goes after *elem2
45  */
46 int
47 sp_compare_x_position(SPItem *first, SPItem *second)
48 {
49     using NR::X;
50     using NR::Y;
52     NR::Rect const a = first->getBounds(sp_item_i2doc_affine(first));
53     double const a_height = a.dimensions()[Y];
55     NR::Rect const b = second->getBounds(sp_item_i2doc_affine(second));
56     double const b_height = b.dimensions()[Y];
57     
58     bool a_in_b_vert = false;
59     if ((a.min()[Y] < b.min()[Y] + 0.1) && (a.min()[Y] > b.min()[Y] - b_height)) {
60         a_in_b_vert = true;
61     } else if ((b.min()[Y] < a.min()[Y] + 0.1) && (b.min()[Y] > a.min()[Y] - a_height)) {
62         a_in_b_vert = true;
63     } else if (b.min()[Y] == a.min()[Y]) {
64         a_in_b_vert = true;
65     } else {
66         a_in_b_vert = false;
67     }
69     if (!a_in_b_vert) {
70         return -1;
71     }
72     if (a_in_b_vert && a.min()[X] > b.min()[X]) {
73         return 1;
74     }
75     if (a_in_b_vert && a.min()[X] < b.min()[X]) {
76         return -1;
77     }
78     return 0;
79 }
81 /*
82  *    Sort items by their y co-ordinates.
83  */
84 int
85 sp_compare_y_position(SPItem *first, SPItem *second)
86 {
87     NR::Rect const a = first->getBounds(sp_item_i2doc_affine(first));
88     NR::Rect const b = second->getBounds(sp_item_i2doc_affine(second));
90     if (a.min()[NR::Y] > b.min()[NR::Y]) {
91         return 1;
92     }
93     if (a.min()[NR::Y] < b.min()[NR::Y]) {
94         return -1;
95     }
96     
97     return 0;
98 }
100 namespace Inkscape {
101 namespace UI {
102 namespace Dialog {
105 //#########################################################################
106 //## E V E N T S
107 //#########################################################################
109 /*
110  *
111  * This arranges the selection in a grid pattern.
112  *
113  */
115 void TileDialog::Grid_Arrange ()
118     int cnt,row_cnt,col_cnt,a,row,col;
119     double grid_left,grid_top,col_width,row_height,paddingx,paddingy,width, height, new_x, new_y,cx,cy;
120     double total_col_width,total_row_height;
121     col_width = 0;
122     row_height = 0;
123     total_col_width=0;
124     total_row_height=0;
126     // check for correct numbers in the row- and col-spinners
127     on_col_spinbutton_changed();
128     on_row_spinbutton_changed();
130     // set padding to manual values
131     paddingx = XPadSpinner.get_value();
132     paddingy = YPadSpinner.get_value();
134     std::vector<double> row_heights;
135     std::vector<double> col_widths;
136     std::vector<double> row_ys;
137     std::vector<double> col_xs;
139     int NoOfCols = NoOfColsSpinner.get_value_as_int();
140     int NoOfRows = NoOfRowsSpinner.get_value_as_int();
142     width = 0;
143     for (a=0;a<NoOfCols; a++){
144         col_widths.push_back(width);
145     }
147     height = 0;
148     for (a=0;a<NoOfRows; a++){
149         row_heights.push_back(height);
150     }
151     grid_left = 99999;
152     grid_top = 99999;
154     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
155     sp_document_ensure_up_to_date(sp_desktop_document(desktop));
157     Inkscape::Selection *selection = sp_desktop_selection (desktop);
158     const GSList *items = selection->itemList();
159     cnt=0;
160     for (; items != NULL; items = items->next) {
161         SPItem *item = SP_ITEM(items->data);
162         NR::Rect const b = item->getBounds(sp_item_i2doc_affine(item));
163         width = b.dimensions()[NR::X];
164         height = b.dimensions()[NR::Y];
165         cx = b.midpoint()[NR::X];
166         cy = b.midpoint()[NR::Y];
168         if (b.min()[NR::X] < grid_left) {
169             grid_left = b.min()[NR::X];
170         }
171         if (b.min()[NR::Y] < grid_top) {
172             grid_top = b.min()[NR::Y];
173         }
174         if (width > col_width) {
175             col_width = width;
176         }
177         if (height > row_height) {
178             row_height = height;
179         }
180     }
183     // require the sorting done before we can calculate row heights etc.
185     const GSList *items2 = selection->itemList();
186     GSList *rev = g_slist_copy((GSList *) items2);
187     GSList *sorted = NULL;
188     rev = g_slist_sort(rev, (GCompareFunc) sp_compare_y_position);
189     sorted = g_slist_sort(rev, (GCompareFunc) sp_compare_x_position);
192     // Calculate individual Row and Column sizes if necessary
195         cnt=0;
196         const GSList *sizes = sorted;
197         for (; sizes != NULL; sizes = sizes->next) {
198             SPItem *item = SP_ITEM(sizes->data);
199             NR::Rect const b = item->getBounds(sp_item_i2doc_affine(item));
200             width = b.dimensions()[NR::X];
201             height = b.dimensions()[NR::Y];
202             if (width > col_widths[(cnt % NoOfCols)]) {
203                 col_widths[(cnt % NoOfCols)] = width;
204             }
205             if (height > row_heights[(cnt / NoOfCols)]) {
206                 row_heights[(cnt / NoOfCols)] = height;
207             }
208             cnt++;
209         }
212     /// Make sure the top and left of the grid dont move by compensating for align values.
213     if (RowHeightButton.get_active()){
214         grid_top = grid_top - (((row_height - row_heights[0]) / 2)*(VertAlign));
215     }
216     if (ColumnWidthButton.get_active()){
217         grid_left = grid_left - (((col_width - col_widths[0]) /2)*(HorizAlign));
218     }
220     #ifdef DEBUG_GRID_ARRANGE
221      g_print("\n cx = %f cy= %f gridleft=%f",cx,cy,grid_left);
222     #endif
224     // Calculate total widths and heights, allowing for columns and rows non uniformly sized.
226     if (ColumnWidthButton.get_active()){
227         total_col_width = col_width * NoOfCols;
228         col_widths.clear();
229         for (a=0;a<NoOfCols; a++){
230             col_widths.push_back(col_width);
231         }
232     } else {
233         for (a = 0; a < (int)col_widths.size(); a++)
234         {
235           total_col_width += col_widths[a] ;
236         }
237     }
239     if (RowHeightButton.get_active()){
240         total_row_height = row_height * NoOfRows;
241         row_heights.clear();
242         for (a=0;a<NoOfRows; a++){
243             row_heights.push_back(row_height);
244         }
245     } else {
246         for (a = 0; a < (int)row_heights.size(); a++)
247         {
248           total_row_height += row_heights[a] ;
249         }
250     }
253     // Fit to bbox, calculate padding between rows accordingly.
254     if (!SpaceManualRadioButton.get_active()){
255         NR::Rect b = selection->bounds();
256 #ifdef DEBUG_GRID_ARRANGE
257 g_print("\n row = %f     col = %f selection x= %f selection y = %f", total_row_height,total_col_width, b.extent(NR::X), b.extent(NR::Y));
258 #endif
259         paddingx = (b.extent(NR::X) - total_col_width) / (NoOfCols -1);
260         paddingy = (b.extent(NR::Y) - total_row_height) / (NoOfRows -1);
261     }
263 /*
264     Horizontal align  - Left    = 0
265                         Centre  = 1
266                         Right   = 2
268     Vertical align    - Top     = 0
269                         Middle  = 1
270                         Bottom  = 2
272     X position is calculated by taking the grids left co-ord, adding the distance to the column,
273    then adding 1/2 the spacing multiplied by the align variable above,
274    Y position likewise, takes the top of the grid, adds the y to the current row then adds the padding in to align it.
276 */
278     // Calculate row and column x and y coords required to allow for columns and rows which are non uniformly sized.
280     for (a=0;a<NoOfCols; a++){
281         if (a<1) col_xs.push_back(0);
282         else col_xs.push_back(col_widths[a-1]+paddingx+col_xs[a-1]);
283     }
286     for (a=0;a<NoOfRows; a++){
287         if (a<1) row_ys.push_back(0);
288         else row_ys.push_back(row_heights[a-1]+paddingy+row_ys[a-1]);
289     }
291     cnt=0;
292   for (row_cnt=0; ((sorted != NULL) && (row_cnt<NoOfRows)); row_cnt++) {
294              GSList *current_row = NULL;
295              for (col_cnt = 0; ((sorted != NULL) && (col_cnt<NoOfCols)); col_cnt++) {
296                  current_row = g_slist_append (current_row, sorted->data);
297                  sorted = sorted->next;
298              }
300              for (; current_row != NULL; current_row = current_row->next) {
301                  SPItem *item=SP_ITEM(current_row->data);
302                  Inkscape::XML::Node *repr = SP_OBJECT_REPR(item);
303                  NR::Rect const b = item->getBounds(sp_item_i2doc_affine(item));
304                  width = b.dimensions()[NR::X];
305                  height = b.dimensions()[NR::Y];
306                  row = cnt / NoOfCols;
307                  col = cnt % NoOfCols;
309                 // original before I started fecking about with it.
310                 // new_x = grid_left + (((col_width - width)/2)*HorizAlign) + (( col_width + paddingx ) * (cnt % NoOfCols));
311                 // new_y = grid_top + (((row_height - height)/2)*VertAlign) +(( row_height + paddingy ) * (cnt / NoOfCols));
313                  new_x = grid_left + (((col_widths[col] - width)/2)*HorizAlign) + col_xs[col];
314                  new_y = grid_top + (((row_heights[row] - height)/2)*VertAlign) + row_ys[row];
316                  NR::Point move = NR::Point(new_x - b.min()[NR::X], b.min()[NR::Y] - new_y); // why are the two args the opposite ways round???
317                  NR::Matrix const &affine = NR::Matrix(NR::translate(move));
318                  sp_item_set_i2d_affine(item, sp_item_i2d_affine(item) * affine);
319                  sp_item_write_transform(item, repr, item->transform,  NULL);
320                  SP_OBJECT (current_row->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
321                  cnt +=1;
322              }
323              g_slist_free (current_row);
324     }
326     NRRect b;
327             selection->bounds(&b);
329             sp_document_done (sp_desktop_document (desktop), SP_VERB_SELECTION_GRIDTILE, 
330                               _("Arrange in a grid"));
335 //#########################################################################
336 //## E V E N T S
337 //#########################################################################
340 void TileDialog::_apply()
342         Grid_Arrange();
346 /**
347  * changed value in # of columns spinbox.
348  */
349 void TileDialog::on_row_spinbutton_changed()
351     // quit if run by the attr_changed listener
352     if (updating) {
353             return;
354         }
356     // in turn, prevent listener from responding
357     updating = true;
358     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
360     Inkscape::Selection *selection = sp_desktop_selection (desktop);
362     GSList const *items = selection->itemList();
363     int selcount = g_slist_length((GSList *)items);
365     double PerCol = ceil(selcount / NoOfColsSpinner.get_value());
366     NoOfRowsSpinner.set_value(PerCol);
367     prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", NoOfColsSpinner.get_value());
368     updating=false;
371 /**
372  * changed value in # of rows spinbox.
373  */
374 void TileDialog::on_col_spinbutton_changed()
376     // quit if run by the attr_changed listener
377     if (updating) {
378             return;
379         }
381     // in turn, prevent listener from responding
382     updating = true;
383     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
384     Inkscape::Selection *selection = sp_desktop_selection (desktop);
386     GSList const *items = selection->itemList();
387     int selcount = g_slist_length((GSList *)items);
389     double PerRow = ceil(selcount / NoOfRowsSpinner.get_value());
390     NoOfColsSpinner.set_value(PerRow);
391     prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", PerRow);
393     updating=false;
396 /**
397  * changed value in x padding spinbox.
398  */
399 void TileDialog::on_xpad_spinbutton_changed()
402     prefs_set_double_attribute ("dialogs.gridtiler", "XPad", XPadSpinner.get_value());
406 /**
407  * changed value in y padding spinbox.
408  */
409 void TileDialog::on_ypad_spinbutton_changed()
412     prefs_set_double_attribute ("dialogs.gridtiler", "YPad", YPadSpinner.get_value());
417 /**
418  * checked/unchecked autosize Rows button.
419  */
420 void TileDialog::on_RowSize_checkbutton_changed()
423    if (RowHeightButton.get_active()) {
424        prefs_set_double_attribute ("dialogs.gridtiler", "AutoRowSize", 20);
425    } else {
426        prefs_set_double_attribute ("dialogs.gridtiler", "AutoRowSize", -20);
427    }
428    RowHeightBox.set_sensitive ( !RowHeightButton.get_active());
431 /**
432  * checked/unchecked autosize Rows button.
433  */
434 void TileDialog::on_ColSize_checkbutton_changed()
437    if (ColumnWidthButton.get_active()) {
438        prefs_set_double_attribute ("dialogs.gridtiler", "AutoColSize", 20);
439    } else {
440        prefs_set_double_attribute ("dialogs.gridtiler", "AutoColSize", -20);
441    }
442    ColumnWidthBox.set_sensitive ( !ColumnWidthButton.get_active());
446 /**
447  * changed value in columns spinbox.
448  */
449 void TileDialog::on_rowSize_spinbutton_changed()
451     // quit if run by the attr_changed listener
452     if (updating) {
453             return;
454         }
456     // in turn, prevent listener from responding
457     updating = true;
458     prefs_set_double_attribute ("dialogs.gridtiler", "RowHeight", RowHeightSpinner.get_value());
459     updating=false;
463 /**
464  * changed value in rows spinbox.
465  */
466 void TileDialog::on_colSize_spinbutton_changed()
468     // quit if run by the attr_changed listener
469     if (updating) {
470             return;
471         }
473     // in turn, prevent listener from responding
474     updating = true;
475     prefs_set_double_attribute ("dialogs.gridtiler", "ColWidth", ColumnWidthSpinner.get_value());
476     updating=false;
480 /**
481  * changed Radio button in Spacing group.
482  */
483 void TileDialog::Spacing_button_changed()
485    if (SpaceManualRadioButton.get_active()) {
486        prefs_set_double_attribute ("dialogs.gridtiler", "SpacingType", 20);
487    } else {
488        prefs_set_double_attribute ("dialogs.gridtiler", "SpacingType", -20);
489    }
491    SizesHBox.set_sensitive ( SpaceManualRadioButton.get_active());
494 /**
495  * changed Radio button in Vertical Align group.
496  */
497 void TileDialog::VertAlign_changed()
499    if (VertTopRadioButton.get_active()) {
500        VertAlign = 0;
501        prefs_set_double_attribute ("dialogs.gridtiler", "VertAlign", 0);
502    } else if (VertCentreRadioButton.get_active()){
503        VertAlign = 1;
504        prefs_set_double_attribute ("dialogs.gridtiler", "VertAlign", 1);
505    } else if (VertBotRadioButton.get_active()){
506        VertAlign = 2;
507        prefs_set_double_attribute ("dialogs.gridtiler", "VertAlign", 2);
508    }
512 /**
513  * changed Radio button in Vertical Align group.
514  */
515 void TileDialog::HorizAlign_changed()
517    if (HorizLeftRadioButton.get_active()) {
518        HorizAlign = 0;
519        prefs_set_double_attribute ("dialogs.gridtiler", "HorizAlign", 0);
520    } else if (HorizCentreRadioButton.get_active()){
521        HorizAlign = 1;
522        prefs_set_double_attribute ("dialogs.gridtiler", "HorizAlign", 1);
523    } else if (HorizRightRadioButton.get_active()){
524        HorizAlign = 2;
525        prefs_set_double_attribute ("dialogs.gridtiler", "HorizAlign", 2);
526    }
530 /**
531  * Desktop selection changed
532  */
533 void TileDialog::updateSelection()
535     double col_width, row_height;
536     // quit if run by the attr_changed listener
537     if (updating) {
538             return;
539         }
541     col_width=0;
542     row_height=0;
543     // in turn, prevent listener from responding
544     updating = true;
545     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
546     Inkscape::Selection *selection = sp_desktop_selection (desktop);
547     const GSList *items = selection->itemList();
548     int selcount = g_slist_length((GSList *)items);
550     if (NoOfColsSpinner.get_value()>1){
551         // Update the number of rows assuming number of columns wanted remains same.
552         double NoOfRows = ceil(selcount / NoOfColsSpinner.get_value());
553         NoOfRowsSpinner.set_value(NoOfRows);
555         // if the selection has less than the number set for one row, reduce it appropriately
556         if (selcount<NoOfColsSpinner.get_value()) {
557             double NoOfCols = ceil(selcount / NoOfRowsSpinner.get_value());
558             NoOfColsSpinner.set_value(NoOfCols);
559             prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", NoOfCols);
560         }
561     } else {
562         NoOfColsSpinner.set_value(selcount);
563         prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", selcount);
565     }
567     updating=false;
575 /*##########################
576 ## Experimental
577 ##########################*/
579 static void updateSelectionCallback(Inkscape::Application *inkscape, Inkscape::Selection *selection, TileDialog *dlg)
581     TileDialog *tledlg = (TileDialog *) dlg;
582     tledlg->updateSelection();
586 //#########################################################################
587 //## C O N S T R U C T O R    /    D E S T R U C T O R
588 //#########################################################################
589 /**
590  * Constructor
591  */
592 TileDialog::TileDialog()
593     : Dialog ("dialogs.gridtiler", SP_VERB_SELECTION_GRIDTILE)
595      // bool used by spin button callbacks to stop loops where they change each other.
596     updating = false;
598     // could not do this in gtkmm - there's no Gtk::SizeGroup public constructor (!)
599     GtkSizeGroup *_col1 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
600     GtkSizeGroup *_col2 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
601     GtkSizeGroup *_col3 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
603     {
604         // Selection Change signal
605         g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (updateSelectionCallback), this);
606     }
608     Gtk::VBox *mainVBox = get_vbox();
610 #define MARGIN 2
612     //##Set up the panel
614     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
616     Inkscape::Selection *selection = sp_desktop_selection (desktop);
617     int selcount = 1;
618     if (!selection->isEmpty()) {
619         GSList const *items = selection->itemList();
620         selcount = g_slist_length((GSList *)items);
621     }
624     /*#### Number of Rows ####*/
626     double PerRow = selcount;
627     double PerCol = 1;
629     #ifdef DEBUG_GRID_ARRANGE
630         g_print("/n PerRox = %f PerCol = %f selcount = %d",PerRow,PerCol,selcount);
631     #endif
633     NoOfRowsLabel.set_label(_("Rows:"));
634     NoOfRowsBox.pack_start(NoOfRowsLabel, false, false, MARGIN);
636     NoOfRowsSpinner.set_digits(0);
637     NoOfRowsSpinner.set_increments(1, 5);
638     NoOfRowsSpinner.set_range(1.0, 100.0);
639     NoOfRowsSpinner.set_value(PerCol);
640     NoOfRowsSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_col_spinbutton_changed));
641     tips.set_tip(NoOfRowsSpinner, _("Number of rows"));
642     NoOfRowsBox.pack_start(NoOfRowsSpinner, false, false, MARGIN);
643     gtk_size_group_add_widget(_col1, (GtkWidget *) NoOfRowsBox.gobj());
645     RowHeightButton.set_label(_("Equal height"));
646     double AutoRow = prefs_get_double_attribute ("dialogs.gridtiler", "AutoRowSize", 15);
647     if (AutoRow>0)
648          AutoRowSize=true;
649     else
650          AutoRowSize=false;
651     RowHeightButton.set_active(AutoRowSize);
653     NoOfRowsBox.pack_start(RowHeightButton, false, false, MARGIN);
655     tips.set_tip(RowHeightButton, _("If not set, each row has the height of the tallest object in it"));
656     RowHeightButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::on_RowSize_checkbutton_changed));
658  {
659         /*#### Radio buttons to control vertical alignment ####*/
661         VertAlignLabel.set_label(_("Align:"));
662         VertAlignHBox.pack_start(VertAlignLabel, false, false, MARGIN);
664         VertTopRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
665         VertAlignGroup = VertTopRadioButton.get_group();
666         VertAlignVBox.pack_start(VertTopRadioButton, false, false, 0);
668         VertCentreRadioButton.set_group(VertAlignGroup);
669         VertCentreRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
670         VertAlignVBox.pack_start(VertCentreRadioButton, false, false, 0);
672         VertBotRadioButton.set_group(VertAlignGroup);
673         VertBotRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
674         VertAlignVBox.pack_start(VertBotRadioButton, false, false, 0);
676         VertAlign = prefs_get_double_attribute ("dialogs.gridtiler", "VertAlign", 1);
677         if (VertAlign == 0) {
678             VertTopRadioButton.set_active(TRUE);
679         }
680         else if (VertAlign == 1) {
681             VertCentreRadioButton.set_active(TRUE);
682         }
683         else if (VertAlign == 2){
684             VertBotRadioButton.set_active(TRUE);
685         }
686         VertAlignHBox.pack_start(VertAlignVBox, false, false, MARGIN);
687         NoOfRowsBox.pack_start(VertAlignHBox, false, false, MARGIN);
688     }
690     SpinsHBox.pack_start(NoOfRowsBox, false, false, MARGIN);
693     /*#### Label for X ####*/
694     padXByYLabel.set_label(" ");
695     XByYLabelVBox.pack_start(padXByYLabel, false, false, MARGIN);
696     XByYLabel.set_markup(" &#215; ");
697     XByYLabelVBox.pack_start(XByYLabel, false, false, MARGIN);
698     SpinsHBox.pack_start(XByYLabelVBox, false, false, MARGIN);
699     gtk_size_group_add_widget(_col2, (GtkWidget *) XByYLabelVBox.gobj());
701     /*#### Number of columns ####*/
703     NoOfColsLabel.set_label(_("Columns:"));
704     NoOfColsBox.pack_start(NoOfColsLabel, false, false, MARGIN);
706     NoOfColsSpinner.set_digits(0);
707     NoOfColsSpinner.set_increments(1, 5);
708     NoOfColsSpinner.set_range(1.0, 100.0);
709     NoOfColsSpinner.set_value(PerRow);
710     NoOfColsSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_row_spinbutton_changed));
711     tips.set_tip(NoOfColsSpinner, _("Number of columns"));
712     NoOfColsBox.pack_start(NoOfColsSpinner, false, false, MARGIN);
713     gtk_size_group_add_widget(_col3, (GtkWidget *) NoOfColsBox.gobj());
715     ColumnWidthButton.set_label(_("Equal width"));
716     double AutoCol = prefs_get_double_attribute ("dialogs.gridtiler", "AutoColSize", 15);
717     if (AutoCol>0)
718          AutoColSize=true;
719     else
720          AutoColSize=false;
721     ColumnWidthButton.set_active(AutoColSize);
722     NoOfColsBox.pack_start(ColumnWidthButton, false, false, MARGIN);
724     tips.set_tip(ColumnWidthButton, _("If not set, each column has the width of the widest object in it"));
725     ColumnWidthButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::on_ColSize_checkbutton_changed));
728     {
729         /*#### Radio buttons to control horizontal alignment ####*/
731         HorizAlignLabel.set_label(_("Align:"));
732         HorizAlignVBox.pack_start(HorizAlignLabel, false, false, MARGIN);
734         HorizAlignHBox.pack_start(*(new Gtk::HBox()), true, true, 0); // centering strut
736         HorizLeftRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
737         HorizAlignGroup = HorizLeftRadioButton.get_group();
738         HorizAlignHBox.pack_start(HorizLeftRadioButton, false, false, 0);
740         HorizCentreRadioButton.set_group(HorizAlignGroup);
741         HorizCentreRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
742         HorizAlignHBox.pack_start(HorizCentreRadioButton, false, false, 0);
744         HorizRightRadioButton.set_group(HorizAlignGroup);
745         HorizRightRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
746         HorizAlignHBox.pack_start(HorizRightRadioButton, false, false, 0);
748         HorizAlignHBox.pack_start(*(new Gtk::HBox()), true, true, 0); // centering strut
750         HorizAlign = prefs_get_double_attribute ("dialogs.gridtiler", "HorizAlign", 1);
751         if (HorizAlign == 0) {
752             HorizLeftRadioButton.set_active(TRUE);
753         }
754         else if (HorizAlign == 1) {
755             HorizCentreRadioButton.set_active(TRUE);
756         }
757         else if (HorizAlign == 2) {
758             HorizRightRadioButton.set_active(TRUE);
759         }
760         HorizAlignVBox.pack_start(HorizAlignHBox, false, false, MARGIN);
761         NoOfColsBox.pack_start(HorizAlignVBox, false, false, MARGIN);
762     }
764     SpinsHBox.pack_start(NoOfColsBox, false, false, MARGIN);
766     TileBox.pack_start(SpinsHBox, false, false, MARGIN);
768     {
769         /*#### Radio buttons to control spacing manually or to fit selection bbox ####*/
770         SpaceByBBoxRadioButton.set_label(_("Fit into selection box"));
771         SpaceByBBoxRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::Spacing_button_changed));
772         SpacingGroup = SpaceByBBoxRadioButton.get_group();
774         SpacingVBox.pack_start(SpaceByBBoxRadioButton, false, false, MARGIN);
776         SpaceManualRadioButton.set_label(_("Set spacing:"));
777         SpaceManualRadioButton.set_group(SpacingGroup);
778         SpaceManualRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::Spacing_button_changed));
779         SpacingVBox.pack_start(SpaceManualRadioButton, false, false, MARGIN);
781         TileBox.pack_start(SpacingVBox, false, false, MARGIN);
782     }
784     {
785         /*#### Y Padding ####*/
787         GtkWidget *i = sp_icon_new (Inkscape::ICON_SIZE_MENU, "clonetiler_per_row");
788         YPadBox.pack_start (*(Glib::wrap(i)), false, false, MARGIN);
790         YPadSpinner.set_digits(1);
791         YPadSpinner.set_increments(0.2, 2);
792         YPadSpinner.set_range(-10000, 10000);
793         double YPad = prefs_get_double_attribute ("dialogs.gridtiler", "YPad", 15);
794         YPadSpinner.set_value(YPad);
795         YPadBox.pack_start(YPadSpinner, true, true, MARGIN);
796         tips.set_tip(YPadSpinner, _("Vertical spacing between rows (px units)"));
797         YPadSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_ypad_spinbutton_changed));
798         gtk_size_group_add_widget(_col1, (GtkWidget *) YPadBox.gobj());
800         SizesHBox.pack_start(YPadBox, false, false, MARGIN);
801     }
803     {
804     Gtk::HBox *spacer = new Gtk::HBox;
805     SizesHBox.pack_start(*spacer, false, false, 0);
806     gtk_size_group_add_widget(_col2, (GtkWidget *) spacer->gobj());
807     }
809     {
810         /*#### X padding ####*/
812         GtkWidget *i = sp_icon_new (Inkscape::ICON_SIZE_MENU, "clonetiler_per_column");
813         XPadBox.pack_start (*(Glib::wrap(i)), false, false, MARGIN);
815         XPadSpinner.set_digits(1);
816         XPadSpinner.set_increments(0.2, 2);
817         XPadSpinner.set_range(-10000, 10000);
818         double XPad = prefs_get_double_attribute ("dialogs.gridtiler", "XPad", 15);
819         XPadSpinner.set_value(XPad);
820         XPadBox.pack_start(XPadSpinner, true, true, MARGIN);
821         tips.set_tip(XPadSpinner, _("Horizontal spacing between columns (px units)"));
822         XPadSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_xpad_spinbutton_changed));
823         gtk_size_group_add_widget(_col3, (GtkWidget *) XPadBox.gobj());
825         SizesHBox.pack_start(XPadBox, false, false, MARGIN);
826     }
829     TileBox.pack_start(SizesHBox, false, false, MARGIN);
831     mainVBox->pack_start(TileBox);
833     double SpacingType = prefs_get_double_attribute ("dialogs.gridtiler", "SpacingType", 15);
834     if (SpacingType>0) {
835         ManualSpacing=true;
836     } else {
837         ManualSpacing=false;
838     }
839     SpaceManualRadioButton.set_active(ManualSpacing);
840     SpaceByBBoxRadioButton.set_active(!ManualSpacing);
841     SizesHBox.set_sensitive (ManualSpacing);
843     //## The OK button
844     TileOkButton     = add_button(Gtk::Stock::APPLY,   GTK_RESPONSE_APPLY);
845     tips.set_tip((*TileOkButton), _("Arrange selected objects"));
847     show_all_children();
855 } //namespace Dialog
856 } //namespace UI
857 } //namespace Inkscape
859 //#########################################################################
860 //## E N D    O F    F I L E
861 //#########################################################################
864 /*
865   Local Variables:
866   mode:c++
867   c-file-style:"stroustrup"
868   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
869   indent-tabs-mode:nil
870   fill-column:99
871   End:
872 */
873 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :