f597c601baf783b3aeae1c812063e4dfedfc1a13
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 "verbs.h"
27 #include "preferences.h"
28 #include "inkscape.h"
29 #include "desktop-handles.h"
30 #include "selection.h"
31 #include "document.h"
32 #include "sp-item.h"
33 #include "widgets/icon.h"
34 #include "tiledialog.h"
38 /*
39 * Sort items by their x co-ordinates, taking account of y (keeps rows intact)
40 *
41 * <0 *elem1 goes before *elem2
42 * 0 *elem1 == *elem2
43 * >0 *elem1 goes after *elem2
44 */
45 int
46 sp_compare_x_position(SPItem *first, SPItem *second)
47 {
48 using Geom::X;
49 using Geom::Y;
51 boost::optional<Geom::Rect> a = first->getBounds(sp_item_i2doc_affine(first));
52 boost::optional<Geom::Rect> b = second->getBounds(sp_item_i2doc_affine(second));
54 if ( !a || !b ) {
55 // FIXME?
56 return 0;
57 }
59 double const a_height = a->dimensions()[Y];
60 double const b_height = b->dimensions()[Y];
62 bool a_in_b_vert = false;
63 if ((a->min()[Y] < b->min()[Y] + 0.1) && (a->min()[Y] > b->min()[Y] - b_height)) {
64 a_in_b_vert = true;
65 } else if ((b->min()[Y] < a->min()[Y] + 0.1) && (b->min()[Y] > a->min()[Y] - a_height)) {
66 a_in_b_vert = true;
67 } else if (b->min()[Y] == a->min()[Y]) {
68 a_in_b_vert = true;
69 } else {
70 a_in_b_vert = false;
71 }
73 if (!a_in_b_vert) {
74 return -1;
75 }
76 if (a_in_b_vert && a->min()[X] > b->min()[X]) {
77 return 1;
78 }
79 if (a_in_b_vert && a->min()[X] < b->min()[X]) {
80 return -1;
81 }
82 return 0;
83 }
85 /*
86 * Sort items by their y co-ordinates.
87 */
88 int
89 sp_compare_y_position(SPItem *first, SPItem *second)
90 {
91 boost::optional<Geom::Rect> a = first->getBounds(sp_item_i2doc_affine(first));
92 boost::optional<Geom::Rect> b = second->getBounds(sp_item_i2doc_affine(second));
94 if ( !a || !b ) {
95 // FIXME?
96 return 0;
97 }
99 if (a->min()[Geom::Y] > b->min()[Geom::Y]) {
100 return 1;
101 }
102 if (a->min()[Geom::Y] < b->min()[Geom::Y]) {
103 return -1;
104 }
106 return 0;
107 }
109 namespace Inkscape {
110 namespace UI {
111 namespace Dialog {
114 //#########################################################################
115 //## E V E N T S
116 //#########################################################################
118 /*
119 *
120 * This arranges the selection in a grid pattern.
121 *
122 */
124 void TileDialog::Grid_Arrange ()
125 {
127 int cnt,row_cnt,col_cnt,a,row,col;
128 double grid_left,grid_top,col_width,row_height,paddingx,paddingy,width, height, new_x, new_y,cx,cy;
129 double total_col_width,total_row_height;
130 col_width = 0;
131 row_height = 0;
132 total_col_width=0;
133 total_row_height=0;
135 // check for correct numbers in the row- and col-spinners
136 on_col_spinbutton_changed();
137 on_row_spinbutton_changed();
139 // set padding to manual values
140 paddingx = XPadSpinner.get_value();
141 paddingy = YPadSpinner.get_value();
143 std::vector<double> row_heights;
144 std::vector<double> col_widths;
145 std::vector<double> row_ys;
146 std::vector<double> col_xs;
148 int NoOfCols = NoOfColsSpinner.get_value_as_int();
149 int NoOfRows = NoOfRowsSpinner.get_value_as_int();
151 width = 0;
152 for (a=0;a<NoOfCols; a++){
153 col_widths.push_back(width);
154 }
156 height = 0;
157 for (a=0;a<NoOfRows; a++){
158 row_heights.push_back(height);
159 }
160 grid_left = 99999;
161 grid_top = 99999;
163 SPDesktop *desktop = getDesktop();
164 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
166 Inkscape::Selection *selection = sp_desktop_selection (desktop);
167 const GSList *items = selection->itemList();
168 cnt=0;
169 for (; items != NULL; items = items->next) {
170 SPItem *item = SP_ITEM(items->data);
171 boost::optional<Geom::Rect> b = item->getBounds(sp_item_i2doc_affine(item));
172 if (!b) {
173 continue;
174 }
176 width = b->dimensions()[Geom::X];
177 height = b->dimensions()[Geom::Y];
179 cx = b->midpoint()[Geom::X];
180 cy = b->midpoint()[Geom::Y];
182 if (b->min()[Geom::X] < grid_left) {
183 grid_left = b->min()[Geom::X];
184 }
185 if (b->min()[Geom::Y] < grid_top) {
186 grid_top = b->min()[Geom::Y];
187 }
188 if (width > col_width) {
189 col_width = width;
190 }
191 if (height > row_height) {
192 row_height = height;
193 }
194 }
197 // require the sorting done before we can calculate row heights etc.
199 const GSList *items2 = selection->itemList();
200 GSList *rev = g_slist_copy((GSList *) items2);
201 GSList *sorted = NULL;
202 rev = g_slist_sort(rev, (GCompareFunc) sp_compare_y_position);
203 sorted = g_slist_sort(rev, (GCompareFunc) sp_compare_x_position);
206 // Calculate individual Row and Column sizes if necessary
209 cnt=0;
210 const GSList *sizes = sorted;
211 for (; sizes != NULL; sizes = sizes->next) {
212 SPItem *item = SP_ITEM(sizes->data);
213 boost::optional<Geom::Rect> b = item->getBounds(sp_item_i2doc_affine(item));
214 if (b) {
215 width = b->dimensions()[Geom::X];
216 height = b->dimensions()[Geom::Y];
217 if (width > col_widths[(cnt % NoOfCols)]) {
218 col_widths[(cnt % NoOfCols)] = width;
219 }
220 if (height > row_heights[(cnt / NoOfCols)]) {
221 row_heights[(cnt / NoOfCols)] = height;
222 }
223 }
225 cnt++;
226 }
229 /// Make sure the top and left of the grid dont move by compensating for align values.
230 if (RowHeightButton.get_active()){
231 grid_top = grid_top - (((row_height - row_heights[0]) / 2)*(VertAlign));
232 }
233 if (ColumnWidthButton.get_active()){
234 grid_left = grid_left - (((col_width - col_widths[0]) /2)*(HorizAlign));
235 }
237 #ifdef DEBUG_GRID_ARRANGE
238 g_print("\n cx = %f cy= %f gridleft=%f",cx,cy,grid_left);
239 #endif
241 // Calculate total widths and heights, allowing for columns and rows non uniformly sized.
243 if (ColumnWidthButton.get_active()){
244 total_col_width = col_width * NoOfCols;
245 col_widths.clear();
246 for (a=0;a<NoOfCols; a++){
247 col_widths.push_back(col_width);
248 }
249 } else {
250 for (a = 0; a < (int)col_widths.size(); a++)
251 {
252 total_col_width += col_widths[a] ;
253 }
254 }
256 if (RowHeightButton.get_active()){
257 total_row_height = row_height * NoOfRows;
258 row_heights.clear();
259 for (a=0;a<NoOfRows; a++){
260 row_heights.push_back(row_height);
261 }
262 } else {
263 for (a = 0; a < (int)row_heights.size(); a++)
264 {
265 total_row_height += row_heights[a] ;
266 }
267 }
270 boost::optional<Geom::Rect> sel_bbox = selection->bounds();
271 // Fit to bbox, calculate padding between rows accordingly.
272 if ( sel_bbox && !SpaceManualRadioButton.get_active() ){
273 #ifdef DEBUG_GRID_ARRANGE
274 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));
275 #endif
276 paddingx = (sel_bbox->width() - total_col_width) / (NoOfCols -1);
277 paddingy = (sel_bbox->height() - total_row_height) / (NoOfRows -1);
278 }
280 /*
281 Horizontal align - Left = 0
282 Centre = 1
283 Right = 2
285 Vertical align - Top = 0
286 Middle = 1
287 Bottom = 2
289 X position is calculated by taking the grids left co-ord, adding the distance to the column,
290 then adding 1/2 the spacing multiplied by the align variable above,
291 Y position likewise, takes the top of the grid, adds the y to the current row then adds the padding in to align it.
293 */
295 // Calculate row and column x and y coords required to allow for columns and rows which are non uniformly sized.
297 for (a=0;a<NoOfCols; a++){
298 if (a<1) col_xs.push_back(0);
299 else col_xs.push_back(col_widths[a-1]+paddingx+col_xs[a-1]);
300 }
303 for (a=0;a<NoOfRows; a++){
304 if (a<1) row_ys.push_back(0);
305 else row_ys.push_back(row_heights[a-1]+paddingy+row_ys[a-1]);
306 }
308 cnt=0;
309 for (row_cnt=0; ((sorted != NULL) && (row_cnt<NoOfRows)); row_cnt++) {
311 GSList *current_row = NULL;
312 for (col_cnt = 0; ((sorted != NULL) && (col_cnt<NoOfCols)); col_cnt++) {
313 current_row = g_slist_append (current_row, sorted->data);
314 sorted = sorted->next;
315 }
317 for (; current_row != NULL; current_row = current_row->next) {
318 SPItem *item=SP_ITEM(current_row->data);
319 Inkscape::XML::Node *repr = SP_OBJECT_REPR(item);
320 boost::optional<Geom::Rect> b = item->getBounds(sp_item_i2doc_affine(item));
321 Geom::Point min;
322 if (b) {
323 width = b->dimensions()[Geom::X];
324 height = b->dimensions()[Geom::Y];
325 min = b->min();
326 } else {
327 width = height = 0;
328 min = Geom::Point(0, 0);
329 }
331 row = cnt / NoOfCols;
332 col = cnt % NoOfCols;
334 new_x = grid_left + (((col_widths[col] - width)/2)*HorizAlign) + col_xs[col];
335 new_y = grid_top + (((row_heights[row] - height)/2)*VertAlign) + row_ys[row];
337 // signs are inverted between x and y due to y inversion
338 Geom::Point move = Geom::Point(new_x - min[Geom::X], min[Geom::Y] - new_y);
339 Geom::Matrix const affine = Geom::Matrix(Geom::Translate(move));
340 sp_item_set_i2d_affine(item, sp_item_i2d_affine(item) * affine);
341 sp_item_write_transform(item, repr, item->transform, NULL);
342 SP_OBJECT (current_row->data)->updateRepr();
343 cnt +=1;
344 }
345 g_slist_free (current_row);
346 }
348 sp_document_done (sp_desktop_document (desktop), SP_VERB_SELECTION_GRIDTILE,
349 _("Arrange in a grid"));
351 }
354 //#########################################################################
355 //## E V E N T S
356 //#########################################################################
359 void TileDialog::_apply()
360 {
361 Grid_Arrange();
362 }
365 /**
366 * changed value in # of columns spinbox.
367 */
368 void TileDialog::on_row_spinbutton_changed()
369 {
370 // quit if run by the attr_changed listener
371 if (updating) {
372 return;
373 }
375 // in turn, prevent listener from responding
376 updating = true;
377 SPDesktop *desktop = getDesktop();
379 Inkscape::Selection *selection = sp_desktop_selection (desktop);
381 GSList const *items = selection->itemList();
382 int selcount = g_slist_length((GSList *)items);
384 double PerCol = ceil(selcount / NoOfColsSpinner.get_value());
385 NoOfRowsSpinner.set_value(PerCol);
386 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
387 prefs->setDouble("/dialogs/gridtiler/NoOfCols", NoOfColsSpinner.get_value());
388 updating=false;
389 }
391 /**
392 * changed value in # of rows spinbox.
393 */
394 void TileDialog::on_col_spinbutton_changed()
395 {
396 // quit if run by the attr_changed listener
397 if (updating) {
398 return;
399 }
401 // in turn, prevent listener from responding
402 updating = true;
403 SPDesktop *desktop = getDesktop();
404 Inkscape::Selection *selection = sp_desktop_selection (desktop);
406 GSList const *items = selection->itemList();
407 int selcount = g_slist_length((GSList *)items);
409 double PerRow = ceil(selcount / NoOfRowsSpinner.get_value());
410 NoOfColsSpinner.set_value(PerRow);
411 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
412 prefs->setDouble("/dialogs/gridtiler/NoOfCols", PerRow);
414 updating=false;
415 }
417 /**
418 * changed value in x padding spinbox.
419 */
420 void TileDialog::on_xpad_spinbutton_changed()
421 {
422 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
423 prefs->setDouble("/dialogs/gridtiler/XPad", XPadSpinner.get_value());
425 }
427 /**
428 * changed value in y padding spinbox.
429 */
430 void TileDialog::on_ypad_spinbutton_changed()
431 {
432 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
433 prefs->setDouble("/dialogs/gridtiler/YPad", YPadSpinner.get_value());
434 }
437 /**
438 * checked/unchecked autosize Rows button.
439 */
440 void TileDialog::on_RowSize_checkbutton_changed()
441 {
442 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
443 if (RowHeightButton.get_active()) {
444 prefs->setDouble("/dialogs/gridtiler/AutoRowSize", 20);
445 } else {
446 prefs->setDouble("/dialogs/gridtiler/AutoRowSize", -20);
447 }
448 RowHeightBox.set_sensitive ( !RowHeightButton.get_active());
449 }
451 /**
452 * checked/unchecked autosize Rows button.
453 */
454 void TileDialog::on_ColSize_checkbutton_changed()
455 {
456 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
457 if (ColumnWidthButton.get_active()) {
458 prefs->setDouble("/dialogs/gridtiler/AutoColSize", 20);
459 } else {
460 prefs->setDouble("/dialogs/gridtiler/AutoColSize", -20);
461 }
462 ColumnWidthBox.set_sensitive ( !ColumnWidthButton.get_active());
463 }
465 /**
466 * changed value in columns spinbox.
467 */
468 void TileDialog::on_rowSize_spinbutton_changed()
469 {
470 // quit if run by the attr_changed listener
471 if (updating) {
472 return;
473 }
475 // in turn, prevent listener from responding
476 updating = true;
477 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
478 prefs->setDouble("/dialogs/gridtiler/RowHeight", RowHeightSpinner.get_value());
479 updating=false;
481 }
483 /**
484 * changed value in rows spinbox.
485 */
486 void TileDialog::on_colSize_spinbutton_changed()
487 {
488 // quit if run by the attr_changed listener
489 if (updating) {
490 return;
491 }
493 // in turn, prevent listener from responding
494 updating = true;
495 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
496 prefs->setDouble("/dialogs/gridtiler/ColWidth", ColumnWidthSpinner.get_value());
497 updating=false;
499 }
501 /**
502 * changed Radio button in Spacing group.
503 */
504 void TileDialog::Spacing_button_changed()
505 {
506 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
507 if (SpaceManualRadioButton.get_active()) {
508 prefs->setDouble("/dialogs/gridtiler/SpacingType", 20);
509 } else {
510 prefs->setDouble("/dialogs/gridtiler/SpacingType", -20);
511 }
513 SizesHBox.set_sensitive ( SpaceManualRadioButton.get_active());
514 }
516 /**
517 * changed Radio button in Vertical Align group.
518 */
519 void TileDialog::VertAlign_changed()
520 {
521 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
522 if (VertTopRadioButton.get_active()) {
523 VertAlign = 0;
524 prefs->setInt("/dialogs/gridtiler/VertAlign", 0);
525 } else if (VertCentreRadioButton.get_active()){
526 VertAlign = 1;
527 prefs->setInt("/dialogs/gridtiler/VertAlign", 1);
528 } else if (VertBotRadioButton.get_active()){
529 VertAlign = 2;
530 prefs->setInt("/dialogs/gridtiler/VertAlign", 2);
531 }
532 }
534 /**
535 * changed Radio button in Vertical Align group.
536 */
537 void TileDialog::HorizAlign_changed()
538 {
539 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
540 if (HorizLeftRadioButton.get_active()) {
541 HorizAlign = 0;
542 prefs->setInt("/dialogs/gridtiler/HorizAlign", 0);
543 } else if (HorizCentreRadioButton.get_active()){
544 HorizAlign = 1;
545 prefs->setInt("/dialogs/gridtiler/HorizAlign", 1);
546 } else if (HorizRightRadioButton.get_active()){
547 HorizAlign = 2;
548 prefs->setInt("/dialogs/gridtiler/HorizAlign", 2);
549 }
550 }
552 /**
553 * Desktop selection changed
554 */
555 void TileDialog::updateSelection()
556 {
557 // quit if run by the attr_changed listener
558 if (updating) {
559 return;
560 }
562 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
563 double col_width = 0;
564 double row_height = 0;
565 // in turn, prevent listener from responding
566 updating = true;
567 SPDesktop *desktop = getDesktop();
568 Inkscape::Selection *selection = sp_desktop_selection (desktop);
569 const GSList *items = selection->itemList();
570 int selcount = g_slist_length((GSList *)items);
572 if (NoOfColsSpinner.get_value()>1 && NoOfRowsSpinner.get_value()>1){
573 // Update the number of rows assuming number of columns wanted remains same.
574 double NoOfRows = ceil(selcount / NoOfColsSpinner.get_value());
575 NoOfRowsSpinner.set_value(NoOfRows);
577 // if the selection has less than the number set for one row, reduce it appropriately
578 if (selcount<NoOfColsSpinner.get_value()) {
579 double NoOfCols = ceil(selcount / NoOfRowsSpinner.get_value());
580 NoOfColsSpinner.set_value(NoOfCols);
581 prefs->setInt("/dialogs/gridtiler/NoOfCols", NoOfCols);
582 }
583 } else {
584 double PerRow = ceil(sqrt(selcount));
585 double PerCol = ceil(sqrt(selcount));
586 NoOfRowsSpinner.set_value(PerRow);
587 NoOfColsSpinner.set_value(PerCol);
588 prefs->setInt("/dialogs/gridtiler/NoOfCols", static_cast<int>(PerCol));
590 }
592 updating=false;
594 }
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()
616 : UI::Widget::Panel("", "/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;
620 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
622 // could not do this in gtkmm - there's no Gtk::SizeGroup public constructor (!)
623 GtkSizeGroup *_col1 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
624 GtkSizeGroup *_col2 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
625 GtkSizeGroup *_col3 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
627 {
628 // Selection Change signal
629 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (updateSelectionCallback), this);
630 }
632 Gtk::Box *contents = _getContents();
634 #define MARGIN 2
636 //##Set up the panel
638 SPDesktop *desktop = getDesktop();
640 Inkscape::Selection *selection = sp_desktop_selection (desktop);
641 int selcount = 1;
642 if (!selection->isEmpty()) {
643 GSList const *items = selection->itemList();
644 selcount = g_slist_length((GSList *)items);
645 }
648 /*#### Number of Rows ####*/
650 double PerRow = ceil(sqrt(selcount));
651 double PerCol = ceil(sqrt(selcount));
653 #ifdef DEBUG_GRID_ARRANGE
654 g_print("/n PerRox = %f PerCol = %f selcount = %d",PerRow,PerCol,selcount);
655 #endif
657 NoOfRowsLabel.set_label(_("Rows:"));
658 NoOfRowsBox.pack_start(NoOfRowsLabel, false, false, MARGIN);
660 NoOfRowsSpinner.set_digits(0);
661 NoOfRowsSpinner.set_increments(1, 5);
662 NoOfRowsSpinner.set_range(1.0, 100.0);
663 NoOfRowsSpinner.set_value(PerCol);
664 NoOfRowsSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_col_spinbutton_changed));
665 tips.set_tip(NoOfRowsSpinner, _("Number of rows"));
666 NoOfRowsBox.pack_start(NoOfRowsSpinner, false, false, MARGIN);
667 gtk_size_group_add_widget(_col1, (GtkWidget *) NoOfRowsBox.gobj());
669 RowHeightButton.set_label(_("Equal height"));
670 double AutoRow = prefs->getDouble("/dialogs/gridtiler/AutoRowSize", 15);
671 if (AutoRow>0)
672 AutoRowSize=true;
673 else
674 AutoRowSize=false;
675 RowHeightButton.set_active(AutoRowSize);
677 NoOfRowsBox.pack_start(RowHeightButton, false, false, MARGIN);
679 tips.set_tip(RowHeightButton, _("If not set, each row has the height of the tallest object in it"));
680 RowHeightButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::on_RowSize_checkbutton_changed));
682 {
683 /*#### Radio buttons to control vertical alignment ####*/
685 VertAlignLabel.set_label(_("Align:"));
686 VertAlignHBox.pack_start(VertAlignLabel, false, false, MARGIN);
688 VertTopRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
689 VertAlignGroup = VertTopRadioButton.get_group();
690 VertAlignVBox.pack_start(VertTopRadioButton, false, false, 0);
692 VertCentreRadioButton.set_group(VertAlignGroup);
693 VertCentreRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
694 VertAlignVBox.pack_start(VertCentreRadioButton, false, false, 0);
696 VertBotRadioButton.set_group(VertAlignGroup);
697 VertBotRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
698 VertAlignVBox.pack_start(VertBotRadioButton, false, false, 0);
700 VertAlign = prefs->getInt("/dialogs/gridtiler/VertAlign", 1);
701 if (VertAlign == 0) {
702 VertTopRadioButton.set_active(TRUE);
703 }
704 else if (VertAlign == 1) {
705 VertCentreRadioButton.set_active(TRUE);
706 }
707 else if (VertAlign == 2){
708 VertBotRadioButton.set_active(TRUE);
709 }
710 VertAlignHBox.pack_start(VertAlignVBox, false, false, MARGIN);
711 NoOfRowsBox.pack_start(VertAlignHBox, false, false, MARGIN);
712 }
714 SpinsHBox.pack_start(NoOfRowsBox, false, false, MARGIN);
717 /*#### Label for X ####*/
718 padXByYLabel.set_label(" ");
719 XByYLabelVBox.pack_start(padXByYLabel, false, false, MARGIN);
720 XByYLabel.set_markup(" × ");
721 XByYLabelVBox.pack_start(XByYLabel, false, false, MARGIN);
722 SpinsHBox.pack_start(XByYLabelVBox, false, false, MARGIN);
723 gtk_size_group_add_widget(_col2, (GtkWidget *) XByYLabelVBox.gobj());
725 /*#### Number of columns ####*/
727 NoOfColsLabel.set_label(_("Columns:"));
728 NoOfColsBox.pack_start(NoOfColsLabel, false, false, MARGIN);
730 NoOfColsSpinner.set_digits(0);
731 NoOfColsSpinner.set_increments(1, 5);
732 NoOfColsSpinner.set_range(1.0, 100.0);
733 NoOfColsSpinner.set_value(PerRow);
734 NoOfColsSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_row_spinbutton_changed));
735 tips.set_tip(NoOfColsSpinner, _("Number of columns"));
736 NoOfColsBox.pack_start(NoOfColsSpinner, false, false, MARGIN);
737 gtk_size_group_add_widget(_col3, (GtkWidget *) NoOfColsBox.gobj());
739 ColumnWidthButton.set_label(_("Equal width"));
740 double AutoCol = prefs->getDouble("/dialogs/gridtiler/AutoColSize", 15);
741 if (AutoCol>0)
742 AutoColSize=true;
743 else
744 AutoColSize=false;
745 ColumnWidthButton.set_active(AutoColSize);
746 NoOfColsBox.pack_start(ColumnWidthButton, false, false, MARGIN);
748 tips.set_tip(ColumnWidthButton, _("If not set, each column has the width of the widest object in it"));
749 ColumnWidthButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::on_ColSize_checkbutton_changed));
752 {
753 /*#### Radio buttons to control horizontal alignment ####*/
755 HorizAlignLabel.set_label(_("Align:"));
756 HorizAlignVBox.pack_start(HorizAlignLabel, false, false, MARGIN);
758 HorizAlignHBox.pack_start(*(new Gtk::HBox()), true, true, 0); // centering strut
760 HorizLeftRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
761 HorizAlignGroup = HorizLeftRadioButton.get_group();
762 HorizAlignHBox.pack_start(HorizLeftRadioButton, false, false, 0);
764 HorizCentreRadioButton.set_group(HorizAlignGroup);
765 HorizCentreRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
766 HorizAlignHBox.pack_start(HorizCentreRadioButton, false, false, 0);
768 HorizRightRadioButton.set_group(HorizAlignGroup);
769 HorizRightRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
770 HorizAlignHBox.pack_start(HorizRightRadioButton, false, false, 0);
772 HorizAlignHBox.pack_start(*(new Gtk::HBox()), true, true, 0); // centering strut
774 HorizAlign = prefs->getInt("/dialogs/gridtiler/HorizAlign", 1);
775 if (HorizAlign == 0) {
776 HorizLeftRadioButton.set_active(TRUE);
777 }
778 else if (HorizAlign == 1) {
779 HorizCentreRadioButton.set_active(TRUE);
780 }
781 else if (HorizAlign == 2) {
782 HorizRightRadioButton.set_active(TRUE);
783 }
784 HorizAlignVBox.pack_start(HorizAlignHBox, false, false, MARGIN);
785 NoOfColsBox.pack_start(HorizAlignVBox, false, false, MARGIN);
786 }
788 SpinsHBox.pack_start(NoOfColsBox, false, false, MARGIN);
790 TileBox.pack_start(SpinsHBox, false, false, MARGIN);
792 {
793 /*#### Radio buttons to control spacing manually or to fit selection bbox ####*/
794 SpaceByBBoxRadioButton.set_label(_("Fit into selection box"));
795 SpaceByBBoxRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::Spacing_button_changed));
796 SpacingGroup = SpaceByBBoxRadioButton.get_group();
798 SpacingVBox.pack_start(SpaceByBBoxRadioButton, false, false, MARGIN);
800 SpaceManualRadioButton.set_label(_("Set spacing:"));
801 SpaceManualRadioButton.set_group(SpacingGroup);
802 SpaceManualRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::Spacing_button_changed));
803 SpacingVBox.pack_start(SpaceManualRadioButton, false, false, MARGIN);
805 TileBox.pack_start(SpacingVBox, false, false, MARGIN);
806 }
808 {
809 /*#### Y Padding ####*/
811 GtkWidget *i = sp_icon_new (Inkscape::ICON_SIZE_MENU, "clonetiler_per_row");
812 YPadBox.pack_start (*(Glib::wrap(i)), false, false, MARGIN);
814 YPadSpinner.set_digits(1);
815 YPadSpinner.set_increments(0.2, 2);
816 YPadSpinner.set_range(-10000, 10000);
817 double YPad = prefs->getDouble("/dialogs/gridtiler/YPad", 15);
818 YPadSpinner.set_value(YPad);
819 YPadBox.pack_start(YPadSpinner, true, true, MARGIN);
820 tips.set_tip(YPadSpinner, _("Vertical spacing between rows (px units)"));
821 YPadSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_ypad_spinbutton_changed));
822 gtk_size_group_add_widget(_col1, (GtkWidget *) YPadBox.gobj());
824 SizesHBox.pack_start(YPadBox, false, false, MARGIN);
825 }
827 {
828 Gtk::HBox *spacer = new Gtk::HBox;
829 SizesHBox.pack_start(*spacer, false, false, 0);
830 gtk_size_group_add_widget(_col2, (GtkWidget *) spacer->gobj());
831 }
833 {
834 /*#### X padding ####*/
836 GtkWidget *i = sp_icon_new (Inkscape::ICON_SIZE_MENU, "clonetiler_per_column");
837 XPadBox.pack_start (*(Glib::wrap(i)), false, false, MARGIN);
839 XPadSpinner.set_digits(1);
840 XPadSpinner.set_increments(0.2, 2);
841 XPadSpinner.set_range(-10000, 10000);
842 double XPad = prefs->getDouble("/dialogs/gridtiler/XPad", 15);
843 XPadSpinner.set_value(XPad);
844 XPadBox.pack_start(XPadSpinner, true, true, MARGIN);
845 tips.set_tip(XPadSpinner, _("Horizontal spacing between columns (px units)"));
846 XPadSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_xpad_spinbutton_changed));
847 gtk_size_group_add_widget(_col3, (GtkWidget *) XPadBox.gobj());
849 SizesHBox.pack_start(XPadBox, false, false, MARGIN);
850 }
853 TileBox.pack_start(SizesHBox, false, false, MARGIN);
855 contents->pack_start(TileBox);
857 double SpacingType = prefs->getDouble("/dialogs/gridtiler/SpacingType", 15);
858 if (SpacingType>0) {
859 ManualSpacing=true;
860 } else {
861 ManualSpacing=false;
862 }
863 SpaceManualRadioButton.set_active(ManualSpacing);
864 SpaceByBBoxRadioButton.set_active(!ManualSpacing);
865 SizesHBox.set_sensitive (ManualSpacing);
867 //## The OK button
868 TileOkButton = addResponseButton(_("Arrange"), GTK_RESPONSE_APPLY);
869 tips.set_tip((*TileOkButton), _("Arrange selected objects"));
871 show_all_children();
872 }
874 } //namespace Dialog
875 } //namespace UI
876 } //namespace Inkscape
878 /*
879 Local Variables:
880 mode:c++
881 c-file-style:"stroustrup"
882 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
883 indent-tabs-mode:nil
884 fill-column:99
885 End:
886 */
887 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 ::