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];
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 }
106 return 0;
107 }
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 ()
125 {
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"));
351 }
354 //#########################################################################
355 //## E V E N T S
356 //#########################################################################
359 void TileDialog::_apply()
360 {
361 Grid_Arrange();
362 }
365 /**
366 * changed value in # of columns spinbox.
367 */
368 void TileDialog::on_row_spinbutton_changed()
369 {
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;
388 }
390 /**
391 * changed value in # of rows spinbox.
392 */
393 void TileDialog::on_col_spinbutton_changed()
394 {
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;
413 }
415 /**
416 * changed value in x padding spinbox.
417 */
418 void TileDialog::on_xpad_spinbutton_changed()
419 {
421 prefs_set_double_attribute ("dialogs.gridtiler", "XPad", XPadSpinner.get_value());
423 }
425 /**
426 * changed value in y padding spinbox.
427 */
428 void TileDialog::on_ypad_spinbutton_changed()
429 {
431 prefs_set_double_attribute ("dialogs.gridtiler", "YPad", YPadSpinner.get_value());
433 }
436 /**
437 * checked/unchecked autosize Rows button.
438 */
439 void TileDialog::on_RowSize_checkbutton_changed()
440 {
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());
448 }
450 /**
451 * checked/unchecked autosize Rows button.
452 */
453 void TileDialog::on_ColSize_checkbutton_changed()
454 {
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());
463 }
465 /**
466 * changed value in columns spinbox.
467 */
468 void TileDialog::on_rowSize_spinbutton_changed()
469 {
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;
480 }
482 /**
483 * changed value in rows spinbox.
484 */
485 void TileDialog::on_colSize_spinbutton_changed()
486 {
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;
497 }
499 /**
500 * changed Radio button in Spacing group.
501 */
502 void TileDialog::Spacing_button_changed()
503 {
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());
511 }
513 /**
514 * changed Radio button in Vertical Align group.
515 */
516 void TileDialog::VertAlign_changed()
517 {
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 }
529 }
531 /**
532 * changed Radio button in Vertical Align group.
533 */
534 void TileDialog::HorizAlign_changed()
535 {
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 }
547 }
549 /**
550 * Desktop selection changed
551 */
552 void TileDialog::updateSelection()
553 {
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;
591 }
595 /*##########################
596 ## Experimental
597 ##########################*/
599 static void updateSelectionCallback(Inkscape::Application */*inkscape*/, Inkscape::Selection */*selection*/, TileDialog *dlg)
600 {
601 TileDialog *tledlg = (TileDialog *) dlg;
602 tledlg->updateSelection();
603 }
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)
614 {
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(" × ");
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();
868 }
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 :