1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * A simple dialog for creating grid type arrangements of selected objects
4 *
5 * Authors:
6 * Bob Jamison ( based off trace dialog)
7 * John Cliff
8 * Other dudes from The Inkscape Organization
9 * Abhishek Sharma
10 * Declara Denis
11 *
12 * Copyright (C) 2004 Bob Jamison
13 * Copyright (C) 2004 John Cliff
14 *
15 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
16 */
17 //#define DEBUG_GRID_ARRANGE 1
18
19 #include "ui/dialog/grid-arrange-tab.h"
20 #include <glibmm/i18n.h>
21
22 #include <gtkmm/grid.h>
23 #include <gtkmm/sizegroup.h>
24
25 #include <2geom/transforms.h>
26
27 #include "verbs.h"
28 #include "preferences.h"
29 #include "inkscape.h"
30
31 #include "document.h"
32 #include "document-undo.h"
33 #include "desktop.h"
34 //#include "sp-item-transform.h" FIXME
35 #include "ui/dialog/tile.h" // for Inkscape::UI::Dialog::ArrangeDialog
36
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 */
sp_compare_x_position(SPItem * first,SPItem * second)44 static bool sp_compare_x_position(SPItem *first, SPItem *second)
45 {
46 using Geom::X;
47 using Geom::Y;
48
49 Geom::OptRect a = first->documentVisualBounds();
50 Geom::OptRect b = second->documentVisualBounds();
51
52 if ( !a || !b ) {
53 // FIXME?
54 return false;
55 }
56
57 double const a_height = a->dimensions()[Y];
58 double const b_height = b->dimensions()[Y];
59
60 bool a_in_b_vert = false;
61 if ((a->min()[Y] < b->min()[Y] + 0.1) && (a->min()[Y] > b->min()[Y] - b_height)) {
62 a_in_b_vert = true;
63 } else if ((b->min()[Y] < a->min()[Y] + 0.1) && (b->min()[Y] > a->min()[Y] - a_height)) {
64 a_in_b_vert = true;
65 } else if (b->min()[Y] == a->min()[Y]) {
66 a_in_b_vert = true;
67 } else {
68 a_in_b_vert = false;
69 }
70
71 if (!a_in_b_vert) { // a and b are not in the same row
72 return (a->min()[Y] < b->min()[Y]);
73 }
74 return (a->min()[X] < b->min()[X]);
75 }
76
77 /*
78 * Sort items by their y co-ordinates.
79 */
sp_compare_y_position(SPItem * first,SPItem * second)80 static bool sp_compare_y_position(SPItem *first, SPItem *second)
81 {
82 Geom::OptRect a = first->documentVisualBounds();
83 Geom::OptRect b = second->documentVisualBounds();
84
85 if ( !a || !b ) {
86 // FIXME?
87 return false;
88 }
89
90 if (a->min()[Geom::Y] > b->min()[Geom::Y]) {
91 return false;
92 }
93 if (a->min()[Geom::Y] < b->min()[Geom::Y]) {
94 return true;
95 }
96
97 return false;
98 }
99
100
101 namespace Inkscape {
102 namespace UI {
103 namespace Dialog {
104
105
106 //#########################################################################
107 //## E V E N T S
108 //#########################################################################
109
110 /*
111 *
112 * This arranges the selection in a grid pattern.
113 *
114 */
115
arrange()116 void GridArrangeTab::arrange()
117 {
118
119 int cnt,row_cnt,col_cnt,a,row,col;
120 double grid_left,grid_top,col_width,row_height,paddingx,paddingy,width, height, new_x, new_y;
121 double total_col_width,total_row_height;
122 col_width = 0;
123 row_height = 0;
124 total_col_width=0;
125 total_row_height=0;
126
127 // check for correct numbers in the row- and col-spinners
128 on_col_spinbutton_changed();
129 on_row_spinbutton_changed();
130
131 // set padding to manual values
132 paddingx = XPadding.getValue("px");
133 paddingy = YPadding.getValue("px");
134
135 std::vector<double> row_heights;
136 std::vector<double> col_widths;
137 std::vector<double> row_ys;
138 std::vector<double> col_xs;
139
140 int NoOfCols = NoOfColsSpinner.get_value_as_int();
141 int NoOfRows = NoOfRowsSpinner.get_value_as_int();
142
143 width = 0;
144 for (a=0;a<NoOfCols; a++){
145 col_widths.push_back(width);
146 }
147
148 height = 0;
149 for (a=0;a<NoOfRows; a++){
150 row_heights.push_back(height);
151 }
152 grid_left = 99999;
153 grid_top = 99999;
154
155 SPDesktop *desktop = Parent->getDesktop();
156 desktop->getDocument()->ensureUpToDate();
157
158 Inkscape::Selection *selection = desktop->getSelection();
159 std::vector<SPItem*> items;
160 if (selection) {
161 items.insert(items.end(), selection->items().begin(), selection->items().end());
162 }
163
164 for(auto item : items){
165 Geom::OptRect b = item->documentVisualBounds();
166 if (!b) {
167 continue;
168 }
169
170 width = b->dimensions()[Geom::X];
171 height = b->dimensions()[Geom::Y];
172
173 if (b->min()[Geom::X] < grid_left) {
174 grid_left = b->min()[Geom::X];
175 }
176 if (b->min()[Geom::Y] < grid_top) {
177 grid_top = b->min()[Geom::Y];
178 }
179 if (width > col_width) {
180 col_width = width;
181 }
182 if (height > row_height) {
183 row_height = height;
184 }
185 }
186
187
188 // require the sorting done before we can calculate row heights etc.
189
190 g_return_if_fail(selection);
191 std::vector<SPItem*> sorted(selection->items().begin(), selection->items().end());
192 sort(sorted.begin(),sorted.end(),sp_compare_y_position);
193 sort(sorted.begin(),sorted.end(),sp_compare_x_position);
194
195
196 // Calculate individual Row and Column sizes if necessary
197
198
199 cnt=0;
200 const std::vector<SPItem*> sizes(sorted);
201 for (auto item : sizes) {
202 Geom::OptRect b = item->documentVisualBounds();
203 if (b) {
204 width = b->dimensions()[Geom::X];
205 height = b->dimensions()[Geom::Y];
206 if (width > col_widths[(cnt % NoOfCols)]) {
207 col_widths[(cnt % NoOfCols)] = width;
208 }
209 if (height > row_heights[(cnt / NoOfCols)]) {
210 row_heights[(cnt / NoOfCols)] = height;
211 }
212 }
213
214 cnt++;
215 }
216
217
218 /// Make sure the top and left of the grid don't move by compensating for align values.
219 if (RowHeightButton.get_active()){
220 grid_top = grid_top - (((row_height - row_heights[0]) / 2)*(VertAlign));
221 }
222 if (ColumnWidthButton.get_active()){
223 grid_left = grid_left - (((col_width - col_widths[0]) /2)*(HorizAlign));
224 }
225
226 #ifdef DEBUG_GRID_ARRANGE
227 g_print("\n cx = %f cy= %f gridleft=%f",cx,cy,grid_left);
228 #endif
229
230 // Calculate total widths and heights, allowing for columns and rows non uniformly sized.
231
232 if (ColumnWidthButton.get_active()){
233 total_col_width = col_width * NoOfCols;
234 col_widths.clear();
235 for (a=0;a<NoOfCols; a++){
236 col_widths.push_back(col_width);
237 }
238 } else {
239 for (a = 0; a < (int)col_widths.size(); a++)
240 {
241 total_col_width += col_widths[a] ;
242 }
243 }
244
245 if (RowHeightButton.get_active()){
246 total_row_height = row_height * NoOfRows;
247 row_heights.clear();
248 for (a=0;a<NoOfRows; a++){
249 row_heights.push_back(row_height);
250 }
251 } else {
252 for (a = 0; a < (int)row_heights.size(); a++)
253 {
254 total_row_height += row_heights[a] ;
255 }
256 }
257
258
259 Geom::OptRect sel_bbox = selection->visualBounds();
260 // Fit to bbox, calculate padding between rows accordingly.
261 if ( sel_bbox && !SpaceManualRadioButton.get_active() ){
262 #ifdef DEBUG_GRID_ARRANGE
263 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));
264 #endif
265 paddingx = (sel_bbox->width() - total_col_width) / (NoOfCols -1);
266 paddingy = (sel_bbox->height() - total_row_height) / (NoOfRows -1);
267 }
268
269 /*
270 Horizontal align - Left = 0
271 Centre = 1
272 Right = 2
273
274 Vertical align - Top = 0
275 Middle = 1
276 Bottom = 2
277
278 X position is calculated by taking the grids left co-ord, adding the distance to the column,
279 then adding 1/2 the spacing multiplied by the align variable above,
280 Y position likewise, takes the top of the grid, adds the y to the current row then adds the padding in to align it.
281
282 */
283
284 // Calculate row and column x and y coords required to allow for columns and rows which are non uniformly sized.
285
286 for (a=0;a<NoOfCols; a++){
287 if (a<1) col_xs.push_back(0);
288 else col_xs.push_back(col_widths[a-1]+paddingx+col_xs[a-1]);
289 }
290
291
292 for (a=0;a<NoOfRows; a++){
293 if (a<1) row_ys.push_back(0);
294 else row_ys.push_back(row_heights[a-1]+paddingy+row_ys[a-1]);
295 }
296
297 cnt=0;
298 std::vector<SPItem*>::iterator it = sorted.begin();
299 for (row_cnt=0; ((it != sorted.end()) && (row_cnt<NoOfRows)); ++row_cnt) {
300
301 std::vector<SPItem *> current_row;
302 col_cnt = 0;
303 for(;it!=sorted.end()&&col_cnt<NoOfCols;++it) {
304 current_row.push_back(*it);
305 col_cnt++;
306 }
307
308 for (auto item:current_row) {
309 Geom::OptRect b = item->documentVisualBounds();
310 Geom::Point min;
311 if (b) {
312 width = b->dimensions()[Geom::X];
313 height = b->dimensions()[Geom::Y];
314 min = b->min();
315 } else {
316 width = height = 0;
317 min = Geom::Point(0, 0);
318 }
319
320 row = cnt / NoOfCols;
321 col = cnt % NoOfCols;
322
323 new_x = grid_left + (((col_widths[col] - width)/2)*HorizAlign) + col_xs[col];
324 new_y = grid_top + (((row_heights[row] - height)/2)*VertAlign) + row_ys[row];
325
326 Geom::Point move = Geom::Point(new_x, new_y) - min;
327 Geom::Affine const affine = Geom::Affine(Geom::Translate(move));
328 item->set_i2d_affine(item->i2doc_affine() * affine * item->document->doc2dt());
329 item->doWriteTransform(item->transform);
330 item->updateRepr();
331 cnt +=1;
332 }
333 }
334
335 DocumentUndo::done(desktop->getDocument(), SP_VERB_SELECTION_ARRANGE,
336 _("Arrange in a grid"));
337
338 }
339
340
341 //#########################################################################
342 //## E V E N T S
343 //#########################################################################
344
345 /**
346 * changed value in # of columns spinbox.
347 */
on_row_spinbutton_changed()348 void GridArrangeTab::on_row_spinbutton_changed()
349 {
350 SPDesktop *desktop = Parent->getDesktop();
351 Inkscape::Selection *selection = desktop ? desktop->selection : nullptr;
352 if (!selection) return;
353
354 int selcount = (int) boost::distance(selection->items());
355
356 double NoOfRows = ceil(selcount / NoOfColsSpinner.get_value());
357 NoOfRowsSpinner.set_value(NoOfRows);
358 }
359
360 /**
361 * changed value in # of rows spinbox.
362 */
on_col_spinbutton_changed()363 void GridArrangeTab::on_col_spinbutton_changed()
364 {
365 SPDesktop *desktop = Parent->getDesktop();
366 Inkscape::Selection *selection = desktop ? desktop->selection : nullptr;
367 if (!selection) return;
368
369 int selcount = (int) boost::distance(selection->items());
370
371 double NoOfCols = ceil(selcount / NoOfRowsSpinner.get_value());
372 NoOfColsSpinner.set_value(NoOfCols);
373 }
374
375 /**
376 * changed value in x padding spinbox.
377 */
on_xpad_spinbutton_changed()378 void GridArrangeTab::on_xpad_spinbutton_changed()
379 {
380 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
381 prefs->setDouble("/dialogs/gridtiler/XPad", XPadding.getValue("px"));
382
383 }
384
385 /**
386 * changed value in y padding spinbox.
387 */
on_ypad_spinbutton_changed()388 void GridArrangeTab::on_ypad_spinbutton_changed()
389 {
390 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
391 prefs->setDouble("/dialogs/gridtiler/YPad", YPadding.getValue("px"));
392 }
393
394
395 /**
396 * checked/unchecked autosize Rows button.
397 */
on_RowSize_checkbutton_changed()398 void GridArrangeTab::on_RowSize_checkbutton_changed()
399 {
400 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
401 if (RowHeightButton.get_active()) {
402 prefs->setDouble("/dialogs/gridtiler/AutoRowSize", 20);
403 } else {
404 prefs->setDouble("/dialogs/gridtiler/AutoRowSize", -20);
405 }
406 RowHeightBox.set_sensitive ( !RowHeightButton.get_active());
407 }
408
409 /**
410 * checked/unchecked autosize Rows button.
411 */
on_ColSize_checkbutton_changed()412 void GridArrangeTab::on_ColSize_checkbutton_changed()
413 {
414 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
415 if (ColumnWidthButton.get_active()) {
416 prefs->setDouble("/dialogs/gridtiler/AutoColSize", 20);
417 } else {
418 prefs->setDouble("/dialogs/gridtiler/AutoColSize", -20);
419 }
420 ColumnWidthBox.set_sensitive ( !ColumnWidthButton.get_active());
421 }
422
423 /**
424 * changed value in columns spinbox.
425 */
on_rowSize_spinbutton_changed()426 void GridArrangeTab::on_rowSize_spinbutton_changed()
427 {
428 // quit if run by the attr_changed listener
429 if (updating) {
430 return;
431 }
432
433 // in turn, prevent listener from responding
434 updating = true;
435 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
436 prefs->setDouble("/dialogs/gridtiler/RowHeight", RowHeightSpinner.get_value());
437 updating=false;
438
439 }
440
441 /**
442 * changed value in rows spinbox.
443 */
on_colSize_spinbutton_changed()444 void GridArrangeTab::on_colSize_spinbutton_changed()
445 {
446 // quit if run by the attr_changed listener
447 if (updating) {
448 return;
449 }
450
451 // in turn, prevent listener from responding
452 updating = true;
453 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
454 prefs->setDouble("/dialogs/gridtiler/ColWidth", ColumnWidthSpinner.get_value());
455 updating=false;
456
457 }
458
459 /**
460 * changed Radio button in Spacing group.
461 */
Spacing_button_changed()462 void GridArrangeTab::Spacing_button_changed()
463 {
464 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
465 if (SpaceManualRadioButton.get_active()) {
466 prefs->setDouble("/dialogs/gridtiler/SpacingType", 20);
467 } else {
468 prefs->setDouble("/dialogs/gridtiler/SpacingType", -20);
469 }
470
471 XPadding.set_sensitive ( SpaceManualRadioButton.get_active());
472 YPadding.set_sensitive ( SpaceManualRadioButton.get_active());
473 }
474
475 /**
476 * changed Anchor selection widget.
477 */
Align_changed()478 void GridArrangeTab::Align_changed()
479 {
480 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
481 VertAlign = AlignmentSelector.getVerticalAlignment();
482 prefs->setInt("/dialogs/gridtiler/VertAlign", VertAlign);
483 HorizAlign = AlignmentSelector.getHorizontalAlignment();
484 prefs->setInt("/dialogs/gridtiler/HorizAlign", HorizAlign);
485 }
486
487 /**
488 * Desktop selection changed
489 */
updateSelection()490 void GridArrangeTab::updateSelection()
491 {
492 // quit if run by the attr_changed listener
493 if (updating) {
494 return;
495 }
496
497 // in turn, prevent listener from responding
498 updating = true;
499 SPDesktop *desktop = Parent->getDesktop();
500 Inkscape::Selection *selection = desktop ? desktop->selection : nullptr;
501 std::vector<SPItem*> items;
502 if (selection) {
503 items.insert(items.end(), selection->items().begin(), selection->items().end());
504 }
505
506 if (!items.empty()) {
507 int selcount = items.size();
508
509 if (NoOfColsSpinner.get_value() > 1 && NoOfRowsSpinner.get_value() > 1){
510 // Update the number of rows assuming number of columns wanted remains same.
511 double NoOfRows = ceil(selcount / NoOfColsSpinner.get_value());
512 NoOfRowsSpinner.set_value(NoOfRows);
513
514 // if the selection has less than the number set for one row, reduce it appropriately
515 if (selcount < NoOfColsSpinner.get_value()) {
516 double NoOfCols = ceil(selcount / NoOfRowsSpinner.get_value());
517 NoOfColsSpinner.set_value(NoOfCols);
518 }
519 } else {
520 double PerRow = ceil(sqrt(selcount));
521 double PerCol = ceil(sqrt(selcount));
522 NoOfRowsSpinner.set_value(PerRow);
523 NoOfColsSpinner.set_value(PerCol);
524 }
525 }
526
527 updating = false;
528 }
529
setDesktop(SPDesktop * desktop)530 void GridArrangeTab::setDesktop(SPDesktop *desktop)
531 {
532 _selection_changed_connection.disconnect();
533
534 if (desktop) {
535 updateSelection();
536
537 _selection_changed_connection = INKSCAPE.signal_selection_changed.connect(
538 sigc::hide<0>(sigc::mem_fun(*this, &GridArrangeTab::updateSelection)));
539 }
540 }
541
542
543 //#########################################################################
544 //## C O N S T R U C T O R / D E S T R U C T O R
545 //#########################################################################
546 /**
547 * Constructor
548 */
GridArrangeTab(ArrangeDialog * parent)549 GridArrangeTab::GridArrangeTab(ArrangeDialog *parent)
550 : Parent(parent),
551 XPadding(_("X:"), _("Horizontal spacing between columns."), UNIT_TYPE_LINEAR, "", "object-columns", &PaddingUnitMenu),
552 YPadding(_("Y:"), _("Vertical spacing between rows."), XPadding, "", "object-rows"),
553 PaddingTable(Gtk::manage(new Gtk::Grid()))
554 {
555 // bool used by spin button callbacks to stop loops where they change each other.
556 updating = false;
557 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
558
559 auto _col1 = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL);
560 auto _col2 = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL);
561 auto _col3 = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL);
562
563 Gtk::Box *contents = this;
564
565 #define MARGIN 2
566
567 //##Set up the panel
568
569 NoOfRowsLabel.set_text_with_mnemonic(_("_Rows:"));
570 NoOfRowsLabel.set_mnemonic_widget(NoOfRowsSpinner);
571 NoOfRowsBox.set_orientation(Gtk::ORIENTATION_VERTICAL);
572 NoOfRowsBox.pack_start(NoOfRowsLabel, false, false, MARGIN);
573
574 NoOfRowsSpinner.set_digits(0);
575 NoOfRowsSpinner.set_increments(1, 0);
576 NoOfRowsSpinner.set_range(1.0, 10000.0);
577 NoOfRowsSpinner.signal_changed().connect(sigc::mem_fun(*this, &GridArrangeTab::on_col_spinbutton_changed));
578 NoOfRowsSpinner.set_tooltip_text(_("Number of rows"));
579 NoOfRowsBox.pack_start(NoOfRowsSpinner, false, false, MARGIN);
580 _col1->add_widget(NoOfRowsBox);
581
582 RowHeightButton.set_label(_("Equal _height"));
583 RowHeightButton.set_use_underline(true);
584 double AutoRow = prefs->getDouble("/dialogs/gridtiler/AutoRowSize", 15);
585 if (AutoRow>0)
586 AutoRowSize=true;
587 else
588 AutoRowSize=false;
589 RowHeightButton.set_active(AutoRowSize);
590
591 NoOfRowsBox.pack_start(RowHeightButton, false, false, MARGIN);
592
593 RowHeightButton.set_tooltip_text(_("If not set, each row has the height of the tallest object in it"));
594 RowHeightButton.signal_toggled().connect(sigc::mem_fun(*this, &GridArrangeTab::on_RowSize_checkbutton_changed));
595
596 SpinsHBox.pack_start(NoOfRowsBox, false, false, MARGIN);
597
598
599 /*#### Label for X ####*/
600 padXByYLabel.set_label(" ");
601 XByYLabelVBox.set_orientation(Gtk::ORIENTATION_VERTICAL);
602 XByYLabelVBox.pack_start(padXByYLabel, false, false, MARGIN);
603 XByYLabel.set_markup(" × ");
604 XByYLabelVBox.pack_start(XByYLabel, false, false, MARGIN);
605 SpinsHBox.pack_start(XByYLabelVBox, false, false, MARGIN);
606 _col2->add_widget(XByYLabelVBox);
607
608 /*#### Number of columns ####*/
609
610 NoOfColsLabel.set_text_with_mnemonic(_("_Columns:"));
611 NoOfColsLabel.set_mnemonic_widget(NoOfColsSpinner);
612 NoOfColsBox.set_orientation(Gtk::ORIENTATION_VERTICAL);
613 NoOfColsBox.pack_start(NoOfColsLabel, false, false, MARGIN);
614
615 NoOfColsSpinner.set_digits(0);
616 NoOfColsSpinner.set_increments(1, 0);
617 NoOfColsSpinner.set_range(1.0, 10000.0);
618 NoOfColsSpinner.signal_changed().connect(sigc::mem_fun(*this, &GridArrangeTab::on_row_spinbutton_changed));
619 NoOfColsSpinner.set_tooltip_text(_("Number of columns"));
620 NoOfColsBox.pack_start(NoOfColsSpinner, false, false, MARGIN);
621 _col3->add_widget(NoOfColsBox);
622
623 ColumnWidthButton.set_label(_("Equal _width"));
624 ColumnWidthButton.set_use_underline(true);
625 double AutoCol = prefs->getDouble("/dialogs/gridtiler/AutoColSize", 15);
626 if (AutoCol>0)
627 AutoColSize=true;
628 else
629 AutoColSize=false;
630 ColumnWidthButton.set_active(AutoColSize);
631 NoOfColsBox.pack_start(ColumnWidthButton, false, false, MARGIN);
632
633 ColumnWidthButton.set_tooltip_text(_("If not set, each column has the width of the widest object in it"));
634 ColumnWidthButton.signal_toggled().connect(sigc::mem_fun(*this, &GridArrangeTab::on_ColSize_checkbutton_changed));
635
636 SpinsHBox.pack_start(NoOfColsBox, false, false, MARGIN);
637
638 TileBox.set_orientation(Gtk::ORIENTATION_VERTICAL);
639 TileBox.pack_start(SpinsHBox, false, false, MARGIN);
640
641 VertAlign = prefs->getInt("/dialogs/gridtiler/VertAlign", 1);
642 HorizAlign = prefs->getInt("/dialogs/gridtiler/HorizAlign", 1);
643
644 // Anchor selection widget
645 AlignLabel.set_label(_("Alignment:"));
646 AlignLabel.set_halign(Gtk::ALIGN_START);
647 AlignLabel.set_valign(Gtk::ALIGN_CENTER);
648 AlignmentSelector.setAlignment(HorizAlign, VertAlign);
649 AlignmentSelector.on_selectionChanged().connect(sigc::mem_fun(*this, &GridArrangeTab::Align_changed));
650 TileBox.pack_start(AlignLabel, false, false, MARGIN);
651 TileBox.pack_start(AlignmentSelector, true, false, MARGIN);
652
653 {
654 /*#### Radio buttons to control spacing manually or to fit selection bbox ####*/
655 SpaceByBBoxRadioButton.set_label(_("_Fit into selection box"));
656 SpaceByBBoxRadioButton.set_use_underline (true);
657 SpaceByBBoxRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &GridArrangeTab::Spacing_button_changed));
658 SpacingGroup = SpaceByBBoxRadioButton.get_group();
659
660 SpacingVBox.pack_start(SpaceByBBoxRadioButton, false, false, MARGIN);
661
662 SpaceManualRadioButton.set_label(_("_Set spacing:"));
663 SpaceManualRadioButton.set_use_underline (true);
664 SpaceManualRadioButton.set_group(SpacingGroup);
665 SpaceManualRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &GridArrangeTab::Spacing_button_changed));
666 SpacingVBox.pack_start(SpaceManualRadioButton, false, false, MARGIN);
667
668 TileBox.pack_start(SpacingVBox, false, false, MARGIN);
669 }
670
671 {
672 /*#### Padding ####*/
673 PaddingUnitMenu.setUnitType(UNIT_TYPE_LINEAR);
674 PaddingUnitMenu.setUnit("px");
675
676 YPadding.setDigits(5);
677 YPadding.setIncrements(0.2, 0);
678 YPadding.setRange(-10000, 10000);
679 double yPad = prefs->getDouble("/dialogs/gridtiler/YPad", 15);
680 YPadding.setValue(yPad, "px");
681 YPadding.signal_value_changed().connect(sigc::mem_fun(*this, &GridArrangeTab::on_ypad_spinbutton_changed));
682
683 XPadding.setDigits(5);
684 XPadding.setIncrements(0.2, 0);
685 XPadding.setRange(-10000, 10000);
686 double xPad = prefs->getDouble("/dialogs/gridtiler/XPad", 15);
687 XPadding.setValue(xPad, "px");
688
689 XPadding.signal_value_changed().connect(sigc::mem_fun(*this, &GridArrangeTab::on_xpad_spinbutton_changed));
690 }
691
692 PaddingTable->set_border_width(MARGIN);
693 PaddingTable->set_row_spacing(MARGIN);
694 PaddingTable->set_column_spacing(MARGIN);
695 PaddingTable->attach(XPadding, 0, 0, 1, 1);
696 PaddingTable->attach(PaddingUnitMenu, 1, 0, 1, 1);
697 PaddingTable->attach(YPadding, 0, 1, 1, 1);
698
699 TileBox.pack_start(*PaddingTable, false, false, MARGIN);
700
701 contents->set_border_width(4);
702 contents->pack_start(TileBox);
703
704 double SpacingType = prefs->getDouble("/dialogs/gridtiler/SpacingType", 15);
705 if (SpacingType>0) {
706 ManualSpacing=true;
707 } else {
708 ManualSpacing=false;
709 }
710 SpaceManualRadioButton.set_active(ManualSpacing);
711 SpaceByBBoxRadioButton.set_active(!ManualSpacing);
712 XPadding.set_sensitive (ManualSpacing);
713 YPadding.set_sensitive (ManualSpacing);
714
715 show_all_children();
716 }
717
718 } //namespace Dialog
719 } //namespace UI
720 } //namespace Inkscape
721
722 /*
723 Local Variables:
724 mode:c++
725 c-file-style:"stroustrup"
726 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
727 indent-tabs-mode:nil
728 fill-column:99
729 End:
730 */
731 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
732