align-and-distribute.cpp revision 52cad88e59adf0342e9294b9249b96bffe8fbe9e
/**
* @file
* Align and Distribute dialog - implementation.
*/
/* Authors:
* Bryce W. Harrington <bryce@bryceharrington.org>
* Aubanel MONNIER <aubi@libertysurf.fr>
* Frank Felfe <innerspace@iname.com>
* Lauris Kaplinski <lauris@kaplinski.com>
* Tim Dwyer <tgdwyer@gmail.com>
* Jon A. Cruz <jon@joncruz.org>
* Abhishek Sharma
*
* Copyright (C) 1999-2004, 2005 Authors
*
* Released under GNU GPL. Read the file 'COPYING' for more information.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "align-and-distribute.h"
#include "ui/widget/spinbutton.h"
#include "desktop-handles.h"
#include "unclump.h"
#include "document.h"
#include "enums.h"
#include "graphlayout.h"
#include "inkscape.h"
#include "macros.h"
#include "preferences.h"
#include "removeoverlap.h"
#include "selection.h"
#include "sp-flowtext.h"
#include "sp-item-transform.h"
#include "sp-text.h"
#include "text-editing.h"
#include "tools-switch.h"
#include "ui/icon-names.h"
#include "ui/tools/node-tool.h"
#include "ui/tool/multi-path-manipulator.h"
#include "util/glib-list-iterators.h"
#include "verbs.h"
#include "sp-root.h"
#include "document-undo.h"
#include "desktop.h"
namespace Inkscape {
namespace UI {
namespace Dialog {
/////////helper classes//////////////////////////////////
#if WITH_GTKMM_3_0
#else
#endif
{
#if WITH_GTKMM_3_0
#else
#endif
}
{
if (!selection) return;
{
case LAST:
break;
case FIRST:
break;
case BIGGEST:
break;
case SMALLEST:
break;
case PAGE:
break;
case DRAWING:
break;
case SELECTION:
b = selection->preferredBounds();
break;
default:
break;
};
if(focus)
b = focus->desktopPreferredBounds();
g_return_if_fail(b);
// Generate the move point from the selected bounding box
bool changed = false;
if (sel_as_group)
b = selection->preferredBounds();
//Move each item in the selected list separately
{
if (!sel_as_group)
b = (*it)->desktopPreferredBounds();
changed = true;
}
}
}
if (changed) {
_("Align"));
}
}
};
{
}
return i;
}
}
return -1;
}
BBoxSort::BBoxSort(SPItem *pItem, Geom::Rect const &bounds, Geom::Dim2 orientation, double kBegin, double kEnd) :
{
}
//NOTE : this copy ctor is called O(sort) when sorting the vector
//this is bad. The vector should be a vector of pointers.
//But I'll wait the bohem GC before doing that
{
}
{
}
class ActionDistribute : public Action {
public :
bool onInterSpace,
):
{}
private :
virtual void on_button_click() {
//Retreive selected objects
if (!desktop) return;
if (!selection) return;
//Check 2 or more selected objects
++second;
++it)
{
if (bbox) {
}
}
//sort bbox by anchors
// see comment in ActionAlign above
int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
bool changed = false;
if (_onInterSpace)
{
//overall bboxes span
//space eaten by bboxes
float span = 0;
for (unsigned int i = 0; i < len; i++)
{
}
//new distance between each bbox
++it )
{
changed = true;
}
}
}
else
{
//overall anchor span
//distance between anchors
for ( unsigned int i = 0; i < len ; i ++ )
{
//new anchor position
//Don't move if we are really close
//Compute translation
//translate
changed = true;
}
}
}
// restore compensation setting
if (changed) {
_("Distribute"));
}
}
bool _onInterSpace;
double _kBegin;
double _kEnd;
};
class ActionNode : public Action {
public :
{}
private :
bool _distribute;
virtual void on_button_click() {
if (!_dialog.getDesktop()) {
return;
}
if (!INK_IS_NODE_TOOL(event_context)) {
return;
}
if (_distribute) {
} else {
}
}
};
class ActionRemoveOverlaps : public Action {
private:
public:
{
#if WITH_GTKMM_3_0
#else
#endif
removeOverlapXGap.set_tooltip_text(_("Minimum horizontal gap (in px units) between bounding boxes"));
//TRANSLATORS: "H:" stands for horizontal gap
/* TRANSLATORS: Vertical gap */
#if WITH_GTKMM_3_0
#else
dialog.removeOverlap_table().attach(removeOverlapXGapLabel, column, column+1, row, row+1, Gtk::FILL, Gtk::FILL);
dialog.removeOverlap_table().attach(removeOverlapXGap, column+1, column+2, row, row+1, Gtk::FILL, Gtk::FILL);
dialog.removeOverlap_table().attach(removeOverlapYGapLabel, column+2, column+3, row, row+1, Gtk::FILL, Gtk::FILL);
dialog.removeOverlap_table().attach(removeOverlapYGap, column+3, column+4, row, row+1, Gtk::FILL, Gtk::FILL);
#endif
}
private :
virtual void on_button_click()
{
if (!_dialog.getDesktop()) return;
// see comment in ActionAlign above
int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
// xGap and yGap are the minimum space required between bounding rectangles.
// restore compensation setting
_("Remove overlaps"));
}
};
class ActionGraphLayout : public Action {
public:
{}
private :
virtual void on_button_click()
{
if (!_dialog.getDesktop()) return;
// see comment in ActionAlign above
int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
// restore compensation setting
_("Arrange connector network"));
}
};
class ActionExchangePositions : public Action {
public:
enum SortOrder {
None,
};
{};
private :
if (a == NULL) return false;
if (b == NULL) return true;
if (center) {
// First criteria: Sort according to the angle to the center point
// Second criteria: Sort according to the distance the center point
}
// Last criteria: Sort according to the z-coordinate
return (a->isSiblingOf(b));
}
virtual void on_button_click()
{
if (!desktop) return;
if (!selection) return;
//Check 2 or more selected objects
// see comment in ActionAlign above
int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
// sort the list
} else { // sorting by ZOrder is outomatically done by not setting the center
}
}
{
}
// restore compensation setting
_("Exchange Positions"));
}
};
// instantiae the private static member
class ActionUnclump : public Action {
public :
{}
private :
virtual void on_button_click()
{
if (!_dialog.getDesktop()) return;
// see comment in ActionAlign above
int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
// restore compensation setting
_("Unclump"));
}
};
class ActionRandomize : public Action {
public :
{}
private :
virtual void on_button_click()
{
if (!desktop) return;
if (!selection) return;
//Check 2 or more selected objects
if (!sel_bbox) {
return;
}
// This bbox is cached between calls to randomize, so that there's no growth nor shrink
// nor drift on sequential randomizations. Discard cache on global (or better active
// desktop's) selection_change signal.
if (!_dialog.randomize_bbox) {
}
// see comment in ActionAlign above
int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
++it)
{
Geom::OptRect item_box = !prefs_bbox ? (*it)->desktopVisualBounds() : (*it)->desktopGeometricBounds();
if (item_box) {
// find new center, staying within bbox
g_random_double_range (0, (*_dialog.randomize_bbox)[Geom::X].extent() - (*item_box)[Geom::X].extent());
g_random_double_range (0, (*_dialog.randomize_bbox)[Geom::Y].extent() - (*item_box)[Geom::Y].extent());
// displacement is the new center minus old:
}
}
// restore compensation setting
_("Randomize positions"));
}
};
struct Baselines
{
{}
};
{
}
class ActionBaseline : public Action {
public :
#if WITH_GTKMM_3_0
#else
#endif
{}
private :
bool _distribute;
virtual void on_button_click()
{
if (!desktop) return;
if (!selection) return;
//Check 2 or more selected objects
++it)
{
if (pt) {
}
}
}
//sort baselines
bool changed = false;
if (_distribute) {
changed = true;
}
if (changed) {
_("Distribute text baselines"));
}
} else {
++it)
{
if (pt) {
changed = true;
}
}
}
if (changed) {
_("Align text baselines"));
}
}
}
};
{
}
{
}
/////////////////////////////////////////////////////////
_alignFrame(_("Align")),
_distributeFrame(_("Distribute")),
_rearrangeFrame(_("Rearrange")),
_removeOverlapFrame(_("Remove overlaps")),
_nodesFrame(_("Nodes")),
#if WITH_GTKMM_3_0
_alignTable(),
_nodesTable(),
#else
#endif
_anchorLabel(_("Relative to: ")),
{
//Instanciate the align buttons
_("Align right edges of objects to the left edge of the anchor"),
0, 0);
_("Align left edges"),
0, 1);
_("Center on vertical axis"),
0, 2);
_("Align right sides"),
0, 3);
_("Align left edges of objects to the right edge of the anchor"),
0, 4);
_("Align bottom edges of objects to the top edge of the anchor"),
1, 0);
_("Align top edges"),
1, 1);
_("Center on horizontal axis"),
1, 2);
_("Align bottom edges"),
1, 3);
_("Align top edges of objects to the bottom edge of the anchor"),
1, 4);
//Baseline aligns
_("Align baseline anchors of texts horizontally"),
_("Align baselines of texts"),
//The distribute buttons
_("Make horizontal gaps between objects equal"),
_("Distribute left edges equidistantly"),
_("Distribute centers equidistantly horizontally"),
_("Distribute right edges equidistantly"),
_("Make vertical gaps between objects equal"),
_("Distribute top edges equidistantly"),
_("Distribute centers equidistantly vertically"),
_("Distribute bottom edges equidistantly"),
//Baseline distribs
_("Distribute baseline anchors of texts horizontally"),
_("Distribute baselines of texts vertically"),
// Rearrange
//Graph Layout
_("Nicely arrange selected connector network"),
0, 0);
_("Exchange positions of selected objects - selection order"),
0, 1);
_("Exchange positions of selected objects - stacking order"),
0, 2);
_("Exchange positions of selected objects - clockwise rotate"),
0, 3);
//Randomize & Unclump
_("Randomize centers in both dimensions"),
0, 4);
_("Unclump objects: try to equalize edge-to-edge distances"),
0, 5);
//Remove overlaps
_("Move objects as little as possible so that their bounding boxes do not overlap"),
0, 0);
//Node Mode buttons
// NOTE: "align nodes vertically" means "move nodes vertically until they align on a common
// _horizontal_ line". This is analogous to what the "align-vertical-center" icon means.
// There is no doubt some ambiguity. For this reason the descriptions are different.
_("Align selected nodes to a common horizontal line"),
0, Geom::X, false);
_("Align selected nodes to a common vertical line"),
1, Geom::Y, false);
_("Distribute selected nodes horizontally"),
2, Geom::X, true);
_("Distribute selected nodes vertically"),
3, Geom::Y, true);
//Rest of the widgetry
// Right align the buttons
// Notebook for individual transformations
//Connect to the global tool change signal
_toolChangeConn = INKSCAPE->signal_eventcontext_set.connect(sigc::hide<0>(sigc::bind(sigc::ptr_fun(&on_tool_changed), this)));
// Connect to the global selection change, to invalidate cached randomize_bbox
_selChangeConn = INKSCAPE->signal_selection_changed.connect(sigc::hide<0>(sigc::bind(sigc::ptr_fun(&on_selection_changed), this)));
_desktopChangeConn = _deskTrack.connectDesktopChanged( sigc::mem_fun(*this, &AlignAndDistribute::setDesktop) );
on_tool_changed (this); // set current mode
}
{
delete *it;
}
}
{
on_tool_changed (this);
}
}
void AlignAndDistribute::on_ref_change(){
//Make blink the master
}
void AlignAndDistribute::on_selgrp_toggled(){
//Make blink the master
}
{
//Act on widgets used in node mode
//Act on widgets used in selection mode
((_alignFrame).*(mSel))();
((_distributeFrame).*(mSel))();
((_rearrangeFrame).*(mSel))();
((_removeOverlapFrame).*(mSel))();
((_nodesFrame).*(mNode))();
}
{
new ActionAlign(
}
{
new ActionDistribute(
)
);
}
{
new ActionNode(
*this, orientation, distribute));
}
void AlignAndDistribute::addRemoveOverlapsButton(const Glib::ustring &id, const Glib::ustring tiptext,
{
new ActionRemoveOverlaps(
);
}
{
new ActionGraphLayout(
);
}
void AlignAndDistribute::addExchangePositionsButton(const Glib::ustring &id, const Glib::ustring tiptext,
{
);
}
void AlignAndDistribute::addExchangePositionsByZOrderButton(const Glib::ustring &id, const Glib::ustring tiptext,
{
);
}
void AlignAndDistribute::addExchangePositionsClockwiseButton(const Glib::ustring &id, const Glib::ustring tiptext,
{
);
}
{
new ActionUnclump(
);
}
{
new ActionRandomize(
);
}
#if WITH_GTKMM_3_0
#else
#endif
{
new ActionBaseline(
}
} // namespace Dialog
} // namespace UI
} // namespace Inkscape
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :