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 * Abhishek Sharma
9 *
10 * Copyright (C) 2004 Bob Jamison
11 * Copyright (C) 2004 John Cliff
12 *
13 * Released under GNU GPL, read the file 'COPYING' for more information
14 */
15 //#define DEBUG_GRID_ARRANGE 1
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #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 "tile.h"
35 #include "desktop.h"
37 /*
38 * Sort items by their x co-ordinates, taking account of y (keeps rows intact)
39 *
40 * <0 *elem1 goes before *elem2
41 * 0 *elem1 == *elem2
42 * >0 *elem1 goes after *elem2
43 */
44 int
45 sp_compare_x_position(SPItem *first, SPItem *second)
46 {
47 using Geom::X;
48 using Geom::Y;
50 Geom::OptRect a = first->getBounds(first->i2doc_affine());
51 Geom::OptRect b = second->getBounds(second->i2doc_affine());
53 if ( !a || !b ) {
54 // FIXME?
55 return 0;
56 }
58 double const a_height = a->dimensions()[Y];
59 double const b_height = b->dimensions()[Y];
61 bool a_in_b_vert = false;
62 if ((a->min()[Y] < b->min()[Y] + 0.1) && (a->min()[Y] > b->min()[Y] - b_height)) {
63 a_in_b_vert = true;
64 } else if ((b->min()[Y] < a->min()[Y] + 0.1) && (b->min()[Y] > a->min()[Y] - a_height)) {
65 a_in_b_vert = true;
66 } else if (b->min()[Y] == a->min()[Y]) {
67 a_in_b_vert = true;
68 } else {
69 a_in_b_vert = false;
70 }
72 if (!a_in_b_vert) {
73 return -1;
74 }
75 if (a_in_b_vert && a->min()[X] > b->min()[X]) {
76 return 1;
77 }
78 if (a_in_b_vert && a->min()[X] < b->min()[X]) {
79 return -1;
80 }
81 return 0;
82 }
84 /*
85 * Sort items by their y co-ordinates.
86 */
87 int
88 sp_compare_y_position(SPItem *first, SPItem *second)
89 {
90 Geom::OptRect a = first->getBounds(first->i2doc_affine());
91 Geom::OptRect b = second->getBounds(second->i2doc_affine());
93 if ( !a || !b ) {
94 // FIXME?
95 return 0;
96 }
98 if (a->min()[Geom::Y] > b->min()[Geom::Y]) {
99 return 1;
100 }
101 if (a->min()[Geom::Y] < b->min()[Geom::Y]) {
102 return -1;
103 }
105 return 0;
106 }
108 namespace Inkscape {
109 namespace UI {
110 namespace Dialog {
113 //#########################################################################
114 //## E V E N T S
115 //#########################################################################
117 /*
118 *
119 * This arranges the selection in a grid pattern.
120 *
121 */
123 void TileDialog::Grid_Arrange ()
124 {
126 int cnt,row_cnt,col_cnt,a,row,col;
127 double grid_left,grid_top,col_width,row_height,paddingx,paddingy,width, height, new_x, new_y,cx,cy;
128 double total_col_width,total_row_height;
129 col_width = 0;
130 row_height = 0;
131 total_col_width=0;
132 total_row_height=0;
134 // check for correct numbers in the row- and col-spinners
135 on_col_spinbutton_changed();
136 on_row_spinbutton_changed();
138 // set padding to manual values
139 paddingx = XPadSpinner.get_value();
140 paddingy = YPadSpinner.get_value();
142 std::vector<double> row_heights;
143 std::vector<double> col_widths;
144 std::vector<double> row_ys;
145 std::vector<double> col_xs;
147 int NoOfCols = NoOfColsSpinner.get_value_as_int();
148 int NoOfRows = NoOfRowsSpinner.get_value_as_int();
150 width = 0;
151 for (a=0;a<NoOfCols; a++){
152 col_widths.push_back(width);
153 }
155 height = 0;
156 for (a=0;a<NoOfRows; a++){
157 row_heights.push_back(height);
158 }
159 grid_left = 99999;
160 grid_top = 99999;
162 SPDesktop *desktop = getDesktop();
163 sp_desktop_document(desktop)->ensureUpToDate();
165 Inkscape::Selection *selection = sp_desktop_selection (desktop);
166 const GSList *items = selection ? selection->itemList() : 0;
167 cnt=0;
168 for (; items != NULL; items = items->next) {
169 SPItem *item = SP_ITEM(items->data);
170 Geom::OptRect b = item->getBounds(item->i2doc_affine());
171 if (!b) {
172 continue;
173 }
175 width = b->dimensions()[Geom::X];
176 height = b->dimensions()[Geom::Y];
178 cx = b->midpoint()[Geom::X];
179 cy = b->midpoint()[Geom::Y];
181 if (b->min()[Geom::X] < grid_left) {
182 grid_left = b->min()[Geom::X];
183 }
184 if (b->min()[Geom::Y] < grid_top) {
185 grid_top = b->min()[Geom::Y];
186 }
187 if (width > col_width) {
188 col_width = width;
189 }
190 if (height > row_height) {
191 row_height = height;
192 }
193 }
196 // require the sorting done before we can calculate row heights etc.
198 g_return_if_fail(selection);
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 Geom::OptRect b = item->getBounds(item->i2doc_affine());
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 Geom::OptRect 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 Geom::OptRect b = item->getBounds(item->i2doc_affine());
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 item->set_i2d_affine(item->i2d_affine() * affine);
341 item->doWriteTransform(repr, item->transform, NULL);
342 SP_OBJECT (current_row->data)->updateRepr();
343 cnt +=1;
344 }
345 g_slist_free (current_row);
346 }
348 DocumentUndo::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 = desktop ? desktop->selection : 0;
380 g_return_if_fail( selection );
382 GSList const *items = selection->itemList();
383 int selcount = g_slist_length((GSList *)items);
385 double PerCol = ceil(selcount / NoOfColsSpinner.get_value());
386 NoOfRowsSpinner.set_value(PerCol);
387 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
388 prefs->setDouble("/dialogs/gridtiler/NoOfCols", NoOfColsSpinner.get_value());
389 updating=false;
390 }
392 /**
393 * changed value in # of rows spinbox.
394 */
395 void TileDialog::on_col_spinbutton_changed()
396 {
397 // quit if run by the attr_changed listener
398 if (updating) {
399 return;
400 }
402 // in turn, prevent listener from responding
403 updating = true;
404 SPDesktop *desktop = getDesktop();
405 Inkscape::Selection *selection = desktop ? desktop->selection : 0;
406 g_return_if_fail(selection);
408 GSList const *items = selection->itemList();
409 int selcount = g_slist_length((GSList *)items);
411 double PerRow = ceil(selcount / NoOfRowsSpinner.get_value());
412 NoOfColsSpinner.set_value(PerRow);
413 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
414 prefs->setDouble("/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 {
424 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
425 prefs->setDouble("/dialogs/gridtiler/XPad", XPadSpinner.get_value());
427 }
429 /**
430 * changed value in y padding spinbox.
431 */
432 void TileDialog::on_ypad_spinbutton_changed()
433 {
434 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
435 prefs->setDouble("/dialogs/gridtiler/YPad", YPadSpinner.get_value());
436 }
439 /**
440 * checked/unchecked autosize Rows button.
441 */
442 void TileDialog::on_RowSize_checkbutton_changed()
443 {
444 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
445 if (RowHeightButton.get_active()) {
446 prefs->setDouble("/dialogs/gridtiler/AutoRowSize", 20);
447 } else {
448 prefs->setDouble("/dialogs/gridtiler/AutoRowSize", -20);
449 }
450 RowHeightBox.set_sensitive ( !RowHeightButton.get_active());
451 }
453 /**
454 * checked/unchecked autosize Rows button.
455 */
456 void TileDialog::on_ColSize_checkbutton_changed()
457 {
458 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
459 if (ColumnWidthButton.get_active()) {
460 prefs->setDouble("/dialogs/gridtiler/AutoColSize", 20);
461 } else {
462 prefs->setDouble("/dialogs/gridtiler/AutoColSize", -20);
463 }
464 ColumnWidthBox.set_sensitive ( !ColumnWidthButton.get_active());
465 }
467 /**
468 * changed value in columns spinbox.
469 */
470 void TileDialog::on_rowSize_spinbutton_changed()
471 {
472 // quit if run by the attr_changed listener
473 if (updating) {
474 return;
475 }
477 // in turn, prevent listener from responding
478 updating = true;
479 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
480 prefs->setDouble("/dialogs/gridtiler/RowHeight", RowHeightSpinner.get_value());
481 updating=false;
483 }
485 /**
486 * changed value in rows spinbox.
487 */
488 void TileDialog::on_colSize_spinbutton_changed()
489 {
490 // quit if run by the attr_changed listener
491 if (updating) {
492 return;
493 }
495 // in turn, prevent listener from responding
496 updating = true;
497 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
498 prefs->setDouble("/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 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
509 if (SpaceManualRadioButton.get_active()) {
510 prefs->setDouble("/dialogs/gridtiler/SpacingType", 20);
511 } else {
512 prefs->setDouble("/dialogs/gridtiler/SpacingType", -20);
513 }
515 SizesHBox.set_sensitive ( SpaceManualRadioButton.get_active());
516 }
518 /**
519 * changed Radio button in Vertical Align group.
520 */
521 void TileDialog::VertAlign_changed()
522 {
523 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
524 if (VertTopRadioButton.get_active()) {
525 VertAlign = 0;
526 prefs->setInt("/dialogs/gridtiler/VertAlign", 0);
527 } else if (VertCentreRadioButton.get_active()){
528 VertAlign = 1;
529 prefs->setInt("/dialogs/gridtiler/VertAlign", 1);
530 } else if (VertBotRadioButton.get_active()){
531 VertAlign = 2;
532 prefs->setInt("/dialogs/gridtiler/VertAlign", 2);
533 }
534 }
536 /**
537 * changed Radio button in Vertical Align group.
538 */
539 void TileDialog::HorizAlign_changed()
540 {
541 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
542 if (HorizLeftRadioButton.get_active()) {
543 HorizAlign = 0;
544 prefs->setInt("/dialogs/gridtiler/HorizAlign", 0);
545 } else if (HorizCentreRadioButton.get_active()){
546 HorizAlign = 1;
547 prefs->setInt("/dialogs/gridtiler/HorizAlign", 1);
548 } else if (HorizRightRadioButton.get_active()){
549 HorizAlign = 2;
550 prefs->setInt("/dialogs/gridtiler/HorizAlign", 2);
551 }
552 }
554 /**
555 * Desktop selection changed
556 */
557 void TileDialog::updateSelection()
558 {
559 // quit if run by the attr_changed listener
560 if (updating) {
561 return;
562 }
564 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
565 // in turn, prevent listener from responding
566 updating = true;
567 SPDesktop *desktop = getDesktop();
568 Inkscape::Selection *selection = desktop ? desktop->selection : 0;
569 GSList const *items = selection ? selection->itemList() : 0;
571 if (items) {
572 int selcount = g_slist_length((GSList *)items);
574 if (NoOfColsSpinner.get_value() > 1 && NoOfRowsSpinner.get_value() > 1){
575 // Update the number of rows assuming number of columns wanted remains same.
576 double NoOfRows = ceil(selcount / NoOfColsSpinner.get_value());
577 NoOfRowsSpinner.set_value(NoOfRows);
579 // if the selection has less than the number set for one row, reduce it appropriately
580 if (selcount < NoOfColsSpinner.get_value()) {
581 double NoOfCols = ceil(selcount / NoOfRowsSpinner.get_value());
582 NoOfColsSpinner.set_value(NoOfCols);
583 prefs->setInt("/dialogs/gridtiler/NoOfCols", NoOfCols);
584 }
585 } else {
586 double PerRow = ceil(sqrt(selcount));
587 double PerCol = ceil(sqrt(selcount));
588 NoOfRowsSpinner.set_value(PerRow);
589 NoOfColsSpinner.set_value(PerCol);
590 prefs->setInt("/dialogs/gridtiler/NoOfCols", static_cast<int>(PerCol));
591 }
592 }
594 updating = false;
595 }
599 /*##########################
600 ## Experimental
601 ##########################*/
603 static void updateSelectionCallback(Inkscape::Application */*inkscape*/, Inkscape::Selection */*selection*/, TileDialog *dlg)
604 {
605 TileDialog *tledlg = (TileDialog *) dlg;
606 tledlg->updateSelection();
607 }
610 //#########################################################################
611 //## C O N S T R U C T O R / D E S T R U C T O R
612 //#########################################################################
613 /**
614 * Constructor
615 */
616 TileDialog::TileDialog()
617 : UI::Widget::Panel("", "/dialogs/gridtiler", SP_VERB_SELECTION_GRIDTILE)
618 {
619 // bool used by spin button callbacks to stop loops where they change each other.
620 updating = false;
621 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
623 // could not do this in gtkmm - there's no Gtk::SizeGroup public constructor (!)
624 GtkSizeGroup *_col1 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
625 GtkSizeGroup *_col2 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
626 GtkSizeGroup *_col3 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
628 {
629 // Selection Change signal
630 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (updateSelectionCallback), this);
631 }
633 Gtk::Box *contents = _getContents();
635 #define MARGIN 2
637 //##Set up the panel
639 SPDesktop *desktop = getDesktop();
641 Inkscape::Selection *selection = desktop ? desktop->selection : 0;
642 g_return_if_fail( selection );
643 int selcount = 1;
644 if (!selection->isEmpty()) {
645 GSList const *items = selection->itemList();
646 selcount = g_slist_length((GSList *)items);
647 }
650 /*#### Number of Rows ####*/
652 double PerRow = ceil(sqrt(selcount));
653 double PerCol = ceil(sqrt(selcount));
655 #ifdef DEBUG_GRID_ARRANGE
656 g_print("/n PerRox = %f PerCol = %f selcount = %d",PerRow,PerCol,selcount);
657 #endif
659 NoOfRowsLabel.set_text_with_mnemonic(_("_Rows:"));
660 NoOfRowsLabel.set_mnemonic_widget(NoOfRowsSpinner);
661 NoOfRowsBox.pack_start(NoOfRowsLabel, false, false, MARGIN);
663 NoOfRowsSpinner.set_digits(0);
664 NoOfRowsSpinner.set_increments(1, 0);
665 NoOfRowsSpinner.set_range(1.0, 100.0);
666 NoOfRowsSpinner.set_value(PerCol);
667 NoOfRowsSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_col_spinbutton_changed));
668 tips.set_tip(NoOfRowsSpinner, _("Number of rows"));
669 NoOfRowsBox.pack_start(NoOfRowsSpinner, false, false, MARGIN);
670 gtk_size_group_add_widget(_col1, (GtkWidget *) NoOfRowsBox.gobj());
672 RowHeightButton.set_label(_("Equal _height"));
673 RowHeightButton.set_use_underline(true);
674 double AutoRow = prefs->getDouble("/dialogs/gridtiler/AutoRowSize", 15);
675 if (AutoRow>0)
676 AutoRowSize=true;
677 else
678 AutoRowSize=false;
679 RowHeightButton.set_active(AutoRowSize);
681 NoOfRowsBox.pack_start(RowHeightButton, false, false, MARGIN);
683 tips.set_tip(RowHeightButton, _("If not set, each row has the height of the tallest object in it"));
684 RowHeightButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::on_RowSize_checkbutton_changed));
686 {
687 /*#### Radio buttons to control vertical alignment ####*/
689 VertAlignLabel.set_label(_("Align:"));
690 VertAlignHBox.pack_start(VertAlignLabel, false, false, MARGIN);
692 VertTopRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
693 VertAlignGroup = VertTopRadioButton.get_group();
694 VertAlignVBox.pack_start(VertTopRadioButton, false, false, 0);
696 VertCentreRadioButton.set_group(VertAlignGroup);
697 VertCentreRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
698 VertAlignVBox.pack_start(VertCentreRadioButton, false, false, 0);
700 VertBotRadioButton.set_group(VertAlignGroup);
701 VertBotRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
702 VertAlignVBox.pack_start(VertBotRadioButton, false, false, 0);
704 VertAlign = prefs->getInt("/dialogs/gridtiler/VertAlign", 1);
705 if (VertAlign == 0) {
706 VertTopRadioButton.set_active(TRUE);
707 }
708 else if (VertAlign == 1) {
709 VertCentreRadioButton.set_active(TRUE);
710 }
711 else if (VertAlign == 2){
712 VertBotRadioButton.set_active(TRUE);
713 }
714 VertAlignHBox.pack_start(VertAlignVBox, false, false, MARGIN);
715 NoOfRowsBox.pack_start(VertAlignHBox, false, false, MARGIN);
716 }
718 SpinsHBox.pack_start(NoOfRowsBox, false, false, MARGIN);
721 /*#### Label for X ####*/
722 padXByYLabel.set_label(" ");
723 XByYLabelVBox.pack_start(padXByYLabel, false, false, MARGIN);
724 XByYLabel.set_markup(" × ");
725 XByYLabelVBox.pack_start(XByYLabel, false, false, MARGIN);
726 SpinsHBox.pack_start(XByYLabelVBox, false, false, MARGIN);
727 gtk_size_group_add_widget(_col2, (GtkWidget *) XByYLabelVBox.gobj());
729 /*#### Number of columns ####*/
731 NoOfColsLabel.set_text_with_mnemonic(_("_Columns:"));
732 NoOfColsLabel.set_mnemonic_widget(NoOfColsSpinner);
733 NoOfColsBox.pack_start(NoOfColsLabel, false, false, MARGIN);
735 NoOfColsSpinner.set_digits(0);
736 NoOfColsSpinner.set_increments(1, 0);
737 NoOfColsSpinner.set_range(1.0, 100.0);
738 NoOfColsSpinner.set_value(PerRow);
739 NoOfColsSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_row_spinbutton_changed));
740 tips.set_tip(NoOfColsSpinner, _("Number of columns"));
741 NoOfColsBox.pack_start(NoOfColsSpinner, false, false, MARGIN);
742 gtk_size_group_add_widget(_col3, (GtkWidget *) NoOfColsBox.gobj());
744 ColumnWidthButton.set_label(_("Equal _width"));
745 ColumnWidthButton.set_use_underline(true);
746 double AutoCol = prefs->getDouble("/dialogs/gridtiler/AutoColSize", 15);
747 if (AutoCol>0)
748 AutoColSize=true;
749 else
750 AutoColSize=false;
751 ColumnWidthButton.set_active(AutoColSize);
752 NoOfColsBox.pack_start(ColumnWidthButton, false, false, MARGIN);
754 tips.set_tip(ColumnWidthButton, _("If not set, each column has the width of the widest object in it"));
755 ColumnWidthButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::on_ColSize_checkbutton_changed));
758 {
759 /*#### Radio buttons to control horizontal alignment ####*/
761 HorizAlignLabel.set_label(_("Align:"));
762 HorizAlignVBox.pack_start(HorizAlignLabel, false, false, MARGIN);
764 HorizAlignHBox.pack_start(*(new Gtk::HBox()), true, true, 0); // centering strut
766 HorizLeftRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
767 HorizAlignGroup = HorizLeftRadioButton.get_group();
768 HorizAlignHBox.pack_start(HorizLeftRadioButton, false, false, 0);
770 HorizCentreRadioButton.set_group(HorizAlignGroup);
771 HorizCentreRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
772 HorizAlignHBox.pack_start(HorizCentreRadioButton, false, false, 0);
774 HorizRightRadioButton.set_group(HorizAlignGroup);
775 HorizRightRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
776 HorizAlignHBox.pack_start(HorizRightRadioButton, false, false, 0);
778 HorizAlignHBox.pack_start(*(new Gtk::HBox()), true, true, 0); // centering strut
780 HorizAlign = prefs->getInt("/dialogs/gridtiler/HorizAlign", 1);
781 if (HorizAlign == 0) {
782 HorizLeftRadioButton.set_active(TRUE);
783 }
784 else if (HorizAlign == 1) {
785 HorizCentreRadioButton.set_active(TRUE);
786 }
787 else if (HorizAlign == 2) {
788 HorizRightRadioButton.set_active(TRUE);
789 }
790 HorizAlignVBox.pack_start(HorizAlignHBox, false, false, MARGIN);
791 NoOfColsBox.pack_start(HorizAlignVBox, false, false, MARGIN);
792 }
794 SpinsHBox.pack_start(NoOfColsBox, false, false, MARGIN);
796 TileBox.pack_start(SpinsHBox, false, false, MARGIN);
798 {
799 /*#### Radio buttons to control spacing manually or to fit selection bbox ####*/
800 SpaceByBBoxRadioButton.set_label(_("_Fit into selection box"));
801 SpaceByBBoxRadioButton.set_use_underline (true);
802 SpaceByBBoxRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::Spacing_button_changed));
803 SpacingGroup = SpaceByBBoxRadioButton.get_group();
805 SpacingVBox.pack_start(SpaceByBBoxRadioButton, false, false, MARGIN);
807 SpaceManualRadioButton.set_label(_("_Set spacing:"));
808 SpaceManualRadioButton.set_use_underline (true);
809 SpaceManualRadioButton.set_group(SpacingGroup);
810 SpaceManualRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::Spacing_button_changed));
811 SpacingVBox.pack_start(SpaceManualRadioButton, false, false, MARGIN);
813 TileBox.pack_start(SpacingVBox, false, false, MARGIN);
814 }
816 {
817 /*#### Y Padding ####*/
819 GtkWidget *i = sp_icon_new (Inkscape::ICON_SIZE_MENU, "object-rows");
820 YPadBox.pack_start (*(Glib::wrap(i)), false, false, MARGIN);
822 YPadSpinner.set_digits(1);
823 YPadSpinner.set_increments(0.2, 0);
824 YPadSpinner.set_range(-10000, 10000);
825 double YPad = prefs->getDouble("/dialogs/gridtiler/YPad", 15);
826 YPadSpinner.set_value(YPad);
827 YPadBox.pack_start(YPadSpinner, true, true, MARGIN);
828 tips.set_tip(YPadSpinner, _("Vertical spacing between rows (px units)"));
829 YPadSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_ypad_spinbutton_changed));
830 gtk_size_group_add_widget(_col1, (GtkWidget *) YPadBox.gobj());
832 SizesHBox.pack_start(YPadBox, false, false, MARGIN);
833 }
835 {
836 Gtk::HBox *spacer = new Gtk::HBox;
837 SizesHBox.pack_start(*spacer, false, false, 0);
838 gtk_size_group_add_widget(_col2, (GtkWidget *) spacer->gobj());
839 }
841 {
842 /*#### X padding ####*/
844 GtkWidget *i = sp_icon_new (Inkscape::ICON_SIZE_MENU, "object-columns");
845 XPadBox.pack_start (*(Glib::wrap(i)), false, false, MARGIN);
847 XPadSpinner.set_digits(1);
848 XPadSpinner.set_increments(0.2, 0);
849 XPadSpinner.set_range(-10000, 10000);
850 double XPad = prefs->getDouble("/dialogs/gridtiler/XPad", 15);
851 XPadSpinner.set_value(XPad);
852 XPadBox.pack_start(XPadSpinner, true, true, MARGIN);
853 tips.set_tip(XPadSpinner, _("Horizontal spacing between columns (px units)"));
854 XPadSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_xpad_spinbutton_changed));
855 gtk_size_group_add_widget(_col3, (GtkWidget *) XPadBox.gobj());
857 SizesHBox.pack_start(XPadBox, false, false, MARGIN);
858 }
861 TileBox.pack_start(SizesHBox, false, false, MARGIN);
863 contents->pack_start(TileBox);
865 double SpacingType = prefs->getDouble("/dialogs/gridtiler/SpacingType", 15);
866 if (SpacingType>0) {
867 ManualSpacing=true;
868 } else {
869 ManualSpacing=false;
870 }
871 SpaceManualRadioButton.set_active(ManualSpacing);
872 SpaceByBBoxRadioButton.set_active(!ManualSpacing);
873 SizesHBox.set_sensitive (ManualSpacing);
875 //## The OK button
876 TileOkButton = addResponseButton(C_("Rows and columns dialog","_Arrange"), GTK_RESPONSE_APPLY);
877 TileOkButton->set_use_underline(true);
878 tips.set_tip((*TileOkButton), _("Arrange selected objects"));
880 show_all_children();
881 }
883 } //namespace Dialog
884 } //namespace UI
885 } //namespace Inkscape
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:fileencoding=utf-8:textwidth=99 :