tile.cpp revision e463a5a70da38ff6014e7da6f70fcb7546186ebb
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof * A simple dialog for creating grid type arrangements of selected objects
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof * Bob Jamison ( based off trace dialog)
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof * John Cliff
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof * Other dudes from The Inkscape Organization
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof * Abhishek Sharma
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof * Copyright (C) 2004 Bob Jamison
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof * Copyright (C) 2004 John Cliff
60db347ea19e568bd09c35a0248a9f78d7931ca0Jabiertxof * Released under GNU GPL, read the file 'COPYING' for more information
60db347ea19e568bd09c35a0248a9f78d7931ca0Jabiertxof//#define DEBUG_GRID_ARRANGE 1
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof * Sort items by their x co-ordinates, taking account of y (keeps rows intact)
163aa8601274792f2afe8e24a061fed96b7a6599Jabiertxof * <0 *elem1 goes before *elem2
163aa8601274792f2afe8e24a061fed96b7a6599Jabiertxof * 0 *elem1 == *elem2
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof * >0 *elem1 goes after *elem2
163aa8601274792f2afe8e24a061fed96b7a6599Jabiertxofsp_compare_x_position(SPItem *first, SPItem *second)
60db347ea19e568bd09c35a0248a9f78d7931ca0Jabiertxof Geom::OptRect b = second->documentVisualBounds();
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof if ( !a || !b ) {
163aa8601274792f2afe8e24a061fed96b7a6599Jabiertxof if ((a->min()[Y] < b->min()[Y] + 0.1) && (a->min()[Y] > b->min()[Y] - b_height)) {
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof } else if ((b->min()[Y] < a->min()[Y] + 0.1) && (b->min()[Y] > a->min()[Y] - a_height)) {
2ce2003d5713154f99c32af18fb904a1af109031Jabiertxof * Sort items by their y co-ordinates.
60db347ea19e568bd09c35a0248a9f78d7931ca0Jabiertxofsp_compare_y_position(SPItem *first, SPItem *second)
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof Geom::OptRect b = second->documentVisualBounds();
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof if ( !a || !b ) {
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof//#########################################################################
163aa8601274792f2afe8e24a061fed96b7a6599Jabiertxof//## E V E N T S
163aa8601274792f2afe8e24a061fed96b7a6599Jabiertxof//#########################################################################
163aa8601274792f2afe8e24a061fed96b7a6599Jabiertxof * This arranges the selection in a grid pattern.
721286d6ce40a27fcd8b9483667a43ed09023b17Jabiertxof double grid_left,grid_top,col_width,row_height,paddingx,paddingy,width, height, new_x, new_y,cx,cy;
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof // check for correct numbers in the row- and col-spinners
60db347ea19e568bd09c35a0248a9f78d7931ca0Jabiertxof // set padding to manual values
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof int NoOfCols = NoOfColsSpinner.get_value_as_int();
163aa8601274792f2afe8e24a061fed96b7a6599Jabiertxof int NoOfRows = NoOfRowsSpinner.get_value_as_int();
60db347ea19e568bd09c35a0248a9f78d7931ca0Jabiertxof for (a=0;a<NoOfCols; a++){
0b159142b0b5738b20883b411fe8233657cf8b4fJabiertxof for (a=0;a<NoOfRows; a++){
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof Inkscape::Selection *selection = sp_desktop_selection (desktop);
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof const GSList *items = selection ? selection->itemList() : 0;
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof // require the sorting done before we can calculate row heights etc.
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof rev = g_slist_sort(rev, (GCompareFunc) sp_compare_y_position);
163aa8601274792f2afe8e24a061fed96b7a6599Jabiertxof sorted = g_slist_sort(rev, (GCompareFunc) sp_compare_x_position);
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof // Calculate individual Row and Column sizes if necessary
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof /// Make sure the top and left of the grid dont move by compensating for align values.
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof grid_top = grid_top - (((row_height - row_heights[0]) / 2)*(VertAlign));
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof grid_left = grid_left - (((col_width - col_widths[0]) /2)*(HorizAlign));
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof g_print("\n cx = %f cy= %f gridleft=%f",cx,cy,grid_left);
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof // Calculate total widths and heights, allowing for columns and rows non uniformly sized.
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof for (a=0;a<NoOfCols; a++){
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof for (a=0;a<NoOfRows; a++){
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof Geom::OptRect sel_bbox = selection->visualBounds();
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof // Fit to bbox, calculate padding between rows accordingly.
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof if ( sel_bbox && !SpaceManualRadioButton.get_active() ){
60db347ea19e568bd09c35a0248a9f78d7931ca0Jabiertxofg_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));
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof paddingx = (sel_bbox->width() - total_col_width) / (NoOfCols -1);
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof paddingy = (sel_bbox->height() - total_row_height) / (NoOfRows -1);
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof Horizontal align - Left = 0
163aa8601274792f2afe8e24a061fed96b7a6599Jabiertxof Vertical align - Top = 0
163aa8601274792f2afe8e24a061fed96b7a6599Jabiertxof X position is calculated by taking the grids left co-ord, adding the distance to the column,
163aa8601274792f2afe8e24a061fed96b7a6599Jabiertxof then adding 1/2 the spacing multiplied by the align variable above,
163aa8601274792f2afe8e24a061fed96b7a6599Jabiertxof Y position likewise, takes the top of the grid, adds the y to the current row then adds the padding in to align it.
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof // Calculate row and column x and y coords required to allow for columns and rows which are non uniformly sized.
163aa8601274792f2afe8e24a061fed96b7a6599Jabiertxof for (a=0;a<NoOfCols; a++){
163aa8601274792f2afe8e24a061fed96b7a6599Jabiertxof else col_xs.push_back(col_widths[a-1]+paddingx+col_xs[a-1]);
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof for (a=0;a<NoOfRows; a++){
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof else row_ys.push_back(row_heights[a-1]+paddingy+row_ys[a-1]);
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof for (row_cnt=0; ((sorted != NULL) && (row_cnt<NoOfRows)); row_cnt++) {
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof for (col_cnt = 0; ((sorted != NULL) && (col_cnt<NoOfCols)); col_cnt++) {
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof current_row = g_slist_append (current_row, sorted->data);
163aa8601274792f2afe8e24a061fed96b7a6599Jabiertxof for (; current_row != NULL; current_row = current_row->next) {
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof new_x = grid_left + (((col_widths[col] - width)/2)*HorizAlign) + col_xs[col];
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof new_y = grid_top + (((row_heights[row] - height)/2)*VertAlign) + row_ys[row];
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof // signs are inverted between x and y due to y inversion
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof Geom::Point move = Geom::Point(new_x - min[Geom::X], min[Geom::Y] - new_y);
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof Geom::Affine const affine = Geom::Affine(Geom::Translate(move));
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof item->set_i2d_affine(item->i2dt_affine() * affine);
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof item->doWriteTransform(repr, item->transform, NULL);
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_SELECTION_GRIDTILE,
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof _("Arrange in a grid"));
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof//#########################################################################
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof//## E V E N T S
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof//#########################################################################
163aa8601274792f2afe8e24a061fed96b7a6599Jabiertxof * changed value in # of columns spinbox.
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof // quit if run by the attr_changed listener
dbe3e7dacd7d4490ef69cc21314dc718b4706ae5jabiertxof // in turn, prevent listener from responding
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof Inkscape::Selection *selection = desktop ? desktop->selection : 0;
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof double PerCol = ceil(selcount / NoOfColsSpinner.get_value());
163aa8601274792f2afe8e24a061fed96b7a6599Jabiertxof Inkscape::Preferences *prefs = Inkscape::Preferences::get();
163aa8601274792f2afe8e24a061fed96b7a6599Jabiertxof prefs->setDouble("/dialogs/gridtiler/NoOfCols", NoOfColsSpinner.get_value());
163aa8601274792f2afe8e24a061fed96b7a6599Jabiertxof * changed value in # of rows spinbox.
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof // quit if run by the attr_changed listener
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof // in turn, prevent listener from responding
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof Inkscape::Selection *selection = desktop ? desktop->selection : 0;
60db347ea19e568bd09c35a0248a9f78d7931ca0Jabiertxof double PerRow = ceil(selcount / NoOfRowsSpinner.get_value());
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof Inkscape::Preferences *prefs = Inkscape::Preferences::get();
ccbee7d45f91658e83a602a9a28ee26162a261f9Jabiertxof prefs->setDouble("/dialogs/gridtiler/NoOfCols", PerRow);
60db347ea19e568bd09c35a0248a9f78d7931ca0Jabiertxof * changed value in x padding spinbox.
60db347ea19e568bd09c35a0248a9f78d7931ca0Jabiertxof Inkscape::Preferences *prefs = Inkscape::Preferences::get();
60db347ea19e568bd09c35a0248a9f78d7931ca0Jabiertxof prefs->setDouble("/dialogs/gridtiler/XPad", XPadding.getValue("px"));
60db347ea19e568bd09c35a0248a9f78d7931ca0Jabiertxof * changed value in y padding spinbox.
if (updating) {
updating = true;
updating=false;
if (updating) {
updating = true;
updating=false;
VertAlign = 0;
HorizAlign = 0;
if (updating) {
updating = true;
if (items) {
updating = false;
static void updateSelectionCallback(Inkscape::Application */*inkscape*/, Inkscape::Selection */*selection*/, TileDialog *dlg)
XPadding(_("X:"), _("Horizontal spacing between columns."), UNIT_TYPE_LINEAR, "", "object-columns"),
updating = false;
g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (updateSelectionCallback), this);
#ifdef DEBUG_GRID_ARRANGE
NoOfRowsSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_col_spinbutton_changed));
if (AutoRow>0)
AutoRowSize=true;
AutoRowSize=false;
tips.set_tip(RowHeightButton, _("If not set, each row has the height of the tallest object in it"));
RowHeightButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::on_RowSize_checkbutton_changed));
VertCentreRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed));
if (VertAlign == 0) {
NoOfColsSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_row_spinbutton_changed));
if (AutoCol>0)
AutoColSize=true;
AutoColSize=false;
tips.set_tip(ColumnWidthButton, _("If not set, each column has the width of the widest object in it"));
ColumnWidthButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::on_ColSize_checkbutton_changed));
HorizLeftRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
HorizCentreRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
HorizRightRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed));
if (HorizAlign == 0) {
SpaceByBBoxRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::Spacing_button_changed));
SpaceManualRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::Spacing_button_changed));
YPadding.signal_value_changed().connect(sigc::mem_fun(*this, &TileDialog::on_ypad_spinbutton_changed));
XPadding.signal_value_changed().connect(sigc::mem_fun(*this, &TileDialog::on_xpad_spinbutton_changed));
if (SpacingType>0) {
ManualSpacing=true;
ManualSpacing=false;