5741fee4d469016fd570f5136dd44c1633b43119
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
20 #include <gtk/gtkdialog.h> //for GTK_RESPONSE* types
21 #include <gtk/gtksizegroup.h>
22 #include <glibmm/i18n.h>
23 #include <gtkmm/stock.h>
25 #include "verbs.h"
26 #include "preferences.h"
27 #include "inkscape.h"
28 #include "desktop-handles.h"
29 #include "selection.h"
30 #include "document.h"
31 #include "sp-item.h"
32 #include "widgets/icon.h"
33 #include "tile.h"
35 /*
36 * Sort items by their x co-ordinates, taking account of y (keeps rows intact)
37 *
38 * <0 *elem1 goes before *elem2
39 * 0 *elem1 == *elem2
40 * >0 *elem1 goes after *elem2
41 */
42 int
43 sp_compare_x_position(SPItem *first, SPItem *second)
44 {
45 using Geom::X;
46 using Geom::Y;
48 Geom::OptRect a = first->getBounds(sp_item_i2doc_affine(first));
49 Geom::OptRect b = second->getBounds(sp_item_i2doc_affine(second));
51 if ( !a || !b ) {
52 // FIXME?
53 return 0;
54 }
56 double const a_height = a->dimensions()[Y];
57 double const b_height = b->dimensions()[Y];
59 bool a_in_b_vert = false;
60 if ((a->min()[Y] < b->min()[Y] + 0.1) && (a->min()[Y] > b->min()[Y] - b_height)) {
61 a_in_b_vert = true;
62 } else if ((b->min()[Y] < a->min()[Y] + 0.1) && (b->min()[Y] > a->min()[Y] - a_height)) {
63 a_in_b_vert = true;
64 } else if (b->min()[Y] == a->min()[Y]) {
65 a_in_b_vert = true;
66 } else {
67 a_in_b_vert = false;
68 }
70 if (!a_in_b_vert) {
71 return -1;
72 }
73 if (a_in_b_vert && a->min()[X] > b->min()[X]) {
74 return 1;
75 }
76 if (a_in_b_vert && a->min()[X] < b->min()[X]) {
77 return -1;
78 }
79 return 0;
80 }
82 /*
83 * Sort items by their y co-ordinates.
84 */
85 int
86 sp_compare_y_position(SPItem *first, SPItem *second)
87 {
88 Geom::OptRect a = first->getBounds(sp_item_i2doc_affine(first));
89 Geom::OptRect b = second->getBounds(sp_item_i2doc_affine(second));
91 if ( !a || !b ) {
92 // FIXME?
93 return 0;
94 }
96 if (a->min()[Geom::Y] > b->min()[Geom::Y]) {
97 return 1;
98 }
99 if (a->min()[Geom::Y] < b->min()[Geom::Y]) {
100 return -1;
101 }
103 return 0;
104 }
106 namespace Inkscape {
107 namespace UI {
108 namespace Dialog {
111 //#########################################################################
112 //## E V E N T S
113 //#########################################################################
115 /*
116 *
117 * This arranges the selection in a grid pattern.
118 *
119 */
121 void TileDialog::Grid_Arrange ()
122 {
124 int cnt,row_cnt,col_cnt,a,row,col;
125 double grid_left,grid_top,col_width,row_height,paddingx,paddingy,width, height, new_x, new_y,cx,cy;
126 double total_col_width,total_row_height;
127 col_width = 0;
128 row_height = 0;
129 total_col_width=0;
130 total_row_height=0;
132 // check for correct numbers in the row- and col-spinners
133 on_col_spinbutton_changed();
134 on_row_spinbutton_changed();
136 // set padding to manual values
137 paddingx = XPadSpinner.get_value();
138 paddingy = YPadSpinner.get_value();
140 std::vector<double> row_heights;
141 std::vector<double> col_widths;
142 std::vector<double> row_ys;
143 std::vector<double> col_xs;
145 int NoOfCols = NoOfColsSpinner.get_value_as_int();
146 int NoOfRows = NoOfRowsSpinner.get_value_as_int();
148 width = 0;
149 for (a=0;a<NoOfCols; a++){
150 col_widths.push_back(width);
151 }
153 height = 0;
154 for (a=0;a<NoOfRows; a++){
155 row_heights.push_back(height);
156 }
157 grid_left = 99999;
158 grid_top = 99999;
160 SPDesktop *desktop = getDesktop();
161 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
163 Inkscape::Selection *selection = sp_desktop_selection (desktop);
164 const GSList *items = selection->itemList();
165 cnt=0;
166 for (; items != NULL; items = items->next) {
167 SPItem *item = SP_ITEM(items->data);
168 Geom::OptRect b = item->getBounds(sp_item_i2doc_affine(item));
169 if (!b) {
170 continue;
171 }
173 width = b->dimensions()[Geom::X];
174 height = b->dimensions()[Geom::Y];
176 cx = b->midpoint()[Geom::X];
177 cy = b->midpoint()[Geom::Y];
179 if (b->min()[Geom::X] < grid_left) {
180 grid_left = b->min()[Geom::X];
181 }
182 if (b->min()[Geom::Y] < grid_top) {
183 grid_top = b->min()[Geom::Y];
184 }
185 if (width > col_width) {
186 col_width = width;
187 }
188 if (height > row_height) {
189 row_height = height;
190 }
191 }
194 // require the sorting done before we can calculate row heights etc.
196 const GSList *items2 = selection->itemList();
197 GSList *rev = g_slist_copy((GSList *) items2);
198 GSList *sorted = NULL;
199 rev = g_slist_sort(rev, (GCompareFunc) sp_compare_y_position);
200 sorted = g_slist_sort(rev, (GCompareFunc) sp_compare_x_position);
203 // Calculate individual Row and Column sizes if necessary
206 cnt=0;
207 const GSList *sizes = sorted;
208 for (; sizes != NULL; sizes = sizes->next) {
209 SPItem *item = SP_ITEM(sizes->data);
210 Geom::OptRect b = item->getBounds(sp_item_i2doc_affine(item));
211 if (b) {
212 width = b->dimensions()[Geom::X];
213 height = b->dimensions()[Geom::Y];
214 if (width > col_widths[(cnt % NoOfCols)]) {
215 col_widths[(cnt % NoOfCols)] = width;
216 }
217 if (height > row_heights[(cnt / NoOfCols)]) {
218 row_heights[(cnt / NoOfCols)] = height;
219 }
220 }
222 cnt++;
223 }
226 /// Make sure the top and left of the grid dont move by compensating for align values.
227 if (RowHeightButton.get_active()){
228 grid_top = grid_top - (((row_height - row_heights[0]) / 2)*(VertAlign));
229 }
230 if (ColumnWidthButton.get_active()){
231 grid_left = grid_left - (((col_width - col_widths[0]) /2)*(HorizAlign));
232 }
234 #ifdef DEBUG_GRID_ARRANGE
235 g_print("\n cx = %f cy= %f gridleft=%f",cx,cy,grid_left);
236 #endif
238 // Calculate total widths and heights, allowing for columns and rows non uniformly sized.
240 if (ColumnWidthButton.get_active()){
241 total_col_width = col_width * NoOfCols;
242 col_widths.clear();
243 for (a=0;a<NoOfCols; a++){
244 col_widths.push_back(col_width);
245 }
246 } else {
247 for (a = 0; a < (int)col_widths.size(); a++)
248 {
249 total_col_width += col_widths[a] ;
250 }
251 }
253 if (RowHeightButton.get_active()){
254 total_row_height = row_height * NoOfRows;
255 row_heights.clear();
256 for (a=0;a<NoOfRows; a++){
257 row_heights.push_back(row_height);
258 }
259 } else {
260 for (a = 0; a < (int)row_heights.size(); a++)
261 {
262 total_row_height += row_heights[a] ;
263 }
264 }
267 Geom::OptRect sel_bbox = selection->bounds();
268 // Fit to bbox, calculate padding between rows accordingly.
269 if ( sel_bbox && !SpaceManualRadioButton.get_active() ){
270 #ifdef DEBUG_GRID_ARRANGE
271 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));
272 #endif
273 paddingx = (sel_bbox->width() - total_col_width) / (NoOfCols -1);
274 paddingy = (sel_bbox->height() - total_row_height) / (NoOfRows -1);
275 }
277 /*
278 Horizontal align - Left = 0
279 Centre = 1
280 Right = 2
282 Vertical align - Top = 0
283 Middle = 1
284 Bottom = 2
286 X position is calculated by taking the grids left co-ord, adding the distance to the column,
287 then adding 1/2 the spacing multiplied by the align variable above,
288 Y position likewise, takes the top of the grid, adds the y to the current row then adds the padding in to align it.
290 */
292 // Calculate row and column x and y coords required to allow for columns and rows which are non uniformly sized.
294 for (a=0;a<NoOfCols; a++){
295 if (a<1) col_xs.push_back(0);
296 else col_xs.push_back(col_widths[a-1]+paddingx+col_xs[a-1]);
297 }
300 for (a=0;a<NoOfRows; a++){
301 if (a<1) row_ys.push_back(0);
302 else row_ys.push_back(row_heights[a-1]+paddingy+row_ys[a-1]);
303 }
305 cnt=0;
306 for (row_cnt=0; ((sorted != NULL) && (row_cnt<NoOfRows)); row_cnt++) {
308 GSList *current_row = NULL;
309 for (col_cnt = 0; ((sorted != NULL) && (col_cnt<NoOfCols)); col_cnt++) {
310 current_row = g_slist_append (current_row, sorted->data);
311 sorted = sorted->next;
312 }
314 for (; current_row != NULL; current_row = current_row->next) {
315 SPItem *item=SP_ITEM(current_row->data);
316 Inkscape::XML::Node *repr = SP_OBJECT_REPR(item);
317 Geom::OptRect b = item->getBounds(sp_item_i2doc_affine(item));
318 Geom::Point min;
319 if (b) {
320 width = b->dimensions()[Geom::X];
321 height = b->dimensions()[Geom::Y];
322 min = b->min();
323 } else {
324 width = height = 0;
325 min = Geom::Point(0, 0);
326 }
328 row = cnt / NoOfCols;
329 col = cnt % NoOfCols;
331 new_x = grid_left + (((col_widths[col] - width)/2)*HorizAlign) + col_xs[col];
332 new_y = grid_top + (((row_heights[row] - height)/2)*VertAlign) + row_ys[row];
334 // signs are inverted between x and y due to y inversion
335 Geom::Point move = Geom::Point(new_x - min[Geom::X], min[Geom::Y] - new_y);
336 Geom::Matrix const affine = Geom::Matrix(Geom::Translate(move));
337 sp_item_set_i2d_affine(item, sp_item_i2d_affine(item) * affine);
338 sp_item_write_transform(item, repr, item->transform, NULL);
339 SP_OBJECT (current_row->data)->updateRepr();
340 cnt +=1;
341 }
342 g_slist_free (current_row);
343 }
345 sp_document_done (sp_desktop_document (desktop), SP_VERB_SELECTION_GRIDTILE,
346 _("Arrange in a grid"));
348 }
351 //#########################################################################
352 //## E V E N T S
353 //#########################################################################
356 void TileDialog::_apply()
357 {
358 Grid_Arrange();
359 }
362 /**
363 * changed value in # of columns spinbox.
364 */
365 void TileDialog::on_row_spinbutton_changed()
366 {
367 // quit if run by the attr_changed listener
368 if (updating) {
369 return;
370 }
372 // in turn, prevent listener from responding
373 updating = true;
374 SPDesktop *desktop = getDesktop();
376 Inkscape::Selection *selection = sp_desktop_selection (desktop);
378 GSList const *items = selection->itemList();
379 int selcount = g_slist_length((GSList *)items);
381 double PerCol = ceil(selcount / NoOfColsSpinner.get_value());
382 NoOfRowsSpinner.set_value(PerCol);
383 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
384 prefs->setDouble("/dialogs/gridtiler/NoOfCols", NoOfColsSpinner.get_value());
385 updating=false;
386 }
388 /**
389 * changed value in # of rows spinbox.
390 */
391 void TileDialog::on_col_spinbutton_changed()
392 {
393 // quit if run by the attr_changed listener
394 if (updating) {
395 return;
396 }
398 // in turn, prevent listener from responding
399 updating = true;
400 SPDesktop *desktop = getDesktop();
401 Inkscape::Selection *selection = sp_desktop_selection (desktop);
403 GSList const *items = selection->itemList();
404 int selcount = g_slist_length((GSList *)items);
406 double PerRow = ceil(selcount / NoOfRowsSpinner.get_value());
407 NoOfColsSpinner.set_value(PerRow);
408 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
409 prefs->setDouble("/dialogs/gridtiler/NoOfCols", PerRow);
411 updating=false;
412 }
414 /**
415 * changed value in x padding spinbox.
416 */
417 void TileDialog::on_xpad_spinbutton_changed()
418 {
419 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
420 prefs->setDouble("/dialogs/gridtiler/XPad", XPadSpinner.get_value());
422 }
424 /**
425 * changed value in y padding spinbox.
426 */
427 void TileDialog::on_ypad_spinbutton_changed()
428 {
429 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
430 prefs->setDouble("/dialogs/gridtiler/YPad", YPadSpinner.get_value());
431 }
434 /**
435 * checked/unchecked autosize Rows button.
436 */
437 void TileDialog::on_RowSize_checkbutton_changed()
438 {
439 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
440 if (RowHeightButton.get_active()) {
441 prefs->setDouble("/dialogs/gridtiler/AutoRowSize", 20);
442 } else {
443 prefs->setDouble("/dialogs/gridtiler/AutoRowSize", -20);
444 }
445 RowHeightBox.set_sensitive ( !RowHeightButton.get_active());
446 }
448 /**
449 * checked/unchecked autosize Rows button.
450 */
451 void TileDialog::on_ColSize_checkbutton_changed()
452 {
453 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
454 if (ColumnWidthButton.get_active()) {
455 prefs->setDouble("/dialogs/gridtiler/AutoColSize", 20);
456 } else {
457 prefs->setDouble("/dialogs/gridtiler/AutoColSize", -20);
458 }
459 ColumnWidthBox.set_sensitive ( !ColumnWidthButton.get_active());
460 }
462 /**
463 * changed value in columns spinbox.
464 */
465 void TileDialog::on_rowSize_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 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
475 prefs->setDouble("/dialogs/gridtiler/RowHeight", RowHeightSpinner.get_value());
476 updating=false;
478 }
480 /**
481 * changed value in rows spinbox.
482 */
483 void TileDialog::on_colSize_spinbutton_changed()
484 {
485 // quit if run by the attr_changed listener
486 if (updating) {
487 return;
488 }
490 // in turn, prevent listener from responding
491 updating = true;
492 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
493 prefs->setDouble("/dialogs/gridtiler/ColWidth", ColumnWidthSpinner.get_value());
494 updating=false;
496 }
498 /**
499 * changed Radio button in Spacing group.
500 */
501 void TileDialog::Spacing_button_changed()
502 {
503 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
504 if (SpaceManualRadioButton.get_active()) {
505 prefs->setDouble("/dialogs/gridtiler/SpacingType", 20);
506 } else {
507 prefs->setDouble("/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 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
519 if (VertTopRadioButton.get_active()) {
520 VertAlign = 0;
521 prefs->setInt("/dialogs/gridtiler/VertAlign", 0);
522 } else if (VertCentreRadioButton.get_active()){
523 VertAlign = 1;
524 prefs->setInt("/dialogs/gridtiler/VertAlign", 1);
525 } else if (VertBotRadioButton.get_active()){
526 VertAlign = 2;
527 prefs->setInt("/dialogs/gridtiler/VertAlign", 2);
528 }
529 }
531 /**
532 * changed Radio button in Vertical Align group.
533 */
534 void TileDialog::HorizAlign_changed()
535 {
536 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
537 if (HorizLeftRadioButton.get_active()) {
538 HorizAlign = 0;
539 prefs->setInt("/dialogs/gridtiler/HorizAlign", 0);
540 } else if (HorizCentreRadioButton.get_active()){
541 HorizAlign = 1;
542 prefs->setInt("/dialogs/gridtiler/HorizAlign", 1);
543 } else if (HorizRightRadioButton.get_active()){
544 HorizAlign = 2;
545 prefs->setInt("/dialogs/gridtiler/HorizAlign", 2);
546 }
547 }
549 /**
550 * Desktop selection changed
551 */
552 void TileDialog::updateSelection()
553 {
554 // quit if run by the attr_changed listener
555 if (updating) {
556 return;
557 }
559 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
560 // in turn, prevent listener from responding
561 updating = true;
562 SPDesktop *desktop = getDesktop();
563 Inkscape::Selection *selection = sp_desktop_selection (desktop);
564 const GSList *items = selection->itemList();
565 int selcount = g_slist_length((GSList *)items);
567 if (NoOfColsSpinner.get_value()>1 && NoOfRowsSpinner.get_value()>1){
568 // Update the number of rows assuming number of columns wanted remains same.
569 double NoOfRows = ceil(selcount / NoOfColsSpinner.get_value());
570 NoOfRowsSpinner.set_value(NoOfRows);
572 // if the selection has less than the number set for one row, reduce it appropriately
573 if (selcount<NoOfColsSpinner.get_value()) {
574 double NoOfCols = ceil(selcount / NoOfRowsSpinner.get_value());
575 NoOfColsSpinner.set_value(NoOfCols);
576 prefs->setInt("/dialogs/gridtiler/NoOfCols", NoOfCols);
577 }
578 } else {
579 double PerRow = ceil(sqrt(selcount));
580 double PerCol = ceil(sqrt(selcount));
581 NoOfRowsSpinner.set_value(PerRow);
582 NoOfColsSpinner.set_value(PerCol);
583 prefs->setInt("/dialogs/gridtiler/NoOfCols", static_cast<int>(PerCol));
585 }
587 updating=false;
589 }
593 /*##########################
594 ## Experimental
595 ##########################*/
597 static void updateSelectionCallback(Inkscape::Application */*inkscape*/, Inkscape::Selection */*selection*/, TileDialog *dlg)
598 {
599 TileDialog *tledlg = (TileDialog *) dlg;
600 tledlg->updateSelection();
601 }
604 //#########################################################################
605 //## C O N S T R U C T O R / D E S T R U C T O R
606 //#########################################################################
607 /**
608 * Constructor
609 */
610 TileDialog::TileDialog()
611 : UI::Widget::Panel("", "/dialogs/gridtiler", SP_VERB_SELECTION_GRIDTILE)
612 {
613 // bool used by spin button callbacks to stop loops where they change each other.
614 updating = false;
615 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
617 // could not do this in gtkmm - there's no Gtk::SizeGroup public constructor (!)
618 GtkSizeGroup *_col1 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
619 GtkSizeGroup *_col2 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
620 GtkSizeGroup *_col3 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
622 {
623 // Selection Change signal
624 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (updateSelectionCallback), this);
625 }
627 Gtk::Box *contents = _getContents();
629 #define MARGIN 2
631 //##Set up the panel
633 SPDesktop *desktop = getDesktop();
635 Inkscape::Selection *selection = sp_desktop_selection (desktop);
636 int selcount = 1;
637 if (!selection->isEmpty()) {
638 GSList const *items = selection->itemList();
639 selcount = g_slist_length((GSList *)items);
640 }
643 /*#### Number of Rows ####*/
645 double PerRow = ceil(sqrt(selcount));
646 double PerCol = ceil(sqrt(selcount));
648 #ifdef DEBUG_GRID_ARRANGE
649 g_print("/n PerRox = %f PerCol = %f selcount = %d",PerRow,PerCol,selcount);
650 #endif
652 NoOfRowsLabel.set_label(_("Rows:"));
653 NoOfRowsBox.pack_start(NoOfRowsLabel, false, false, MARGIN);
655 NoOfRowsSpinner.set_digits(0);
656 NoOfRowsSpinner.set_increments(1, 5);
657 NoOfRowsSpinner.set_range(1.0, 100.0);
658 NoOfRowsSpinner.set_value(PerCol);
659 NoOfRowsSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_col_spinbutton_changed));
660 tips.set_tip(NoOfRowsSpinner, _("Number of rows"));
661 NoOfRowsBox.pack_start(NoOfRowsSpinner, false, false, MARGIN);
662 gtk_size_group_add_widget(_col1, (GtkWidget *) NoOfRowsBox.gobj());
664 RowHeightButton.set_label(_("Equal height"));
665 double AutoRow = prefs->getDouble("/dialogs/gridtiler/AutoRowSize", 15);
666 if (AutoRow>0)
667 AutoRowSize=true;
668 else
669 AutoRowSize=false;
670 RowHeightButton.set_active(AutoRowSize);
672 NoOfRowsBox.pack_start(RowHeightButton, false, false, MARGIN);
674 tips.set_tip(RowHeightButton, _("If not set, each row has the height of the tallest object in it"));
675 RowHeightButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::on_RowSize_checkbutton_changed));
677 {
678 /*#### Radio buttons to control vertical alignment ####*/
680 VertAlignLabel.set_label(_("Align:"));
681 VertAlignHBox.pack_start(VertAlignLabel, false, false, MARGIN);
683 VertTopRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
684 VertAlignGroup = VertTopRadioButton.get_group();
685 VertAlignVBox.pack_start(VertTopRadioButton, false, false, 0);
687 VertCentreRadioButton.set_group(VertAlignGroup);
688 VertCentreRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
689 VertAlignVBox.pack_start(VertCentreRadioButton, false, false, 0);
691 VertBotRadioButton.set_group(VertAlignGroup);
692 VertBotRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
693 VertAlignVBox.pack_start(VertBotRadioButton, false, false, 0);
695 VertAlign = prefs->getInt("/dialogs/gridtiler/VertAlign", 1);
696 if (VertAlign == 0) {
697 VertTopRadioButton.set_active(TRUE);
698 }
699 else if (VertAlign == 1) {
700 VertCentreRadioButton.set_active(TRUE);
701 }
702 else if (VertAlign == 2){
703 VertBotRadioButton.set_active(TRUE);
704 }
705 VertAlignHBox.pack_start(VertAlignVBox, false, false, MARGIN);
706 NoOfRowsBox.pack_start(VertAlignHBox, false, false, MARGIN);
707 }
709 SpinsHBox.pack_start(NoOfRowsBox, false, false, MARGIN);
712 /*#### Label for X ####*/
713 padXByYLabel.set_label(" ");
714 XByYLabelVBox.pack_start(padXByYLabel, false, false, MARGIN);
715 XByYLabel.set_markup(" × ");
716 XByYLabelVBox.pack_start(XByYLabel, false, false, MARGIN);
717 SpinsHBox.pack_start(XByYLabelVBox, false, false, MARGIN);
718 gtk_size_group_add_widget(_col2, (GtkWidget *) XByYLabelVBox.gobj());
720 /*#### Number of columns ####*/
722 NoOfColsLabel.set_label(_("Columns:"));
723 NoOfColsBox.pack_start(NoOfColsLabel, false, false, MARGIN);
725 NoOfColsSpinner.set_digits(0);
726 NoOfColsSpinner.set_increments(1, 5);
727 NoOfColsSpinner.set_range(1.0, 100.0);
728 NoOfColsSpinner.set_value(PerRow);
729 NoOfColsSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_row_spinbutton_changed));
730 tips.set_tip(NoOfColsSpinner, _("Number of columns"));
731 NoOfColsBox.pack_start(NoOfColsSpinner, false, false, MARGIN);
732 gtk_size_group_add_widget(_col3, (GtkWidget *) NoOfColsBox.gobj());
734 ColumnWidthButton.set_label(_("Equal width"));
735 double AutoCol = prefs->getDouble("/dialogs/gridtiler/AutoColSize", 15);
736 if (AutoCol>0)
737 AutoColSize=true;
738 else
739 AutoColSize=false;
740 ColumnWidthButton.set_active(AutoColSize);
741 NoOfColsBox.pack_start(ColumnWidthButton, false, false, MARGIN);
743 tips.set_tip(ColumnWidthButton, _("If not set, each column has the width of the widest object in it"));
744 ColumnWidthButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::on_ColSize_checkbutton_changed));
747 {
748 /*#### Radio buttons to control horizontal alignment ####*/
750 HorizAlignLabel.set_label(_("Align:"));
751 HorizAlignVBox.pack_start(HorizAlignLabel, false, false, MARGIN);
753 HorizAlignHBox.pack_start(*(new Gtk::HBox()), true, true, 0); // centering strut
755 HorizLeftRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
756 HorizAlignGroup = HorizLeftRadioButton.get_group();
757 HorizAlignHBox.pack_start(HorizLeftRadioButton, false, false, 0);
759 HorizCentreRadioButton.set_group(HorizAlignGroup);
760 HorizCentreRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
761 HorizAlignHBox.pack_start(HorizCentreRadioButton, false, false, 0);
763 HorizRightRadioButton.set_group(HorizAlignGroup);
764 HorizRightRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
765 HorizAlignHBox.pack_start(HorizRightRadioButton, false, false, 0);
767 HorizAlignHBox.pack_start(*(new Gtk::HBox()), true, true, 0); // centering strut
769 HorizAlign = prefs->getInt("/dialogs/gridtiler/HorizAlign", 1);
770 if (HorizAlign == 0) {
771 HorizLeftRadioButton.set_active(TRUE);
772 }
773 else if (HorizAlign == 1) {
774 HorizCentreRadioButton.set_active(TRUE);
775 }
776 else if (HorizAlign == 2) {
777 HorizRightRadioButton.set_active(TRUE);
778 }
779 HorizAlignVBox.pack_start(HorizAlignHBox, false, false, MARGIN);
780 NoOfColsBox.pack_start(HorizAlignVBox, false, false, MARGIN);
781 }
783 SpinsHBox.pack_start(NoOfColsBox, false, false, MARGIN);
785 TileBox.pack_start(SpinsHBox, false, false, MARGIN);
787 {
788 /*#### Radio buttons to control spacing manually or to fit selection bbox ####*/
789 SpaceByBBoxRadioButton.set_label(_("Fit into selection box"));
790 SpaceByBBoxRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::Spacing_button_changed));
791 SpacingGroup = SpaceByBBoxRadioButton.get_group();
793 SpacingVBox.pack_start(SpaceByBBoxRadioButton, false, false, MARGIN);
795 SpaceManualRadioButton.set_label(_("Set spacing:"));
796 SpaceManualRadioButton.set_group(SpacingGroup);
797 SpaceManualRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::Spacing_button_changed));
798 SpacingVBox.pack_start(SpaceManualRadioButton, false, false, MARGIN);
800 TileBox.pack_start(SpacingVBox, false, false, MARGIN);
801 }
803 {
804 /*#### Y Padding ####*/
806 GtkWidget *i = sp_icon_new (Inkscape::ICON_SIZE_MENU, "clonetiler_per_row");
807 YPadBox.pack_start (*(Glib::wrap(i)), false, false, MARGIN);
809 YPadSpinner.set_digits(1);
810 YPadSpinner.set_increments(0.2, 2);
811 YPadSpinner.set_range(-10000, 10000);
812 double YPad = prefs->getDouble("/dialogs/gridtiler/YPad", 15);
813 YPadSpinner.set_value(YPad);
814 YPadBox.pack_start(YPadSpinner, true, true, MARGIN);
815 tips.set_tip(YPadSpinner, _("Vertical spacing between rows (px units)"));
816 YPadSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_ypad_spinbutton_changed));
817 gtk_size_group_add_widget(_col1, (GtkWidget *) YPadBox.gobj());
819 SizesHBox.pack_start(YPadBox, false, false, MARGIN);
820 }
822 {
823 Gtk::HBox *spacer = new Gtk::HBox;
824 SizesHBox.pack_start(*spacer, false, false, 0);
825 gtk_size_group_add_widget(_col2, (GtkWidget *) spacer->gobj());
826 }
828 {
829 /*#### X padding ####*/
831 GtkWidget *i = sp_icon_new (Inkscape::ICON_SIZE_MENU, "clonetiler_per_column");
832 XPadBox.pack_start (*(Glib::wrap(i)), false, false, MARGIN);
834 XPadSpinner.set_digits(1);
835 XPadSpinner.set_increments(0.2, 2);
836 XPadSpinner.set_range(-10000, 10000);
837 double XPad = prefs->getDouble("/dialogs/gridtiler/XPad", 15);
838 XPadSpinner.set_value(XPad);
839 XPadBox.pack_start(XPadSpinner, true, true, MARGIN);
840 tips.set_tip(XPadSpinner, _("Horizontal spacing between columns (px units)"));
841 XPadSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_xpad_spinbutton_changed));
842 gtk_size_group_add_widget(_col3, (GtkWidget *) XPadBox.gobj());
844 SizesHBox.pack_start(XPadBox, false, false, MARGIN);
845 }
848 TileBox.pack_start(SizesHBox, false, false, MARGIN);
850 contents->pack_start(TileBox);
852 double SpacingType = prefs->getDouble("/dialogs/gridtiler/SpacingType", 15);
853 if (SpacingType>0) {
854 ManualSpacing=true;
855 } else {
856 ManualSpacing=false;
857 }
858 SpaceManualRadioButton.set_active(ManualSpacing);
859 SpaceByBBoxRadioButton.set_active(!ManualSpacing);
860 SizesHBox.set_sensitive (ManualSpacing);
862 //## The OK button
863 TileOkButton = addResponseButton(_("Arrange"), GTK_RESPONSE_APPLY);
864 tips.set_tip((*TileOkButton), _("Arrange selected objects"));
866 show_all_children();
867 }
869 } //namespace Dialog
870 } //namespace UI
871 } //namespace Inkscape
873 /*
874 Local Variables:
875 mode:c++
876 c-file-style:"stroustrup"
877 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
878 indent-tabs-mode:nil
879 fill-column:99
880 End:
881 */
882 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 ::