Code

NR::Maybe => boost::optional
[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     boost::optional<NR::Rect> a = first->getBounds(from_2geom(sp_item_i2doc_affine(first)));
53     boost::optional<NR::Rect> b = second->getBounds(from_2geom(sp_item_i2doc_affine(second)));
55     if ( !a || !b ) {
56         // FIXME?
57         return 0;
58     }
60     double const a_height = a->dimensions()[Y];
61     double const b_height = b->dimensions()[Y];
62     
63     bool a_in_b_vert = false;
64     if ((a->min()[Y] < b->min()[Y] + 0.1) && (a->min()[Y] > b->min()[Y] - b_height)) {
65         a_in_b_vert = true;
66     } else if ((b->min()[Y] < a->min()[Y] + 0.1) && (b->min()[Y] > a->min()[Y] - a_height)) {
67         a_in_b_vert = true;
68     } else if (b->min()[Y] == a->min()[Y]) {
69         a_in_b_vert = true;
70     } else {
71         a_in_b_vert = false;
72     }
74     if (!a_in_b_vert) {
75         return -1;
76     }
77     if (a_in_b_vert && a->min()[X] > b->min()[X]) {
78         return 1;
79     }
80     if (a_in_b_vert && a->min()[X] < b->min()[X]) {
81         return -1;
82     }
83     return 0;
84 }
86 /*
87  *    Sort items by their y co-ordinates.
88  */
89 int
90 sp_compare_y_position(SPItem *first, SPItem *second)
91 {
92     boost::optional<NR::Rect> a = first->getBounds(from_2geom(sp_item_i2doc_affine(first)));
93     boost::optional<NR::Rect> b = second->getBounds(from_2geom(sp_item_i2doc_affine(second)));
95     if ( !a || !b ) {
96         // FIXME?
97         return 0;
98     }
100     if (a->min()[NR::Y] > b->min()[NR::Y]) {
101         return 1;
102     }
103     if (a->min()[NR::Y] < b->min()[NR::Y]) {
104         return -1;
105     }
106     
107     return 0;
110 namespace Inkscape {
111 namespace UI {
112 namespace Dialog {
115 //#########################################################################
116 //## E V E N T S
117 //#########################################################################
119 /*
120  *
121  * This arranges the selection in a grid pattern.
122  *
123  */
125 void TileDialog::Grid_Arrange ()
128     int cnt,row_cnt,col_cnt,a,row,col;
129     double grid_left,grid_top,col_width,row_height,paddingx,paddingy,width, height, new_x, new_y,cx,cy;
130     double total_col_width,total_row_height;
131     col_width = 0;
132     row_height = 0;
133     total_col_width=0;
134     total_row_height=0;
136     // check for correct numbers in the row- and col-spinners
137     on_col_spinbutton_changed();
138     on_row_spinbutton_changed();
140     // set padding to manual values
141     paddingx = XPadSpinner.get_value();
142     paddingy = YPadSpinner.get_value();
144     std::vector<double> row_heights;
145     std::vector<double> col_widths;
146     std::vector<double> row_ys;
147     std::vector<double> col_xs;
149     int NoOfCols = NoOfColsSpinner.get_value_as_int();
150     int NoOfRows = NoOfRowsSpinner.get_value_as_int();
152     width = 0;
153     for (a=0;a<NoOfCols; a++){
154         col_widths.push_back(width);
155     }
157     height = 0;
158     for (a=0;a<NoOfRows; a++){
159         row_heights.push_back(height);
160     }
161     grid_left = 99999;
162     grid_top = 99999;
164     SPDesktop *desktop = getDesktop();
165     sp_document_ensure_up_to_date(sp_desktop_document(desktop));
167     Inkscape::Selection *selection = sp_desktop_selection (desktop);
168     const GSList *items = selection->itemList();
169     cnt=0;
170     for (; items != NULL; items = items->next) {
171         SPItem *item = SP_ITEM(items->data);
172         boost::optional<NR::Rect> b = item->getBounds(from_2geom(sp_item_i2doc_affine(item)));
173         if (!b) {
174             continue;
175         }
177         width = b->dimensions()[NR::X];
178         height = b->dimensions()[NR::Y];
180         cx = b->midpoint()[NR::X];
181         cy = b->midpoint()[NR::Y];
183         if (b->min()[NR::X] < grid_left) {
184             grid_left = b->min()[NR::X];
185         }
186         if (b->min()[NR::Y] < grid_top) {
187             grid_top = b->min()[NR::Y];
188         }
189         if (width > col_width) {
190             col_width = width;
191         }
192         if (height > row_height) {
193             row_height = height;
194         }
195     }
198     // require the sorting done before we can calculate row heights etc.
200     const GSList *items2 = selection->itemList();
201     GSList *rev = g_slist_copy((GSList *) items2);
202     GSList *sorted = NULL;
203     rev = g_slist_sort(rev, (GCompareFunc) sp_compare_y_position);
204     sorted = g_slist_sort(rev, (GCompareFunc) sp_compare_x_position);
207     // Calculate individual Row and Column sizes if necessary
210         cnt=0;
211         const GSList *sizes = sorted;
212         for (; sizes != NULL; sizes = sizes->next) {
213             SPItem *item = SP_ITEM(sizes->data);
214             boost::optional<NR::Rect> b = item->getBounds(from_2geom(sp_item_i2doc_affine(item)));
215             if (b) {
216                 width = b->dimensions()[NR::X];
217                 height = b->dimensions()[NR::Y];
218                 if (width > col_widths[(cnt % NoOfCols)]) {
219                     col_widths[(cnt % NoOfCols)] = width;
220                 }
221                 if (height > row_heights[(cnt / NoOfCols)]) {
222                     row_heights[(cnt / NoOfCols)] = height;
223                 }
224             }
226             cnt++;
227         }
230     /// Make sure the top and left of the grid dont move by compensating for align values.
231     if (RowHeightButton.get_active()){
232         grid_top = grid_top - (((row_height - row_heights[0]) / 2)*(VertAlign));
233     }
234     if (ColumnWidthButton.get_active()){
235         grid_left = grid_left - (((col_width - col_widths[0]) /2)*(HorizAlign));
236     }
238     #ifdef DEBUG_GRID_ARRANGE
239      g_print("\n cx = %f cy= %f gridleft=%f",cx,cy,grid_left);
240     #endif
242     // Calculate total widths and heights, allowing for columns and rows non uniformly sized.
244     if (ColumnWidthButton.get_active()){
245         total_col_width = col_width * NoOfCols;
246         col_widths.clear();
247         for (a=0;a<NoOfCols; a++){
248             col_widths.push_back(col_width);
249         }
250     } else {
251         for (a = 0; a < (int)col_widths.size(); a++)
252         {
253           total_col_width += col_widths[a] ;
254         }
255     }
257     if (RowHeightButton.get_active()){
258         total_row_height = row_height * NoOfRows;
259         row_heights.clear();
260         for (a=0;a<NoOfRows; a++){
261             row_heights.push_back(row_height);
262         }
263     } else {
264         for (a = 0; a < (int)row_heights.size(); a++)
265         {
266           total_row_height += row_heights[a] ;
267         }
268     }
271     boost::optional<NR::Rect> sel_bbox = selection->bounds();
272     // Fit to bbox, calculate padding between rows accordingly.
273     if ( sel_bbox && !SpaceManualRadioButton.get_active() ){
274 #ifdef DEBUG_GRID_ARRANGE
275 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));
276 #endif
277         paddingx = (sel_bbox->extent(NR::X) - total_col_width) / (NoOfCols -1);
278         paddingy = (sel_bbox->extent(NR::Y) - total_row_height) / (NoOfRows -1);
279     }
281 /*
282     Horizontal align  - Left    = 0
283                         Centre  = 1
284                         Right   = 2
286     Vertical align    - Top     = 0
287                         Middle  = 1
288                         Bottom  = 2
290     X position is calculated by taking the grids left co-ord, adding the distance to the column,
291    then adding 1/2 the spacing multiplied by the align variable above,
292    Y position likewise, takes the top of the grid, adds the y to the current row then adds the padding in to align it.
294 */
296     // Calculate row and column x and y coords required to allow for columns and rows which are non uniformly sized.
298     for (a=0;a<NoOfCols; a++){
299         if (a<1) col_xs.push_back(0);
300         else col_xs.push_back(col_widths[a-1]+paddingx+col_xs[a-1]);
301     }
304     for (a=0;a<NoOfRows; a++){
305         if (a<1) row_ys.push_back(0);
306         else row_ys.push_back(row_heights[a-1]+paddingy+row_ys[a-1]);
307     }
309     cnt=0;
310   for (row_cnt=0; ((sorted != NULL) && (row_cnt<NoOfRows)); row_cnt++) {
312              GSList *current_row = NULL;
313              for (col_cnt = 0; ((sorted != NULL) && (col_cnt<NoOfCols)); col_cnt++) {
314                  current_row = g_slist_append (current_row, sorted->data);
315                  sorted = sorted->next;
316              }
318              for (; current_row != NULL; current_row = current_row->next) {
319                  SPItem *item=SP_ITEM(current_row->data);
320                  Inkscape::XML::Node *repr = SP_OBJECT_REPR(item);
321                  boost::optional<NR::Rect> b = item->getBounds(from_2geom(sp_item_i2doc_affine(item)));
322                  NR::Point min;
323                  if (b) {
324                      width = b->dimensions()[NR::X];
325                      height = b->dimensions()[NR::Y];
326                      min = b->min();
327                  } else {
328                      width = height = 0;
329                      min = NR::Point(0, 0);
330                  }
332                  row = cnt / NoOfCols;
333                  col = cnt % NoOfCols;
335                  new_x = grid_left + (((col_widths[col] - width)/2)*HorizAlign) + col_xs[col];
336                  new_y = grid_top + (((row_heights[row] - height)/2)*VertAlign) + row_ys[row];
338                  // signs are inverted between x and y due to y inversion
339                  NR::Point move = NR::Point(new_x - min[NR::X], min[NR::Y] - new_y);
340                  NR::Matrix const &affine = NR::Matrix(NR::translate(move));
341                  sp_item_set_i2d_affine(item, sp_item_i2d_affine(item) * to_2geom(affine));
342                  sp_item_write_transform(item, repr, item->transform,  NULL);
343                  SP_OBJECT (current_row->data)->updateRepr();
344                  cnt +=1;
345              }
346              g_slist_free (current_row);
347     }
349     NRRect b;
350             selection->bounds(&b);
352             sp_document_done (sp_desktop_document (desktop), SP_VERB_SELECTION_GRIDTILE, 
353                               _("Arrange in a grid"));
358 //#########################################################################
359 //## E V E N T S
360 //#########################################################################
363 void TileDialog::_apply()
365         Grid_Arrange();
369 /**
370  * changed value in # of columns spinbox.
371  */
372 void TileDialog::on_row_spinbutton_changed()
374     // quit if run by the attr_changed listener
375     if (updating) {
376             return;
377         }
379     // in turn, prevent listener from responding
380     updating = true;
381     SPDesktop *desktop = getDesktop();
383     Inkscape::Selection *selection = sp_desktop_selection (desktop);
385     GSList const *items = selection->itemList();
386     int selcount = g_slist_length((GSList *)items);
388     double PerCol = ceil(selcount / NoOfColsSpinner.get_value());
389     NoOfRowsSpinner.set_value(PerCol);
390     prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", NoOfColsSpinner.get_value());
391     updating=false;
394 /**
395  * changed value in # of rows spinbox.
396  */
397 void TileDialog::on_col_spinbutton_changed()
399     // quit if run by the attr_changed listener
400     if (updating) {
401             return;
402         }
404     // in turn, prevent listener from responding
405     updating = true;
406     SPDesktop *desktop = getDesktop();
407     Inkscape::Selection *selection = sp_desktop_selection (desktop);
409     GSList const *items = selection->itemList();
410     int selcount = g_slist_length((GSList *)items);
412     double PerRow = ceil(selcount / NoOfRowsSpinner.get_value());
413     NoOfColsSpinner.set_value(PerRow);
414     prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", PerRow);
416     updating=false;
419 /**
420  * changed value in x padding spinbox.
421  */
422 void TileDialog::on_xpad_spinbutton_changed()
425     prefs_set_double_attribute ("dialogs.gridtiler", "XPad", XPadSpinner.get_value());
429 /**
430  * changed value in y padding spinbox.
431  */
432 void TileDialog::on_ypad_spinbutton_changed()
435     prefs_set_double_attribute ("dialogs.gridtiler", "YPad", YPadSpinner.get_value());
440 /**
441  * checked/unchecked autosize Rows button.
442  */
443 void TileDialog::on_RowSize_checkbutton_changed()
446    if (RowHeightButton.get_active()) {
447        prefs_set_double_attribute ("dialogs.gridtiler", "AutoRowSize", 20);
448    } else {
449        prefs_set_double_attribute ("dialogs.gridtiler", "AutoRowSize", -20);
450    }
451    RowHeightBox.set_sensitive ( !RowHeightButton.get_active());
454 /**
455  * checked/unchecked autosize Rows button.
456  */
457 void TileDialog::on_ColSize_checkbutton_changed()
460    if (ColumnWidthButton.get_active()) {
461        prefs_set_double_attribute ("dialogs.gridtiler", "AutoColSize", 20);
462    } else {
463        prefs_set_double_attribute ("dialogs.gridtiler", "AutoColSize", -20);
464    }
465    ColumnWidthBox.set_sensitive ( !ColumnWidthButton.get_active());
469 /**
470  * changed value in columns spinbox.
471  */
472 void TileDialog::on_rowSize_spinbutton_changed()
474     // quit if run by the attr_changed listener
475     if (updating) {
476             return;
477         }
479     // in turn, prevent listener from responding
480     updating = true;
481     prefs_set_double_attribute ("dialogs.gridtiler", "RowHeight", RowHeightSpinner.get_value());
482     updating=false;
486 /**
487  * changed value in rows spinbox.
488  */
489 void TileDialog::on_colSize_spinbutton_changed()
491     // quit if run by the attr_changed listener
492     if (updating) {
493             return;
494         }
496     // in turn, prevent listener from responding
497     updating = true;
498     prefs_set_double_attribute ("dialogs.gridtiler", "ColWidth", ColumnWidthSpinner.get_value());
499     updating=false;
503 /**
504  * changed Radio button in Spacing group.
505  */
506 void TileDialog::Spacing_button_changed()
508    if (SpaceManualRadioButton.get_active()) {
509        prefs_set_double_attribute ("dialogs.gridtiler", "SpacingType", 20);
510    } else {
511        prefs_set_double_attribute ("dialogs.gridtiler", "SpacingType", -20);
512    }
514    SizesHBox.set_sensitive ( SpaceManualRadioButton.get_active());
517 /**
518  * changed Radio button in Vertical Align group.
519  */
520 void TileDialog::VertAlign_changed()
522    if (VertTopRadioButton.get_active()) {
523        VertAlign = 0;
524        prefs_set_double_attribute ("dialogs.gridtiler", "VertAlign", 0);
525    } else if (VertCentreRadioButton.get_active()){
526        VertAlign = 1;
527        prefs_set_double_attribute ("dialogs.gridtiler", "VertAlign", 1);
528    } else if (VertBotRadioButton.get_active()){
529        VertAlign = 2;
530        prefs_set_double_attribute ("dialogs.gridtiler", "VertAlign", 2);
531    }
535 /**
536  * changed Radio button in Vertical Align group.
537  */
538 void TileDialog::HorizAlign_changed()
540    if (HorizLeftRadioButton.get_active()) {
541        HorizAlign = 0;
542        prefs_set_double_attribute ("dialogs.gridtiler", "HorizAlign", 0);
543    } else if (HorizCentreRadioButton.get_active()){
544        HorizAlign = 1;
545        prefs_set_double_attribute ("dialogs.gridtiler", "HorizAlign", 1);
546    } else if (HorizRightRadioButton.get_active()){
547        HorizAlign = 2;
548        prefs_set_double_attribute ("dialogs.gridtiler", "HorizAlign", 2);
549    }
553 /**
554  * Desktop selection changed
555  */
556 void TileDialog::updateSelection()
558     double col_width, row_height;
559     // quit if run by the attr_changed listener
560     if (updating) {
561             return;
562         }
564     col_width=0;
565     row_height=0;
566     // in turn, prevent listener from responding
567     updating = true;
568     SPDesktop *desktop = getDesktop();
569     Inkscape::Selection *selection = sp_desktop_selection (desktop);
570     const GSList *items = selection->itemList();
571     int selcount = g_slist_length((GSList *)items);
573     if (NoOfColsSpinner.get_value()>1){
574         // Update the number of rows assuming number of columns wanted remains same.
575         double NoOfRows = ceil(selcount / NoOfColsSpinner.get_value());
576         NoOfRowsSpinner.set_value(NoOfRows);
578         // if the selection has less than the number set for one row, reduce it appropriately
579         if (selcount<NoOfColsSpinner.get_value()) {
580             double NoOfCols = ceil(selcount / NoOfRowsSpinner.get_value());
581             NoOfColsSpinner.set_value(NoOfCols);
582             prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", NoOfCols);
583         }
584     } else {
585         NoOfColsSpinner.set_value(selcount);
586         prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", selcount);
588     }
590     updating=false;
596 /*##########################
597 ## Experimental
598 ##########################*/
600 static void updateSelectionCallback(Inkscape::Application */*inkscape*/, Inkscape::Selection */*selection*/, TileDialog *dlg)
602     TileDialog *tledlg = (TileDialog *) dlg;
603     tledlg->updateSelection();
607 //#########################################################################
608 //## C O N S T R U C T O R    /    D E S T R U C T O R
609 //#########################################################################
610 /**
611  * Constructor
612  */
613 TileDialog::TileDialog()
614     : UI::Widget::Panel("", "dialogs.gridtiler", SP_VERB_SELECTION_GRIDTILE)
616      // bool used by spin button callbacks to stop loops where they change each other.
617     updating = false;
619     // could not do this in gtkmm - there's no Gtk::SizeGroup public constructor (!)
620     GtkSizeGroup *_col1 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
621     GtkSizeGroup *_col2 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
622     GtkSizeGroup *_col3 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
624     {
625         // Selection Change signal
626         g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (updateSelectionCallback), this);
627     }
629     Gtk::Box *contents = _getContents();
631 #define MARGIN 2
633     //##Set up the panel
635     SPDesktop *desktop = getDesktop();
637     Inkscape::Selection *selection = sp_desktop_selection (desktop);
638     int selcount = 1;
639     if (!selection->isEmpty()) {
640         GSList const *items = selection->itemList();
641         selcount = g_slist_length((GSList *)items);
642     }
645     /*#### Number of Rows ####*/
647     double PerRow = selcount;
648     double PerCol = 1;
650     #ifdef DEBUG_GRID_ARRANGE
651         g_print("/n PerRox = %f PerCol = %f selcount = %d",PerRow,PerCol,selcount);
652     #endif
654     NoOfRowsLabel.set_label(_("Rows:"));
655     NoOfRowsBox.pack_start(NoOfRowsLabel, false, false, MARGIN);
657     NoOfRowsSpinner.set_digits(0);
658     NoOfRowsSpinner.set_increments(1, 5);
659     NoOfRowsSpinner.set_range(1.0, 100.0);
660     NoOfRowsSpinner.set_value(PerCol);
661     NoOfRowsSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_col_spinbutton_changed));
662     tips.set_tip(NoOfRowsSpinner, _("Number of rows"));
663     NoOfRowsBox.pack_start(NoOfRowsSpinner, false, false, MARGIN);
664     gtk_size_group_add_widget(_col1, (GtkWidget *) NoOfRowsBox.gobj());
666     RowHeightButton.set_label(_("Equal height"));
667     double AutoRow = prefs_get_double_attribute ("dialogs.gridtiler", "AutoRowSize", 15);
668     if (AutoRow>0)
669          AutoRowSize=true;
670     else
671          AutoRowSize=false;
672     RowHeightButton.set_active(AutoRowSize);
674     NoOfRowsBox.pack_start(RowHeightButton, false, false, MARGIN);
676     tips.set_tip(RowHeightButton, _("If not set, each row has the height of the tallest object in it"));
677     RowHeightButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::on_RowSize_checkbutton_changed));
679  {
680         /*#### Radio buttons to control vertical alignment ####*/
682         VertAlignLabel.set_label(_("Align:"));
683         VertAlignHBox.pack_start(VertAlignLabel, false, false, MARGIN);
685         VertTopRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
686         VertAlignGroup = VertTopRadioButton.get_group();
687         VertAlignVBox.pack_start(VertTopRadioButton, false, false, 0);
689         VertCentreRadioButton.set_group(VertAlignGroup);
690         VertCentreRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
691         VertAlignVBox.pack_start(VertCentreRadioButton, false, false, 0);
693         VertBotRadioButton.set_group(VertAlignGroup);
694         VertBotRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
695         VertAlignVBox.pack_start(VertBotRadioButton, false, false, 0);
697         VertAlign = prefs_get_double_attribute ("dialogs.gridtiler", "VertAlign", 1);
698         if (VertAlign == 0) {
699             VertTopRadioButton.set_active(TRUE);
700         }
701         else if (VertAlign == 1) {
702             VertCentreRadioButton.set_active(TRUE);
703         }
704         else if (VertAlign == 2){
705             VertBotRadioButton.set_active(TRUE);
706         }
707         VertAlignHBox.pack_start(VertAlignVBox, false, false, MARGIN);
708         NoOfRowsBox.pack_start(VertAlignHBox, false, false, MARGIN);
709     }
711     SpinsHBox.pack_start(NoOfRowsBox, false, false, MARGIN);
714     /*#### Label for X ####*/
715     padXByYLabel.set_label(" ");
716     XByYLabelVBox.pack_start(padXByYLabel, false, false, MARGIN);
717     XByYLabel.set_markup(" &#215; ");
718     XByYLabelVBox.pack_start(XByYLabel, false, false, MARGIN);
719     SpinsHBox.pack_start(XByYLabelVBox, false, false, MARGIN);
720     gtk_size_group_add_widget(_col2, (GtkWidget *) XByYLabelVBox.gobj());
722     /*#### Number of columns ####*/
724     NoOfColsLabel.set_label(_("Columns:"));
725     NoOfColsBox.pack_start(NoOfColsLabel, false, false, MARGIN);
727     NoOfColsSpinner.set_digits(0);
728     NoOfColsSpinner.set_increments(1, 5);
729     NoOfColsSpinner.set_range(1.0, 100.0);
730     NoOfColsSpinner.set_value(PerRow);
731     NoOfColsSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_row_spinbutton_changed));
732     tips.set_tip(NoOfColsSpinner, _("Number of columns"));
733     NoOfColsBox.pack_start(NoOfColsSpinner, false, false, MARGIN);
734     gtk_size_group_add_widget(_col3, (GtkWidget *) NoOfColsBox.gobj());
736     ColumnWidthButton.set_label(_("Equal width"));
737     double AutoCol = prefs_get_double_attribute ("dialogs.gridtiler", "AutoColSize", 15);
738     if (AutoCol>0)
739          AutoColSize=true;
740     else
741          AutoColSize=false;
742     ColumnWidthButton.set_active(AutoColSize);
743     NoOfColsBox.pack_start(ColumnWidthButton, false, false, MARGIN);
745     tips.set_tip(ColumnWidthButton, _("If not set, each column has the width of the widest object in it"));
746     ColumnWidthButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::on_ColSize_checkbutton_changed));
749     {
750         /*#### Radio buttons to control horizontal alignment ####*/
752         HorizAlignLabel.set_label(_("Align:"));
753         HorizAlignVBox.pack_start(HorizAlignLabel, false, false, MARGIN);
755         HorizAlignHBox.pack_start(*(new Gtk::HBox()), true, true, 0); // centering strut
757         HorizLeftRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
758         HorizAlignGroup = HorizLeftRadioButton.get_group();
759         HorizAlignHBox.pack_start(HorizLeftRadioButton, false, false, 0);
761         HorizCentreRadioButton.set_group(HorizAlignGroup);
762         HorizCentreRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
763         HorizAlignHBox.pack_start(HorizCentreRadioButton, false, false, 0);
765         HorizRightRadioButton.set_group(HorizAlignGroup);
766         HorizRightRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
767         HorizAlignHBox.pack_start(HorizRightRadioButton, false, false, 0);
769         HorizAlignHBox.pack_start(*(new Gtk::HBox()), true, true, 0); // centering strut
771         HorizAlign = prefs_get_double_attribute ("dialogs.gridtiler", "HorizAlign", 1);
772         if (HorizAlign == 0) {
773             HorizLeftRadioButton.set_active(TRUE);
774         }
775         else if (HorizAlign == 1) {
776             HorizCentreRadioButton.set_active(TRUE);
777         }
778         else if (HorizAlign == 2) {
779             HorizRightRadioButton.set_active(TRUE);
780         }
781         HorizAlignVBox.pack_start(HorizAlignHBox, false, false, MARGIN);
782         NoOfColsBox.pack_start(HorizAlignVBox, false, false, MARGIN);
783     }
785     SpinsHBox.pack_start(NoOfColsBox, false, false, MARGIN);
787     TileBox.pack_start(SpinsHBox, false, false, MARGIN);
789     {
790         /*#### Radio buttons to control spacing manually or to fit selection bbox ####*/
791         SpaceByBBoxRadioButton.set_label(_("Fit into selection box"));
792         SpaceByBBoxRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::Spacing_button_changed));
793         SpacingGroup = SpaceByBBoxRadioButton.get_group();
795         SpacingVBox.pack_start(SpaceByBBoxRadioButton, false, false, MARGIN);
797         SpaceManualRadioButton.set_label(_("Set spacing:"));
798         SpaceManualRadioButton.set_group(SpacingGroup);
799         SpaceManualRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::Spacing_button_changed));
800         SpacingVBox.pack_start(SpaceManualRadioButton, false, false, MARGIN);
802         TileBox.pack_start(SpacingVBox, false, false, MARGIN);
803     }
805     {
806         /*#### Y Padding ####*/
808         GtkWidget *i = sp_icon_new (Inkscape::ICON_SIZE_MENU, "clonetiler_per_row");
809         YPadBox.pack_start (*(Glib::wrap(i)), false, false, MARGIN);
811         YPadSpinner.set_digits(1);
812         YPadSpinner.set_increments(0.2, 2);
813         YPadSpinner.set_range(-10000, 10000);
814         double YPad = prefs_get_double_attribute ("dialogs.gridtiler", "YPad", 15);
815         YPadSpinner.set_value(YPad);
816         YPadBox.pack_start(YPadSpinner, true, true, MARGIN);
817         tips.set_tip(YPadSpinner, _("Vertical spacing between rows (px units)"));
818         YPadSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_ypad_spinbutton_changed));
819         gtk_size_group_add_widget(_col1, (GtkWidget *) YPadBox.gobj());
821         SizesHBox.pack_start(YPadBox, false, false, MARGIN);
822     }
824     {
825     Gtk::HBox *spacer = new Gtk::HBox;
826     SizesHBox.pack_start(*spacer, false, false, 0);
827     gtk_size_group_add_widget(_col2, (GtkWidget *) spacer->gobj());
828     }
830     {
831         /*#### X padding ####*/
833         GtkWidget *i = sp_icon_new (Inkscape::ICON_SIZE_MENU, "clonetiler_per_column");
834         XPadBox.pack_start (*(Glib::wrap(i)), false, false, MARGIN);
836         XPadSpinner.set_digits(1);
837         XPadSpinner.set_increments(0.2, 2);
838         XPadSpinner.set_range(-10000, 10000);
839         double XPad = prefs_get_double_attribute ("dialogs.gridtiler", "XPad", 15);
840         XPadSpinner.set_value(XPad);
841         XPadBox.pack_start(XPadSpinner, true, true, MARGIN);
842         tips.set_tip(XPadSpinner, _("Horizontal spacing between columns (px units)"));
843         XPadSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_xpad_spinbutton_changed));
844         gtk_size_group_add_widget(_col3, (GtkWidget *) XPadBox.gobj());
846         SizesHBox.pack_start(XPadBox, false, false, MARGIN);
847     }
850     TileBox.pack_start(SizesHBox, false, false, MARGIN);
852     contents->pack_start(TileBox);
854     double SpacingType = prefs_get_double_attribute ("dialogs.gridtiler", "SpacingType", 15);
855     if (SpacingType>0) {
856         ManualSpacing=true;
857     } else {
858         ManualSpacing=false;
859     }
860     SpaceManualRadioButton.set_active(ManualSpacing);
861     SpaceByBBoxRadioButton.set_active(!ManualSpacing);
862     SizesHBox.set_sensitive (ManualSpacing);
864     //## The OK button
865     TileOkButton = addResponseButton(_("Arrange"), GTK_RESPONSE_APPLY);
866     tips.set_tip((*TileOkButton), _("Arrange selected objects"));
868     show_all_children();
876 } //namespace Dialog
877 } //namespace UI
878 } //namespace Inkscape
880 //#########################################################################
881 //## E N D    O F    F I L E
882 //#########################################################################
885 /*
886   Local Variables:
887   mode:c++
888   c-file-style:"stroustrup"
889   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
890   indent-tabs-mode:nil
891   fill-column:99
892   End:
893 */
894 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :