72b1cdaa8dadbb4329d711b025fad3a512f59f5b
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->invokeBbox(sp_item_i2doc_affine(first));
53 double const a_height = a.dimensions()[Y];
55 NR::Rect const b = second->invokeBbox(sp_item_i2doc_affine(second));
56 double const b_height = b.dimensions()[Y];
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->invokeBbox(sp_item_i2doc_affine(first));
88 NR::Rect const b = second->invokeBbox(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 }
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 ()
116 {
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->invokeBbox(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->invokeBbox(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->invokeBbox(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));
331 }
334 //#########################################################################
335 //## E V E N T S
336 //#########################################################################
339 void TileDialog::_apply()
340 {
341 Grid_Arrange();
342 }
345 /**
346 * changed value in # of columns spinbox.
347 */
348 void TileDialog::on_row_spinbutton_changed()
349 {
350 // quit if run by the attr_changed listener
351 if (updating) {
352 return;
353 }
355 // in turn, prevent listener from responding
356 updating = true;
357 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
359 Inkscape::Selection *selection = sp_desktop_selection (desktop);
361 GSList const *items = selection->itemList();
362 int selcount = g_slist_length((GSList *)items);
364 double PerCol = ceil(selcount / NoOfColsSpinner.get_value());
365 NoOfRowsSpinner.set_value(PerCol);
366 prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", NoOfColsSpinner.get_value());
367 updating=false;
368 }
370 /**
371 * changed value in # of rows spinbox.
372 */
373 void TileDialog::on_col_spinbutton_changed()
374 {
375 // quit if run by the attr_changed listener
376 if (updating) {
377 return;
378 }
380 // in turn, prevent listener from responding
381 updating = true;
382 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
383 Inkscape::Selection *selection = sp_desktop_selection (desktop);
385 GSList const *items = selection->itemList();
386 int selcount = g_slist_length((GSList *)items);
388 double PerRow = ceil(selcount / NoOfRowsSpinner.get_value());
389 NoOfColsSpinner.set_value(PerRow);
390 prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", PerRow);
392 updating=false;
393 }
395 /**
396 * changed value in x padding spinbox.
397 */
398 void TileDialog::on_xpad_spinbutton_changed()
399 {
401 prefs_set_double_attribute ("dialogs.gridtiler", "XPad", XPadSpinner.get_value());
403 }
405 /**
406 * changed value in y padding spinbox.
407 */
408 void TileDialog::on_ypad_spinbutton_changed()
409 {
411 prefs_set_double_attribute ("dialogs.gridtiler", "YPad", YPadSpinner.get_value());
413 }
416 /**
417 * checked/unchecked autosize Rows button.
418 */
419 void TileDialog::on_RowSize_checkbutton_changed()
420 {
422 if (RowHeightButton.get_active()) {
423 prefs_set_double_attribute ("dialogs.gridtiler", "AutoRowSize", 20);
424 } else {
425 prefs_set_double_attribute ("dialogs.gridtiler", "AutoRowSize", -20);
426 }
427 RowHeightBox.set_sensitive ( !RowHeightButton.get_active());
428 }
430 /**
431 * checked/unchecked autosize Rows button.
432 */
433 void TileDialog::on_ColSize_checkbutton_changed()
434 {
436 if (ColumnWidthButton.get_active()) {
437 prefs_set_double_attribute ("dialogs.gridtiler", "AutoColSize", 20);
438 } else {
439 prefs_set_double_attribute ("dialogs.gridtiler", "AutoColSize", -20);
440 }
441 ColumnWidthBox.set_sensitive ( !ColumnWidthButton.get_active());
443 }
445 /**
446 * changed value in columns spinbox.
447 */
448 void TileDialog::on_rowSize_spinbutton_changed()
449 {
450 // quit if run by the attr_changed listener
451 if (updating) {
452 return;
453 }
455 // in turn, prevent listener from responding
456 updating = true;
457 prefs_set_double_attribute ("dialogs.gridtiler", "RowHeight", RowHeightSpinner.get_value());
458 updating=false;
460 }
462 /**
463 * changed value in rows spinbox.
464 */
465 void TileDialog::on_colSize_spinbutton_changed()
466 {
467 // quit if run by the attr_changed listener
468 if (updating) {
469 return;
470 }
472 // in turn, prevent listener from responding
473 updating = true;
474 prefs_set_double_attribute ("dialogs.gridtiler", "ColWidth", ColumnWidthSpinner.get_value());
475 updating=false;
477 }
479 /**
480 * changed Radio button in Spacing group.
481 */
482 void TileDialog::Spacing_button_changed()
483 {
484 if (SpaceManualRadioButton.get_active()) {
485 prefs_set_double_attribute ("dialogs.gridtiler", "SpacingType", 20);
486 } else {
487 prefs_set_double_attribute ("dialogs.gridtiler", "SpacingType", -20);
488 }
490 SizesHBox.set_sensitive ( SpaceManualRadioButton.get_active());
491 }
493 /**
494 * changed Radio button in Vertical Align group.
495 */
496 void TileDialog::VertAlign_changed()
497 {
498 if (VertTopRadioButton.get_active()) {
499 VertAlign = 0;
500 prefs_set_double_attribute ("dialogs.gridtiler", "VertAlign", 0);
501 } else if (VertCentreRadioButton.get_active()){
502 VertAlign = 1;
503 prefs_set_double_attribute ("dialogs.gridtiler", "VertAlign", 1);
504 } else if (VertBotRadioButton.get_active()){
505 VertAlign = 2;
506 prefs_set_double_attribute ("dialogs.gridtiler", "VertAlign", 2);
507 }
509 }
511 /**
512 * changed Radio button in Vertical Align group.
513 */
514 void TileDialog::HorizAlign_changed()
515 {
516 if (HorizLeftRadioButton.get_active()) {
517 HorizAlign = 0;
518 prefs_set_double_attribute ("dialogs.gridtiler", "HorizAlign", 0);
519 } else if (HorizCentreRadioButton.get_active()){
520 HorizAlign = 1;
521 prefs_set_double_attribute ("dialogs.gridtiler", "HorizAlign", 1);
522 } else if (HorizRightRadioButton.get_active()){
523 HorizAlign = 2;
524 prefs_set_double_attribute ("dialogs.gridtiler", "HorizAlign", 2);
525 }
527 }
529 /**
530 * Desktop selection changed
531 */
532 void TileDialog::updateSelection()
533 {
534 double col_width, row_height;
535 // quit if run by the attr_changed listener
536 if (updating) {
537 return;
538 }
540 col_width=0;
541 row_height=0;
542 // in turn, prevent listener from responding
543 updating = true;
544 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
545 Inkscape::Selection *selection = sp_desktop_selection (desktop);
546 const GSList *items = selection->itemList();
547 int selcount = g_slist_length((GSList *)items);
549 if (NoOfColsSpinner.get_value()>1){
550 // Update the number of rows assuming number of columns wanted remains same.
551 double NoOfRows = ceil(selcount / NoOfColsSpinner.get_value());
552 NoOfRowsSpinner.set_value(NoOfRows);
554 // if the selection has less than the number set for one row, reduce it appropriately
555 if (selcount<NoOfColsSpinner.get_value()) {
556 double NoOfCols = ceil(selcount / NoOfRowsSpinner.get_value());
557 NoOfColsSpinner.set_value(NoOfCols);
558 prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", NoOfCols);
559 }
560 } else {
561 NoOfColsSpinner.set_value(selcount);
562 prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", selcount);
564 }
566 updating=false;
568 }
574 /*##########################
575 ## Experimental
576 ##########################*/
578 static void updateSelectionCallback(Inkscape::Application *inkscape, Inkscape::Selection *selection, TileDialog *dlg)
579 {
580 TileDialog *tledlg = (TileDialog *) dlg;
581 tledlg->updateSelection();
582 }
585 //#########################################################################
586 //## C O N S T R U C T O R / D E S T R U C T O R
587 //#########################################################################
588 /**
589 * Constructor
590 */
591 TileDialog::TileDialog()
592 : Dialog ("dialogs.gridtiler", SP_VERB_SELECTION_GRIDTILE)
593 {
594 // bool used by spin button callbacks to stop loops where they change each other.
595 updating = false;
597 // could not do this in gtkmm - there's no Gtk::SizeGroup public constructor (!)
598 GtkSizeGroup *_col1 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
599 GtkSizeGroup *_col2 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
600 GtkSizeGroup *_col3 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
602 {
603 // Selection Change signal
604 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (updateSelectionCallback), this);
605 }
607 Gtk::VBox *mainVBox = get_vbox();
609 #define MARGIN 2
611 //##Set up the panel
613 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
615 Inkscape::Selection *selection = sp_desktop_selection (desktop);
616 int selcount = 1;
617 if (!selection->isEmpty()) {
618 GSList const *items = selection->itemList();
619 selcount = g_slist_length((GSList *)items);
620 }
623 /*#### Number of Rows ####*/
625 double PerRow = selcount;
626 double PerCol = 1;
628 #ifdef DEBUG_GRID_ARRANGE
629 g_print("/n PerRox = %f PerCol = %f selcount = %d",PerRow,PerCol,selcount);
630 #endif
632 NoOfRowsLabel.set_label(_("Rows:"));
633 NoOfRowsBox.pack_start(NoOfRowsLabel, false, false, MARGIN);
635 NoOfRowsSpinner.set_digits(0);
636 NoOfRowsSpinner.set_increments(1, 5);
637 NoOfRowsSpinner.set_range(1.0, 100.0);
638 NoOfRowsSpinner.set_value(PerCol);
639 NoOfRowsSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_col_spinbutton_changed));
640 tips.set_tip(NoOfRowsSpinner, _("Number of rows"));
641 NoOfRowsBox.pack_start(NoOfRowsSpinner, false, false, MARGIN);
642 gtk_size_group_add_widget(_col1, (GtkWidget *) NoOfRowsBox.gobj());
644 RowHeightButton.set_label(_("Equal height"));
645 double AutoRow = prefs_get_double_attribute ("dialogs.gridtiler", "AutoRowSize", 15);
646 if (AutoRow>0)
647 AutoRowSize=true;
648 else
649 AutoRowSize=false;
650 RowHeightButton.set_active(AutoRowSize);
652 NoOfRowsBox.pack_start(RowHeightButton, false, false, MARGIN);
654 tips.set_tip(RowHeightButton, _("If not set, each row has the height of the tallest object in it"));
655 RowHeightButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::on_RowSize_checkbutton_changed));
657 {
658 /*#### Radio buttons to control vertical alignment ####*/
660 VertAlignLabel.set_label(_("Align:"));
661 VertAlignHBox.pack_start(VertAlignLabel, false, false, MARGIN);
663 VertTopRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
664 VertAlignGroup = VertTopRadioButton.get_group();
665 VertAlignVBox.pack_start(VertTopRadioButton, false, false, 0);
667 VertCentreRadioButton.set_group(VertAlignGroup);
668 VertCentreRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
669 VertAlignVBox.pack_start(VertCentreRadioButton, false, false, 0);
671 VertBotRadioButton.set_group(VertAlignGroup);
672 VertBotRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
673 VertAlignVBox.pack_start(VertBotRadioButton, false, false, 0);
675 VertAlign = prefs_get_double_attribute ("dialogs.gridtiler", "VertAlign", 1);
676 if (VertAlign == 0) {
677 VertTopRadioButton.set_active(TRUE);
678 }
679 else if (VertAlign == 1) {
680 VertCentreRadioButton.set_active(TRUE);
681 }
682 else if (VertAlign == 2){
683 VertBotRadioButton.set_active(TRUE);
684 }
685 VertAlignHBox.pack_start(VertAlignVBox, false, false, MARGIN);
686 NoOfRowsBox.pack_start(VertAlignHBox, false, false, MARGIN);
687 }
689 SpinsHBox.pack_start(NoOfRowsBox, false, false, MARGIN);
692 /*#### Label for X ####*/
693 padXByYLabel.set_label(" ");
694 XByYLabelVBox.pack_start(padXByYLabel, false, false, MARGIN);
695 XByYLabel.set_markup(" × ");
696 XByYLabelVBox.pack_start(XByYLabel, false, false, MARGIN);
697 SpinsHBox.pack_start(XByYLabelVBox, false, false, MARGIN);
698 gtk_size_group_add_widget(_col2, (GtkWidget *) XByYLabelVBox.gobj());
700 /*#### Number of columns ####*/
702 NoOfColsLabel.set_label(_("Columns:"));
703 NoOfColsBox.pack_start(NoOfColsLabel, false, false, MARGIN);
705 NoOfColsSpinner.set_digits(0);
706 NoOfColsSpinner.set_increments(1, 5);
707 NoOfColsSpinner.set_range(1.0, 100.0);
708 NoOfColsSpinner.set_value(PerRow);
709 NoOfColsSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_row_spinbutton_changed));
710 tips.set_tip(NoOfColsSpinner, _("Number of columns"));
711 NoOfColsBox.pack_start(NoOfColsSpinner, false, false, MARGIN);
712 gtk_size_group_add_widget(_col3, (GtkWidget *) NoOfColsBox.gobj());
714 ColumnWidthButton.set_label(_("Equal width"));
715 double AutoCol = prefs_get_double_attribute ("dialogs.gridtiler", "AutoColSize", 15);
716 if (AutoCol>0)
717 AutoColSize=true;
718 else
719 AutoColSize=false;
720 ColumnWidthButton.set_active(AutoColSize);
721 NoOfColsBox.pack_start(ColumnWidthButton, false, false, MARGIN);
723 tips.set_tip(ColumnWidthButton, _("If not set, each column has the width of the widest object in it"));
724 ColumnWidthButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::on_ColSize_checkbutton_changed));
727 {
728 /*#### Radio buttons to control horizontal alignment ####*/
730 HorizAlignLabel.set_label(_("Align:"));
731 HorizAlignVBox.pack_start(HorizAlignLabel, false, false, MARGIN);
733 HorizAlignHBox.pack_start(*(new Gtk::HBox()), true, true, 0); // centering strut
735 HorizLeftRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
736 HorizAlignGroup = HorizLeftRadioButton.get_group();
737 HorizAlignHBox.pack_start(HorizLeftRadioButton, false, false, 0);
739 HorizCentreRadioButton.set_group(HorizAlignGroup);
740 HorizCentreRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
741 HorizAlignHBox.pack_start(HorizCentreRadioButton, false, false, 0);
743 HorizRightRadioButton.set_group(HorizAlignGroup);
744 HorizRightRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
745 HorizAlignHBox.pack_start(HorizRightRadioButton, false, false, 0);
747 HorizAlignHBox.pack_start(*(new Gtk::HBox()), true, true, 0); // centering strut
749 HorizAlign = prefs_get_double_attribute ("dialogs.gridtiler", "HorizAlign", 1);
750 if (HorizAlign == 0) {
751 HorizLeftRadioButton.set_active(TRUE);
752 }
753 else if (HorizAlign == 1) {
754 HorizCentreRadioButton.set_active(TRUE);
755 }
756 else if (HorizAlign == 2) {
757 HorizRightRadioButton.set_active(TRUE);
758 }
759 HorizAlignVBox.pack_start(HorizAlignHBox, false, false, MARGIN);
760 NoOfColsBox.pack_start(HorizAlignVBox, false, false, MARGIN);
761 }
763 SpinsHBox.pack_start(NoOfColsBox, false, false, MARGIN);
765 TileBox.pack_start(SpinsHBox, false, false, MARGIN);
767 {
768 /*#### Radio buttons to control spacing manually or to fit selection bbox ####*/
769 SpaceByBBoxRadioButton.set_label(_("Fit into selection box"));
770 SpaceByBBoxRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::Spacing_button_changed));
771 SpacingGroup = SpaceByBBoxRadioButton.get_group();
773 SpacingVBox.pack_start(SpaceByBBoxRadioButton, false, false, MARGIN);
775 SpaceManualRadioButton.set_label(_("Set spacing:"));
776 SpaceManualRadioButton.set_group(SpacingGroup);
777 SpaceManualRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::Spacing_button_changed));
778 SpacingVBox.pack_start(SpaceManualRadioButton, false, false, MARGIN);
780 TileBox.pack_start(SpacingVBox, false, false, MARGIN);
781 }
783 {
784 /*#### Y Padding ####*/
786 GtkWidget *i = sp_icon_new (Inkscape::ICON_SIZE_MENU, "clonetiler_per_row");
787 YPadBox.pack_start (*(Glib::wrap(i)), false, false, MARGIN);
789 YPadSpinner.set_digits(1);
790 YPadSpinner.set_increments(0.2, 2);
791 YPadSpinner.set_range(-10000, 10000);
792 double YPad = prefs_get_double_attribute ("dialogs.gridtiler", "YPad", 15);
793 YPadSpinner.set_value(YPad);
794 YPadBox.pack_start(YPadSpinner, true, true, MARGIN);
795 tips.set_tip(YPadSpinner, _("Vertical spacing between rows (px units)"));
796 YPadSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_ypad_spinbutton_changed));
797 gtk_size_group_add_widget(_col1, (GtkWidget *) YPadBox.gobj());
799 SizesHBox.pack_start(YPadBox, false, false, MARGIN);
800 }
802 {
803 Gtk::HBox *spacer = new Gtk::HBox;
804 SizesHBox.pack_start(*spacer, false, false, 0);
805 gtk_size_group_add_widget(_col2, (GtkWidget *) spacer->gobj());
806 }
808 {
809 /*#### X padding ####*/
811 GtkWidget *i = sp_icon_new (Inkscape::ICON_SIZE_MENU, "clonetiler_per_column");
812 XPadBox.pack_start (*(Glib::wrap(i)), false, false, MARGIN);
814 XPadSpinner.set_digits(1);
815 XPadSpinner.set_increments(0.2, 2);
816 XPadSpinner.set_range(-10000, 10000);
817 double XPad = prefs_get_double_attribute ("dialogs.gridtiler", "XPad", 15);
818 XPadSpinner.set_value(XPad);
819 XPadBox.pack_start(XPadSpinner, true, true, MARGIN);
820 tips.set_tip(XPadSpinner, _("Horizontal spacing between columns (px units)"));
821 XPadSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_xpad_spinbutton_changed));
822 gtk_size_group_add_widget(_col3, (GtkWidget *) XPadBox.gobj());
824 SizesHBox.pack_start(XPadBox, false, false, MARGIN);
825 }
828 TileBox.pack_start(SizesHBox, false, false, MARGIN);
830 mainVBox->pack_start(TileBox);
832 double SpacingType = prefs_get_double_attribute ("dialogs.gridtiler", "SpacingType", 15);
833 if (SpacingType>0) {
834 ManualSpacing=true;
835 } else {
836 ManualSpacing=false;
837 }
838 SpaceManualRadioButton.set_active(ManualSpacing);
839 SpaceByBBoxRadioButton.set_active(!ManualSpacing);
840 SizesHBox.set_sensitive (ManualSpacing);
842 //## The OK button
843 TileOkButton = add_button(Gtk::Stock::APPLY, GTK_RESPONSE_APPLY);
844 tips.set_tip((*TileOkButton), _("Arrange selected objects"));
846 show_all_children();
847 }
854 } //namespace Dialog
855 } //namespace UI
856 } //namespace Inkscape
858 //#########################################################################
859 //## E N D O F F I L E
860 //#########################################################################
863 /*
864 Local Variables:
865 mode:c++
866 c-file-style:"stroustrup"
867 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
868 indent-tabs-mode:nil
869 fill-column:99
870 End:
871 */
872 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :