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;
127 // set padding to manual values
128 paddingx = XPadSpinner.get_value();
129 paddingy = YPadSpinner.get_value();
131 std::vector<double> row_heights;
132 std::vector<double> col_widths;
133 std::vector<double> row_ys;
134 std::vector<double> col_xs;
136 int NoOfCols = NoOfColsSpinner.get_value_as_int();
137 int NoOfRows = NoOfRowsSpinner.get_value_as_int();
139 width = 0;
140 for (a=0;a<NoOfCols; a++){
141 col_widths.push_back(width);
142 }
144 height = 0;
145 for (a=0;a<NoOfRows; a++){
146 row_heights.push_back(height);
147 }
148 grid_left = 99999;
149 grid_top = 99999;
151 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
152 sp_document_ensure_up_to_date(SP_DT_DOCUMENT(desktop));
154 Inkscape::Selection *selection = SP_DT_SELECTION (desktop);
155 const GSList *items = selection->itemList();
156 cnt=0;
157 for (; items != NULL; items = items->next) {
158 SPItem *item = SP_ITEM(items->data);
159 NR::Rect const b = item->invokeBbox(sp_item_i2doc_affine(item));
160 width = b.dimensions()[NR::X];
161 height = b.dimensions()[NR::Y];
162 cx = b.midpoint()[NR::X];
163 cy = b.midpoint()[NR::Y];
165 if (b.min()[NR::X] < grid_left) {
166 grid_left = b.min()[NR::X];
167 }
168 if (b.min()[NR::Y] < grid_top) {
169 grid_top = b.min()[NR::Y];
170 }
171 if (width > col_width) {
172 col_width = width;
173 }
174 if (height > row_height) {
175 row_height = height;
176 }
177 }
180 // require the sorting done before we can calculate row heights etc.
182 const GSList *items2 = selection->itemList();
183 GSList *rev = g_slist_copy((GSList *) items2);
184 GSList *sorted = NULL;
185 rev = g_slist_sort(rev, (GCompareFunc) sp_compare_y_position);
186 sorted = g_slist_sort(rev, (GCompareFunc) sp_compare_x_position);
189 // Calculate individual Row and Column sizes if necessary
192 cnt=0;
193 const GSList *sizes = sorted;
194 for (; sizes != NULL; sizes = sizes->next) {
195 SPItem *item = SP_ITEM(sizes->data);
196 NR::Rect const b = item->invokeBbox(sp_item_i2doc_affine(item));
197 width = b.dimensions()[NR::X];
198 height = b.dimensions()[NR::Y];
199 if (width > col_widths[(cnt % NoOfCols)]) {
200 col_widths[(cnt % NoOfCols)] = width;
201 }
202 if (height > row_heights[(cnt / NoOfCols)]) {
203 row_heights[(cnt / NoOfCols)] = height;
204 }
205 cnt++;
206 }
209 /// Make sure the top and left of the grid dont move by compensating for align values.
210 if (RowHeightButton.get_active()){
211 grid_top = grid_top - (((row_height - row_heights[0]) / 2)*(VertAlign));
212 }
213 if (ColumnWidthButton.get_active()){
214 grid_left = grid_left - (((col_width - col_widths[0]) /2)*(HorizAlign));
215 }
217 #ifdef DEBUG_GRID_ARRANGE
218 g_print("\n cx = %f cy= %f gridleft=%f",cx,cy,grid_left);
219 #endif
221 // Calculate total widths and heights, allowing for columns and rows non uniformly sized.
223 if (ColumnWidthButton.get_active()){
224 total_col_width = col_width * NoOfCols;
225 col_widths.clear();
226 for (a=0;a<NoOfCols; a++){
227 col_widths.push_back(col_width);
228 }
229 } else {
230 for (a = 0; a < (int)col_widths.size(); a++)
231 {
232 total_col_width += col_widths[a] ;
233 }
234 }
236 if (RowHeightButton.get_active()){
237 total_row_height = row_height * NoOfRows;
238 row_heights.clear();
239 for (a=0;a<NoOfRows; a++){
240 row_heights.push_back(row_height);
241 }
242 } else {
243 for (a = 0; a < (int)row_heights.size(); a++)
244 {
245 total_row_height += row_heights[a] ;
246 }
247 }
250 // Fit to bbox, calculate padding between rows accordingly.
251 if (!SpaceManualRadioButton.get_active()){
252 NR::Rect b = selection->bounds();
253 #ifdef DEBUG_GRID_ARRANGE
254 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));
255 #endif
256 paddingx = (b.extent(NR::X) - total_col_width) / (NoOfCols -1);
257 paddingy = (b.extent(NR::Y) - total_row_height) / (NoOfRows -1);
258 }
260 /*
261 Horizontal align - Left = 0
262 Centre = 1
263 Right = 2
265 Vertical align - Top = 0
266 Middle = 1
267 Bottom = 2
269 X position is calculated by taking the grids left co-ord, adding the distance to the column,
270 then adding 1/2 the spacing multiplied by the align variable above,
271 Y position likewise, takes the top of the grid, adds the y to the current row then adds the padding in to align it.
273 */
275 // Calculate row and column x and y coords required to allow for columns and rows which are non uniformly sized.
277 for (a=0;a<NoOfCols; a++){
278 if (a<1) col_xs.push_back(0);
279 else col_xs.push_back(col_widths[a-1]+paddingx+col_xs[a-1]);
280 }
283 for (a=0;a<NoOfRows; a++){
284 if (a<1) row_ys.push_back(0);
285 else row_ys.push_back(row_heights[a-1]+paddingy+row_ys[a-1]);
286 }
288 cnt=0;
289 for (row_cnt=0; ((sorted != NULL) && (row_cnt<NoOfRows)); row_cnt++) {
291 GSList *current_row = NULL;
292 for (col_cnt = 0; ((sorted != NULL) && (col_cnt<NoOfCols)); col_cnt++) {
293 current_row = g_slist_append (current_row, sorted->data);
294 sorted = sorted->next;
295 }
297 for (; current_row != NULL; current_row = current_row->next) {
298 SPItem *item=SP_ITEM(current_row->data);
299 Inkscape::XML::Node *repr = SP_OBJECT_REPR(item);
300 NR::Rect const b = item->invokeBbox(sp_item_i2doc_affine(item));
301 width = b.dimensions()[NR::X];
302 height = b.dimensions()[NR::Y];
303 row = cnt / NoOfCols;
304 col = cnt % NoOfCols;
306 // original before I started fecking about with it.
307 // new_x = grid_left + (((col_width - width)/2)*HorizAlign) + (( col_width + paddingx ) * (cnt % NoOfCols));
308 // new_y = grid_top + (((row_height - height)/2)*VertAlign) +(( row_height + paddingy ) * (cnt / NoOfCols));
310 new_x = grid_left + (((col_widths[col] - width)/2)*HorizAlign) + col_xs[col];
311 new_y = grid_top + (((row_heights[row] - height)/2)*VertAlign) + row_ys[row];
313 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???
314 NR::Matrix const &affine = NR::Matrix(NR::translate(move));
315 sp_item_set_i2d_affine(item, sp_item_i2d_affine(item) * affine);
316 sp_item_write_transform(item, repr, item->transform, NULL);
317 SP_OBJECT (current_row->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
318 cnt +=1;
319 }
320 g_slist_free (current_row);
321 }
323 NRRect b;
324 selection->bounds(&b);
326 sp_document_done (SP_DT_DOCUMENT (desktop));
328 }
331 //#########################################################################
332 //## E V E N T S
333 //#########################################################################
336 void TileDialog::_apply()
337 {
338 Grid_Arrange();
339 }
342 /**
343 * changed value in # of columns spinbox.
344 */
345 void TileDialog::on_row_spinbutton_changed()
346 {
347 // quit if run by the attr_changed listener
348 if (updating) {
349 return;
350 }
352 // in turn, prevent listener from responding
353 updating = true;
354 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
356 Inkscape::Selection *selection = SP_DT_SELECTION (desktop);
358 GSList const *items = selection->itemList();
359 int selcount = g_slist_length((GSList *)items);
361 double PerCol = ceil(selcount / NoOfColsSpinner.get_value());
362 NoOfRowsSpinner.set_value(PerCol);
363 prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", NoOfColsSpinner.get_value());
364 updating=false;
365 }
367 /**
368 * changed value in # of rows spinbox.
369 */
370 void TileDialog::on_col_spinbutton_changed()
371 {
372 // quit if run by the attr_changed listener
373 if (updating) {
374 return;
375 }
377 // in turn, prevent listener from responding
378 updating = true;
379 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
380 Inkscape::Selection *selection = SP_DT_SELECTION (desktop);
382 GSList const *items = selection->itemList();
383 int selcount = g_slist_length((GSList *)items);
385 double PerRow = ceil(selcount / NoOfRowsSpinner.get_value());
386 NoOfColsSpinner.set_value(PerRow);
387 prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", PerRow);
389 updating=false;
390 }
392 /**
393 * changed value in x padding spinbox.
394 */
395 void TileDialog::on_xpad_spinbutton_changed()
396 {
398 prefs_set_double_attribute ("dialogs.gridtiler", "XPad", XPadSpinner.get_value());
400 }
402 /**
403 * changed value in y padding spinbox.
404 */
405 void TileDialog::on_ypad_spinbutton_changed()
406 {
408 prefs_set_double_attribute ("dialogs.gridtiler", "YPad", YPadSpinner.get_value());
410 }
413 /**
414 * checked/unchecked autosize Rows button.
415 */
416 void TileDialog::on_RowSize_checkbutton_changed()
417 {
419 if (RowHeightButton.get_active()) {
420 prefs_set_double_attribute ("dialogs.gridtiler", "AutoRowSize", 20);
421 } else {
422 prefs_set_double_attribute ("dialogs.gridtiler", "AutoRowSize", -20);
423 }
424 RowHeightBox.set_sensitive ( !RowHeightButton.get_active());
425 }
427 /**
428 * checked/unchecked autosize Rows button.
429 */
430 void TileDialog::on_ColSize_checkbutton_changed()
431 {
433 if (ColumnWidthButton.get_active()) {
434 prefs_set_double_attribute ("dialogs.gridtiler", "AutoColSize", 20);
435 } else {
436 prefs_set_double_attribute ("dialogs.gridtiler", "AutoColSize", -20);
437 }
438 ColumnWidthBox.set_sensitive ( !ColumnWidthButton.get_active());
440 }
442 /**
443 * changed value in columns spinbox.
444 */
445 void TileDialog::on_rowSize_spinbutton_changed()
446 {
447 // quit if run by the attr_changed listener
448 if (updating) {
449 return;
450 }
452 // in turn, prevent listener from responding
453 updating = true;
454 prefs_set_double_attribute ("dialogs.gridtiler", "RowHeight", RowHeightSpinner.get_value());
455 updating=false;
457 }
459 /**
460 * changed value in rows spinbox.
461 */
462 void TileDialog::on_colSize_spinbutton_changed()
463 {
464 // quit if run by the attr_changed listener
465 if (updating) {
466 return;
467 }
469 // in turn, prevent listener from responding
470 updating = true;
471 prefs_set_double_attribute ("dialogs.gridtiler", "ColWidth", ColumnWidthSpinner.get_value());
472 updating=false;
474 }
476 /**
477 * changed Radio button in Spacing group.
478 */
479 void TileDialog::Spacing_button_changed()
480 {
481 if (SpaceManualRadioButton.get_active()) {
482 prefs_set_double_attribute ("dialogs.gridtiler", "SpacingType", 20);
483 } else {
484 prefs_set_double_attribute ("dialogs.gridtiler", "SpacingType", -20);
485 }
487 SizesHBox.set_sensitive ( SpaceManualRadioButton.get_active());
488 }
490 /**
491 * changed Radio button in Vertical Align group.
492 */
493 void TileDialog::VertAlign_changed()
494 {
495 if (VertTopRadioButton.get_active()) {
496 VertAlign = 0;
497 prefs_set_double_attribute ("dialogs.gridtiler", "VertAlign", 0);
498 } else if (VertCentreRadioButton.get_active()){
499 VertAlign = 1;
500 prefs_set_double_attribute ("dialogs.gridtiler", "VertAlign", 1);
501 } else if (VertBotRadioButton.get_active()){
502 VertAlign = 2;
503 prefs_set_double_attribute ("dialogs.gridtiler", "VertAlign", 2);
504 }
506 }
508 /**
509 * changed Radio button in Vertical Align group.
510 */
511 void TileDialog::HorizAlign_changed()
512 {
513 if (HorizLeftRadioButton.get_active()) {
514 HorizAlign = 0;
515 prefs_set_double_attribute ("dialogs.gridtiler", "HorizAlign", 0);
516 } else if (HorizCentreRadioButton.get_active()){
517 HorizAlign = 1;
518 prefs_set_double_attribute ("dialogs.gridtiler", "HorizAlign", 1);
519 } else if (HorizRightRadioButton.get_active()){
520 HorizAlign = 2;
521 prefs_set_double_attribute ("dialogs.gridtiler", "HorizAlign", 2);
522 }
524 }
526 /**
527 * Desktop selection changed
528 */
529 void TileDialog::updateSelection()
530 {
531 double col_width, row_height;
532 // quit if run by the attr_changed listener
533 if (updating) {
534 return;
535 }
537 col_width=0;
538 row_height=0;
539 // in turn, prevent listener from responding
540 updating = true;
541 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
542 Inkscape::Selection *selection = SP_DT_SELECTION (desktop);
543 const GSList *items = selection->itemList();
544 int selcount = g_slist_length((GSList *)items);
546 if (NoOfColsSpinner.get_value()>1){
547 // Update the number of rows assuming number of columns wanted remains same.
548 double NoOfRows = ceil(selcount / NoOfColsSpinner.get_value());
549 NoOfRowsSpinner.set_value(NoOfRows);
551 // if the selection has less than the number set for one row, reduce it appropriately
552 if (selcount<NoOfColsSpinner.get_value()) {
553 double NoOfCols = ceil(selcount / NoOfRowsSpinner.get_value());
554 NoOfColsSpinner.set_value(NoOfCols);
555 prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", NoOfCols);
556 }
557 } else {
558 NoOfColsSpinner.set_value(selcount);
559 prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", selcount);
561 }
563 updating=false;
565 }
571 /*##########################
572 ## Experimental
573 ##########################*/
575 static void updateSelectionCallback(Inkscape::Application *inkscape, Inkscape::Selection *selection, TileDialog *dlg)
576 {
577 TileDialog *tledlg = (TileDialog *) dlg;
578 tledlg->updateSelection();
579 }
582 //#########################################################################
583 //## C O N S T R U C T O R / D E S T R U C T O R
584 //#########################################################################
585 /**
586 * Constructor
587 */
588 TileDialog::TileDialog()
589 : Dialog ("dialogs.gridtiler", SP_VERB_SELECTION_GRIDTILE)
590 {
591 // bool used by spin button callbacks to stop loops where they change each other.
592 updating = false;
594 // could not do this in gtkmm - there's no Gtk::SizeGroup public constructor (!)
595 GtkSizeGroup *_col1 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
596 GtkSizeGroup *_col2 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
597 GtkSizeGroup *_col3 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
599 {
600 // Selection Change signal
601 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (updateSelectionCallback), this);
602 }
604 Gtk::VBox *mainVBox = get_vbox();
606 #define MARGIN 2
608 //##Set up the panel
610 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
612 Inkscape::Selection *selection = SP_DT_SELECTION (desktop);
613 int selcount = 1;
614 if (!selection->isEmpty()) {
615 GSList const *items = selection->itemList();
616 selcount = g_slist_length((GSList *)items);
617 }
620 /*#### Number of Rows ####*/
622 double PerRow = selcount;
623 double PerCol = 1;
625 #ifdef DEBUG_GRID_ARRANGE
626 g_print("/n PerRox = %f PerCol = %f selcount = %d",PerRow,PerCol,selcount);
627 #endif
629 NoOfRowsLabel.set_label(_("Rows:"));
630 NoOfRowsBox.pack_start(NoOfRowsLabel, false, false, MARGIN);
632 NoOfRowsSpinner.set_digits(0);
633 NoOfRowsSpinner.set_increments(1, 5);
634 NoOfRowsSpinner.set_range(1.0, 100.0);
635 NoOfRowsSpinner.set_value(PerCol);
636 NoOfRowsSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_col_spinbutton_changed));
637 tips.set_tip(NoOfRowsSpinner, _("Number of rows"));
638 NoOfRowsBox.pack_start(NoOfRowsSpinner, false, false, MARGIN);
639 gtk_size_group_add_widget(_col1, (GtkWidget *) NoOfRowsBox.gobj());
641 RowHeightButton.set_label(_("Equal height"));
642 double AutoRow = prefs_get_double_attribute ("dialogs.gridtiler", "AutoRowSize", 15);
643 if (AutoRow>0)
644 AutoRowSize=true;
645 else
646 AutoRowSize=false;
647 RowHeightButton.set_active(AutoRowSize);
649 NoOfRowsBox.pack_start(RowHeightButton, false, false, MARGIN);
651 tips.set_tip(RowHeightButton, _("If not set, each row has the height of the tallest object in it"));
652 RowHeightButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::on_RowSize_checkbutton_changed));
654 {
655 /*#### Radio buttons to control vertical alignment ####*/
657 VertAlignLabel.set_label(_("Align:"));
658 VertAlignHBox.pack_start(VertAlignLabel, false, false, MARGIN);
660 VertTopRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
661 VertAlignGroup = VertTopRadioButton.get_group();
662 VertAlignVBox.pack_start(VertTopRadioButton, false, false, 0);
664 VertCentreRadioButton.set_group(VertAlignGroup);
665 VertCentreRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
666 VertAlignVBox.pack_start(VertCentreRadioButton, false, false, 0);
668 VertBotRadioButton.set_group(VertAlignGroup);
669 VertBotRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
670 VertAlignVBox.pack_start(VertBotRadioButton, false, false, 0);
672 VertAlign = prefs_get_double_attribute ("dialogs.gridtiler", "VertAlign", 1);
673 if (VertAlign == 0) {
674 VertTopRadioButton.set_active(TRUE);
675 }
676 else if (VertAlign == 1) {
677 VertCentreRadioButton.set_active(TRUE);
678 }
679 else if (VertAlign == 2){
680 VertBotRadioButton.set_active(TRUE);
681 }
682 VertAlignHBox.pack_start(VertAlignVBox, false, false, MARGIN);
683 NoOfRowsBox.pack_start(VertAlignHBox, false, false, MARGIN);
684 }
686 SpinsHBox.pack_start(NoOfRowsBox, false, false, MARGIN);
689 /*#### Label for X ####*/
690 padXByYLabel.set_label(" ");
691 XByYLabelVBox.pack_start(padXByYLabel, false, false, MARGIN);
692 XByYLabel.set_markup(" × ");
693 XByYLabelVBox.pack_start(XByYLabel, false, false, MARGIN);
694 SpinsHBox.pack_start(XByYLabelVBox, false, false, MARGIN);
695 gtk_size_group_add_widget(_col2, (GtkWidget *) XByYLabelVBox.gobj());
697 /*#### Number of columns ####*/
699 NoOfColsLabel.set_label(_("Columns:"));
700 NoOfColsBox.pack_start(NoOfColsLabel, false, false, MARGIN);
702 NoOfColsSpinner.set_digits(0);
703 NoOfColsSpinner.set_increments(1, 5);
704 NoOfColsSpinner.set_range(1.0, 100.0);
705 NoOfColsSpinner.set_value(PerRow);
706 NoOfColsSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_row_spinbutton_changed));
707 tips.set_tip(NoOfColsSpinner, _("Number of columns"));
708 NoOfColsBox.pack_start(NoOfColsSpinner, false, false, MARGIN);
709 gtk_size_group_add_widget(_col3, (GtkWidget *) NoOfColsBox.gobj());
711 ColumnWidthButton.set_label(_("Equal width"));
712 double AutoCol = prefs_get_double_attribute ("dialogs.gridtiler", "AutoColSize", 15);
713 if (AutoCol>0)
714 AutoColSize=true;
715 else
716 AutoColSize=false;
717 ColumnWidthButton.set_active(AutoColSize);
718 NoOfColsBox.pack_start(ColumnWidthButton, false, false, MARGIN);
720 tips.set_tip(ColumnWidthButton, _("If not set, each column has the width of the widest object in it"));
721 ColumnWidthButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::on_ColSize_checkbutton_changed));
724 {
725 /*#### Radio buttons to control horizontal alignment ####*/
727 HorizAlignLabel.set_label(_("Align:"));
728 HorizAlignVBox.pack_start(HorizAlignLabel, false, false, MARGIN);
730 HorizAlignHBox.pack_start(*(new Gtk::HBox()), true, true, 0); // centering strut
732 HorizLeftRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
733 HorizAlignGroup = HorizLeftRadioButton.get_group();
734 HorizAlignHBox.pack_start(HorizLeftRadioButton, false, false, 0);
736 HorizCentreRadioButton.set_group(HorizAlignGroup);
737 HorizCentreRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
738 HorizAlignHBox.pack_start(HorizCentreRadioButton, false, false, 0);
740 HorizRightRadioButton.set_group(HorizAlignGroup);
741 HorizRightRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
742 HorizAlignHBox.pack_start(HorizRightRadioButton, false, false, 0);
744 HorizAlignHBox.pack_start(*(new Gtk::HBox()), true, true, 0); // centering strut
746 HorizAlign = prefs_get_double_attribute ("dialogs.gridtiler", "HorizAlign", 1);
747 if (HorizAlign == 0) {
748 HorizLeftRadioButton.set_active(TRUE);
749 }
750 else if (HorizAlign == 1) {
751 HorizCentreRadioButton.set_active(TRUE);
752 }
753 else if (HorizAlign == 2) {
754 HorizRightRadioButton.set_active(TRUE);
755 }
756 HorizAlignVBox.pack_start(HorizAlignHBox, false, false, MARGIN);
757 NoOfColsBox.pack_start(HorizAlignVBox, false, false, MARGIN);
758 }
760 SpinsHBox.pack_start(NoOfColsBox, false, false, MARGIN);
762 TileBox.pack_start(SpinsHBox, false, false, MARGIN);
764 {
765 /*#### Radio buttons to control spacing manually or to fit selection bbox ####*/
766 SpaceByBBoxRadioButton.set_label(_("Fit into selection box"));
767 SpaceByBBoxRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::Spacing_button_changed));
768 SpacingGroup = SpaceByBBoxRadioButton.get_group();
770 SpacingVBox.pack_start(SpaceByBBoxRadioButton, false, false, MARGIN);
772 SpaceManualRadioButton.set_label(_("Set spacing:"));
773 SpaceManualRadioButton.set_group(SpacingGroup);
774 SpaceManualRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::Spacing_button_changed));
775 SpacingVBox.pack_start(SpaceManualRadioButton, false, false, MARGIN);
777 TileBox.pack_start(SpacingVBox, false, false, MARGIN);
778 }
780 {
781 /*#### Y Padding ####*/
783 GtkWidget *i = sp_icon_new (Inkscape::ICON_SIZE_MENU, "clonetiler_per_row");
784 YPadBox.pack_start (*(Glib::wrap(i)), false, false, MARGIN);
786 YPadSpinner.set_digits(1);
787 YPadSpinner.set_increments(0.2, 2);
788 YPadSpinner.set_range(-10000, 10000);
789 double YPad = prefs_get_double_attribute ("dialogs.gridtiler", "YPad", 15);
790 YPadSpinner.set_value(YPad);
791 YPadBox.pack_start(YPadSpinner, true, true, MARGIN);
792 tips.set_tip(YPadSpinner, _("Vertical spacing between rows (px units)"));
793 YPadSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_ypad_spinbutton_changed));
794 gtk_size_group_add_widget(_col1, (GtkWidget *) YPadBox.gobj());
796 SizesHBox.pack_start(YPadBox, false, false, MARGIN);
797 }
799 {
800 Gtk::HBox *spacer = new Gtk::HBox;
801 SizesHBox.pack_start(*spacer, false, false, 0);
802 gtk_size_group_add_widget(_col2, (GtkWidget *) spacer->gobj());
803 }
805 {
806 /*#### X padding ####*/
808 GtkWidget *i = sp_icon_new (Inkscape::ICON_SIZE_MENU, "clonetiler_per_column");
809 XPadBox.pack_start (*(Glib::wrap(i)), false, false, MARGIN);
811 XPadSpinner.set_digits(1);
812 XPadSpinner.set_increments(0.2, 2);
813 XPadSpinner.set_range(-10000, 10000);
814 double XPad = prefs_get_double_attribute ("dialogs.gridtiler", "XPad", 15);
815 XPadSpinner.set_value(XPad);
816 XPadBox.pack_start(XPadSpinner, true, true, MARGIN);
817 tips.set_tip(XPadSpinner, _("Horizontal spacing between columns (px units)"));
818 XPadSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_xpad_spinbutton_changed));
819 gtk_size_group_add_widget(_col3, (GtkWidget *) XPadBox.gobj());
821 SizesHBox.pack_start(XPadBox, false, false, MARGIN);
822 }
825 TileBox.pack_start(SizesHBox, false, false, MARGIN);
827 mainVBox->pack_start(TileBox);
829 double SpacingType = prefs_get_double_attribute ("dialogs.gridtiler", "SpacingType", 15);
830 if (SpacingType>0) {
831 ManualSpacing=true;
832 } else {
833 ManualSpacing=false;
834 }
835 SpaceManualRadioButton.set_active(ManualSpacing);
836 SpaceByBBoxRadioButton.set_active(!ManualSpacing);
837 SizesHBox.set_sensitive (ManualSpacing);
839 //## The OK button
840 TileOkButton = add_button(Gtk::Stock::APPLY, GTK_RESPONSE_APPLY);
841 tips.set_tip((*TileOkButton), _("Arrange selected objects"));
843 show_all_children();
844 }
851 } //namespace Dialog
852 } //namespace UI
853 } //namespace Inkscape
855 //#########################################################################
856 //## E N D O F F I L E
857 //#########################################################################
860 /*
861 Local Variables:
862 mode:c++
863 c-file-style:"stroustrup"
864 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
865 indent-tabs-mode:nil
866 fill-column:99
867 End:
868 */
869 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :