693426d2d82f7b01a9c9e9cfc1c8e7e4e5c2601a
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), SP_VERB_SELECTION_GRIDTILE,
330 _("Arrange in a grid"));
332 }
335 //#########################################################################
336 //## E V E N T S
337 //#########################################################################
340 void TileDialog::_apply()
341 {
342 Grid_Arrange();
343 }
346 /**
347 * changed value in # of columns spinbox.
348 */
349 void TileDialog::on_row_spinbutton_changed()
350 {
351 // quit if run by the attr_changed listener
352 if (updating) {
353 return;
354 }
356 // in turn, prevent listener from responding
357 updating = true;
358 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
360 Inkscape::Selection *selection = sp_desktop_selection (desktop);
362 GSList const *items = selection->itemList();
363 int selcount = g_slist_length((GSList *)items);
365 double PerCol = ceil(selcount / NoOfColsSpinner.get_value());
366 NoOfRowsSpinner.set_value(PerCol);
367 prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", NoOfColsSpinner.get_value());
368 updating=false;
369 }
371 /**
372 * changed value in # of rows spinbox.
373 */
374 void TileDialog::on_col_spinbutton_changed()
375 {
376 // quit if run by the attr_changed listener
377 if (updating) {
378 return;
379 }
381 // in turn, prevent listener from responding
382 updating = true;
383 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
384 Inkscape::Selection *selection = sp_desktop_selection (desktop);
386 GSList const *items = selection->itemList();
387 int selcount = g_slist_length((GSList *)items);
389 double PerRow = ceil(selcount / NoOfRowsSpinner.get_value());
390 NoOfColsSpinner.set_value(PerRow);
391 prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", PerRow);
393 updating=false;
394 }
396 /**
397 * changed value in x padding spinbox.
398 */
399 void TileDialog::on_xpad_spinbutton_changed()
400 {
402 prefs_set_double_attribute ("dialogs.gridtiler", "XPad", XPadSpinner.get_value());
404 }
406 /**
407 * changed value in y padding spinbox.
408 */
409 void TileDialog::on_ypad_spinbutton_changed()
410 {
412 prefs_set_double_attribute ("dialogs.gridtiler", "YPad", YPadSpinner.get_value());
414 }
417 /**
418 * checked/unchecked autosize Rows button.
419 */
420 void TileDialog::on_RowSize_checkbutton_changed()
421 {
423 if (RowHeightButton.get_active()) {
424 prefs_set_double_attribute ("dialogs.gridtiler", "AutoRowSize", 20);
425 } else {
426 prefs_set_double_attribute ("dialogs.gridtiler", "AutoRowSize", -20);
427 }
428 RowHeightBox.set_sensitive ( !RowHeightButton.get_active());
429 }
431 /**
432 * checked/unchecked autosize Rows button.
433 */
434 void TileDialog::on_ColSize_checkbutton_changed()
435 {
437 if (ColumnWidthButton.get_active()) {
438 prefs_set_double_attribute ("dialogs.gridtiler", "AutoColSize", 20);
439 } else {
440 prefs_set_double_attribute ("dialogs.gridtiler", "AutoColSize", -20);
441 }
442 ColumnWidthBox.set_sensitive ( !ColumnWidthButton.get_active());
444 }
446 /**
447 * changed value in columns spinbox.
448 */
449 void TileDialog::on_rowSize_spinbutton_changed()
450 {
451 // quit if run by the attr_changed listener
452 if (updating) {
453 return;
454 }
456 // in turn, prevent listener from responding
457 updating = true;
458 prefs_set_double_attribute ("dialogs.gridtiler", "RowHeight", RowHeightSpinner.get_value());
459 updating=false;
461 }
463 /**
464 * changed value in rows spinbox.
465 */
466 void TileDialog::on_colSize_spinbutton_changed()
467 {
468 // quit if run by the attr_changed listener
469 if (updating) {
470 return;
471 }
473 // in turn, prevent listener from responding
474 updating = true;
475 prefs_set_double_attribute ("dialogs.gridtiler", "ColWidth", ColumnWidthSpinner.get_value());
476 updating=false;
478 }
480 /**
481 * changed Radio button in Spacing group.
482 */
483 void TileDialog::Spacing_button_changed()
484 {
485 if (SpaceManualRadioButton.get_active()) {
486 prefs_set_double_attribute ("dialogs.gridtiler", "SpacingType", 20);
487 } else {
488 prefs_set_double_attribute ("dialogs.gridtiler", "SpacingType", -20);
489 }
491 SizesHBox.set_sensitive ( SpaceManualRadioButton.get_active());
492 }
494 /**
495 * changed Radio button in Vertical Align group.
496 */
497 void TileDialog::VertAlign_changed()
498 {
499 if (VertTopRadioButton.get_active()) {
500 VertAlign = 0;
501 prefs_set_double_attribute ("dialogs.gridtiler", "VertAlign", 0);
502 } else if (VertCentreRadioButton.get_active()){
503 VertAlign = 1;
504 prefs_set_double_attribute ("dialogs.gridtiler", "VertAlign", 1);
505 } else if (VertBotRadioButton.get_active()){
506 VertAlign = 2;
507 prefs_set_double_attribute ("dialogs.gridtiler", "VertAlign", 2);
508 }
510 }
512 /**
513 * changed Radio button in Vertical Align group.
514 */
515 void TileDialog::HorizAlign_changed()
516 {
517 if (HorizLeftRadioButton.get_active()) {
518 HorizAlign = 0;
519 prefs_set_double_attribute ("dialogs.gridtiler", "HorizAlign", 0);
520 } else if (HorizCentreRadioButton.get_active()){
521 HorizAlign = 1;
522 prefs_set_double_attribute ("dialogs.gridtiler", "HorizAlign", 1);
523 } else if (HorizRightRadioButton.get_active()){
524 HorizAlign = 2;
525 prefs_set_double_attribute ("dialogs.gridtiler", "HorizAlign", 2);
526 }
528 }
530 /**
531 * Desktop selection changed
532 */
533 void TileDialog::updateSelection()
534 {
535 double col_width, row_height;
536 // quit if run by the attr_changed listener
537 if (updating) {
538 return;
539 }
541 col_width=0;
542 row_height=0;
543 // in turn, prevent listener from responding
544 updating = true;
545 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
546 Inkscape::Selection *selection = sp_desktop_selection (desktop);
547 const GSList *items = selection->itemList();
548 int selcount = g_slist_length((GSList *)items);
550 if (NoOfColsSpinner.get_value()>1){
551 // Update the number of rows assuming number of columns wanted remains same.
552 double NoOfRows = ceil(selcount / NoOfColsSpinner.get_value());
553 NoOfRowsSpinner.set_value(NoOfRows);
555 // if the selection has less than the number set for one row, reduce it appropriately
556 if (selcount<NoOfColsSpinner.get_value()) {
557 double NoOfCols = ceil(selcount / NoOfRowsSpinner.get_value());
558 NoOfColsSpinner.set_value(NoOfCols);
559 prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", NoOfCols);
560 }
561 } else {
562 NoOfColsSpinner.set_value(selcount);
563 prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", selcount);
565 }
567 updating=false;
569 }
575 /*##########################
576 ## Experimental
577 ##########################*/
579 static void updateSelectionCallback(Inkscape::Application *inkscape, Inkscape::Selection *selection, TileDialog *dlg)
580 {
581 TileDialog *tledlg = (TileDialog *) dlg;
582 tledlg->updateSelection();
583 }
586 //#########################################################################
587 //## C O N S T R U C T O R / D E S T R U C T O R
588 //#########################################################################
589 /**
590 * Constructor
591 */
592 TileDialog::TileDialog()
593 : Dialog ("dialogs.gridtiler", SP_VERB_SELECTION_GRIDTILE)
594 {
595 // bool used by spin button callbacks to stop loops where they change each other.
596 updating = false;
598 // could not do this in gtkmm - there's no Gtk::SizeGroup public constructor (!)
599 GtkSizeGroup *_col1 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
600 GtkSizeGroup *_col2 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
601 GtkSizeGroup *_col3 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
603 {
604 // Selection Change signal
605 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (updateSelectionCallback), this);
606 }
608 Gtk::VBox *mainVBox = get_vbox();
610 #define MARGIN 2
612 //##Set up the panel
614 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
616 Inkscape::Selection *selection = sp_desktop_selection (desktop);
617 int selcount = 1;
618 if (!selection->isEmpty()) {
619 GSList const *items = selection->itemList();
620 selcount = g_slist_length((GSList *)items);
621 }
624 /*#### Number of Rows ####*/
626 double PerRow = selcount;
627 double PerCol = 1;
629 #ifdef DEBUG_GRID_ARRANGE
630 g_print("/n PerRox = %f PerCol = %f selcount = %d",PerRow,PerCol,selcount);
631 #endif
633 NoOfRowsLabel.set_label(_("Rows:"));
634 NoOfRowsBox.pack_start(NoOfRowsLabel, false, false, MARGIN);
636 NoOfRowsSpinner.set_digits(0);
637 NoOfRowsSpinner.set_increments(1, 5);
638 NoOfRowsSpinner.set_range(1.0, 100.0);
639 NoOfRowsSpinner.set_value(PerCol);
640 NoOfRowsSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_col_spinbutton_changed));
641 tips.set_tip(NoOfRowsSpinner, _("Number of rows"));
642 NoOfRowsBox.pack_start(NoOfRowsSpinner, false, false, MARGIN);
643 gtk_size_group_add_widget(_col1, (GtkWidget *) NoOfRowsBox.gobj());
645 RowHeightButton.set_label(_("Equal height"));
646 double AutoRow = prefs_get_double_attribute ("dialogs.gridtiler", "AutoRowSize", 15);
647 if (AutoRow>0)
648 AutoRowSize=true;
649 else
650 AutoRowSize=false;
651 RowHeightButton.set_active(AutoRowSize);
653 NoOfRowsBox.pack_start(RowHeightButton, false, false, MARGIN);
655 tips.set_tip(RowHeightButton, _("If not set, each row has the height of the tallest object in it"));
656 RowHeightButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::on_RowSize_checkbutton_changed));
658 {
659 /*#### Radio buttons to control vertical alignment ####*/
661 VertAlignLabel.set_label(_("Align:"));
662 VertAlignHBox.pack_start(VertAlignLabel, false, false, MARGIN);
664 VertTopRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
665 VertAlignGroup = VertTopRadioButton.get_group();
666 VertAlignVBox.pack_start(VertTopRadioButton, false, false, 0);
668 VertCentreRadioButton.set_group(VertAlignGroup);
669 VertCentreRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
670 VertAlignVBox.pack_start(VertCentreRadioButton, false, false, 0);
672 VertBotRadioButton.set_group(VertAlignGroup);
673 VertBotRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
674 VertAlignVBox.pack_start(VertBotRadioButton, false, false, 0);
676 VertAlign = prefs_get_double_attribute ("dialogs.gridtiler", "VertAlign", 1);
677 if (VertAlign == 0) {
678 VertTopRadioButton.set_active(TRUE);
679 }
680 else if (VertAlign == 1) {
681 VertCentreRadioButton.set_active(TRUE);
682 }
683 else if (VertAlign == 2){
684 VertBotRadioButton.set_active(TRUE);
685 }
686 VertAlignHBox.pack_start(VertAlignVBox, false, false, MARGIN);
687 NoOfRowsBox.pack_start(VertAlignHBox, false, false, MARGIN);
688 }
690 SpinsHBox.pack_start(NoOfRowsBox, false, false, MARGIN);
693 /*#### Label for X ####*/
694 padXByYLabel.set_label(" ");
695 XByYLabelVBox.pack_start(padXByYLabel, false, false, MARGIN);
696 XByYLabel.set_markup(" × ");
697 XByYLabelVBox.pack_start(XByYLabel, false, false, MARGIN);
698 SpinsHBox.pack_start(XByYLabelVBox, false, false, MARGIN);
699 gtk_size_group_add_widget(_col2, (GtkWidget *) XByYLabelVBox.gobj());
701 /*#### Number of columns ####*/
703 NoOfColsLabel.set_label(_("Columns:"));
704 NoOfColsBox.pack_start(NoOfColsLabel, false, false, MARGIN);
706 NoOfColsSpinner.set_digits(0);
707 NoOfColsSpinner.set_increments(1, 5);
708 NoOfColsSpinner.set_range(1.0, 100.0);
709 NoOfColsSpinner.set_value(PerRow);
710 NoOfColsSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_row_spinbutton_changed));
711 tips.set_tip(NoOfColsSpinner, _("Number of columns"));
712 NoOfColsBox.pack_start(NoOfColsSpinner, false, false, MARGIN);
713 gtk_size_group_add_widget(_col3, (GtkWidget *) NoOfColsBox.gobj());
715 ColumnWidthButton.set_label(_("Equal width"));
716 double AutoCol = prefs_get_double_attribute ("dialogs.gridtiler", "AutoColSize", 15);
717 if (AutoCol>0)
718 AutoColSize=true;
719 else
720 AutoColSize=false;
721 ColumnWidthButton.set_active(AutoColSize);
722 NoOfColsBox.pack_start(ColumnWidthButton, false, false, MARGIN);
724 tips.set_tip(ColumnWidthButton, _("If not set, each column has the width of the widest object in it"));
725 ColumnWidthButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::on_ColSize_checkbutton_changed));
728 {
729 /*#### Radio buttons to control horizontal alignment ####*/
731 HorizAlignLabel.set_label(_("Align:"));
732 HorizAlignVBox.pack_start(HorizAlignLabel, false, false, MARGIN);
734 HorizAlignHBox.pack_start(*(new Gtk::HBox()), true, true, 0); // centering strut
736 HorizLeftRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
737 HorizAlignGroup = HorizLeftRadioButton.get_group();
738 HorizAlignHBox.pack_start(HorizLeftRadioButton, false, false, 0);
740 HorizCentreRadioButton.set_group(HorizAlignGroup);
741 HorizCentreRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
742 HorizAlignHBox.pack_start(HorizCentreRadioButton, false, false, 0);
744 HorizRightRadioButton.set_group(HorizAlignGroup);
745 HorizRightRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
746 HorizAlignHBox.pack_start(HorizRightRadioButton, false, false, 0);
748 HorizAlignHBox.pack_start(*(new Gtk::HBox()), true, true, 0); // centering strut
750 HorizAlign = prefs_get_double_attribute ("dialogs.gridtiler", "HorizAlign", 1);
751 if (HorizAlign == 0) {
752 HorizLeftRadioButton.set_active(TRUE);
753 }
754 else if (HorizAlign == 1) {
755 HorizCentreRadioButton.set_active(TRUE);
756 }
757 else if (HorizAlign == 2) {
758 HorizRightRadioButton.set_active(TRUE);
759 }
760 HorizAlignVBox.pack_start(HorizAlignHBox, false, false, MARGIN);
761 NoOfColsBox.pack_start(HorizAlignVBox, false, false, MARGIN);
762 }
764 SpinsHBox.pack_start(NoOfColsBox, false, false, MARGIN);
766 TileBox.pack_start(SpinsHBox, false, false, MARGIN);
768 {
769 /*#### Radio buttons to control spacing manually or to fit selection bbox ####*/
770 SpaceByBBoxRadioButton.set_label(_("Fit into selection box"));
771 SpaceByBBoxRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::Spacing_button_changed));
772 SpacingGroup = SpaceByBBoxRadioButton.get_group();
774 SpacingVBox.pack_start(SpaceByBBoxRadioButton, false, false, MARGIN);
776 SpaceManualRadioButton.set_label(_("Set spacing:"));
777 SpaceManualRadioButton.set_group(SpacingGroup);
778 SpaceManualRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::Spacing_button_changed));
779 SpacingVBox.pack_start(SpaceManualRadioButton, false, false, MARGIN);
781 TileBox.pack_start(SpacingVBox, false, false, MARGIN);
782 }
784 {
785 /*#### Y Padding ####*/
787 GtkWidget *i = sp_icon_new (Inkscape::ICON_SIZE_MENU, "clonetiler_per_row");
788 YPadBox.pack_start (*(Glib::wrap(i)), false, false, MARGIN);
790 YPadSpinner.set_digits(1);
791 YPadSpinner.set_increments(0.2, 2);
792 YPadSpinner.set_range(-10000, 10000);
793 double YPad = prefs_get_double_attribute ("dialogs.gridtiler", "YPad", 15);
794 YPadSpinner.set_value(YPad);
795 YPadBox.pack_start(YPadSpinner, true, true, MARGIN);
796 tips.set_tip(YPadSpinner, _("Vertical spacing between rows (px units)"));
797 YPadSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_ypad_spinbutton_changed));
798 gtk_size_group_add_widget(_col1, (GtkWidget *) YPadBox.gobj());
800 SizesHBox.pack_start(YPadBox, false, false, MARGIN);
801 }
803 {
804 Gtk::HBox *spacer = new Gtk::HBox;
805 SizesHBox.pack_start(*spacer, false, false, 0);
806 gtk_size_group_add_widget(_col2, (GtkWidget *) spacer->gobj());
807 }
809 {
810 /*#### X padding ####*/
812 GtkWidget *i = sp_icon_new (Inkscape::ICON_SIZE_MENU, "clonetiler_per_column");
813 XPadBox.pack_start (*(Glib::wrap(i)), false, false, MARGIN);
815 XPadSpinner.set_digits(1);
816 XPadSpinner.set_increments(0.2, 2);
817 XPadSpinner.set_range(-10000, 10000);
818 double XPad = prefs_get_double_attribute ("dialogs.gridtiler", "XPad", 15);
819 XPadSpinner.set_value(XPad);
820 XPadBox.pack_start(XPadSpinner, true, true, MARGIN);
821 tips.set_tip(XPadSpinner, _("Horizontal spacing between columns (px units)"));
822 XPadSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_xpad_spinbutton_changed));
823 gtk_size_group_add_widget(_col3, (GtkWidget *) XPadBox.gobj());
825 SizesHBox.pack_start(XPadBox, false, false, MARGIN);
826 }
829 TileBox.pack_start(SizesHBox, false, false, MARGIN);
831 mainVBox->pack_start(TileBox);
833 double SpacingType = prefs_get_double_attribute ("dialogs.gridtiler", "SpacingType", 15);
834 if (SpacingType>0) {
835 ManualSpacing=true;
836 } else {
837 ManualSpacing=false;
838 }
839 SpaceManualRadioButton.set_active(ManualSpacing);
840 SpaceByBBoxRadioButton.set_active(!ManualSpacing);
841 SizesHBox.set_sensitive (ManualSpacing);
843 //## The OK button
844 TileOkButton = add_button(Gtk::Stock::APPLY, GTK_RESPONSE_APPLY);
845 tips.set_tip((*TileOkButton), _("Arrange selected objects"));
847 show_all_children();
848 }
855 } //namespace Dialog
856 } //namespace UI
857 } //namespace Inkscape
859 //#########################################################################
860 //## E N D O F F I L E
861 //#########################################################################
864 /*
865 Local Variables:
866 mode:c++
867 c-file-style:"stroustrup"
868 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
869 indent-tabs-mode:nil
870 fill-column:99
871 End:
872 */
873 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :