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