align-and-distribute.cpp revision 963f23115db07f460bdd862b957f8bd9dba88b9b
/**
* \brief Align and Distribute dialog
*
* 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>
*
* 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 "verbs.h"
#include "removeoverlap/removeoverlap.h"
#include "graphlayout/graphlayout.h"
#include <gtkmm/spinbutton.h>
#include "util/glib-list-iterators.h"
#include "inkscape.h"
#include "document.h"
#include "selection.h"
#include "desktop-handles.h"
#include "macros.h"
#include "sp-item-transform.h"
#include "prefs-utils.h"
#include "enums.h"
#include "sp-text.h"
#include "sp-flowtext.h"
#include "text-editing.h"
#include "node-context.h" //For access to ShapeEditor
#include "shape-editor.h" //For node align/distribute methods
#include "tools-switch.h"
#include "align-and-distribute.h"
namespace Inkscape {
namespace UI {
namespace Dialog {
/////////helper classes//////////////////////////////////
class Action {
public :
{
}
virtual ~Action(){}
private :
virtual void on_button_click(){}
};
class ActionAlign : public Action {
public :
struct Coeffs {
};
{}
private :
virtual void on_button_click() {
//Retreive selected objects
if (!desktop) return;
if (!selection) return;
switch (target)
{
case AlignAndDistribute::LAST:
case AlignAndDistribute::FIRST:
case AlignAndDistribute::BIGGEST:
case AlignAndDistribute::SMALLEST:
{
//Check 2 or more selected objects
++second;
return;
//Find the master (anchor on which the other objects are aligned)
(a.mx0 != 0.0) ||
(a.mx1 != 0.0) )
);
//remove the master from the selection
//Compute the anchor point
if (b) {
} else {
return;
}
break;
}
case AlignAndDistribute::PAGE:
break;
case AlignAndDistribute::DRAWING:
{
if (b) {
} else {
return;
}
break;
}
case AlignAndDistribute::SELECTION:
{
if (b) {
} else {
return;
}
break;
}
default:
break;
}; // end of switch
// Top hack: temporarily set clone compensation to unmoved, so that we can align/distribute
// clones with their original (and the move of the original does not disturb the
// clones). The only problem with this is that if there are outside-of-selection clones of
// a selected original, they will be unmoved too, possibly contrary to user's
// expecation. However this is a minor point compared to making align/distribute always
// work as expected, and "unmoved" is the default option anyway.
int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
bool changed = false;
//Move each item in the selected list
it++)
{
if (b) {
changed = true;
}
}
}
// restore compensation setting
if (changed) {
_("Align"));
}
}
};
{1., 0., 0., 0., 0., 1., 0., 0.},
{1., 0., 0., 0., 1., 0., 0., 0.},
{.5, .5, 0., 0., .5, .5, 0., 0.},
{0., 1., 0., 0., 0., 1., 0., 0.},
{0., 1., 0., 0., 1., 0., 0., 0.},
{0., 0., 0., 1., 0., 0., 1., 0.},
{0., 0., 0., 1., 0., 0., 0., 1.},
{0., 0., .5, .5, 0., 0., .5, .5},
{0., 0., 1., 0., 0., 0., 1., 0.},
{0., 0., 1., 0., 0., 0., 0., 1.}
};
struct BBoxSort
{
float anchor;
{
}
//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_get_int_attribute("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 (!SP_ACTIVE_DESKTOP) return;
if (!SP_IS_NODE_CONTEXT (event_context)) return ;
if (_distribute)
else
}
};
class ActionRemoveOverlaps : public Action {
private:
public:
{
_("Minimum horizontal gap (in px units) between bounding boxes"));
/* TRANSLATORS: Horizontal gap */
_("Minimum vertical gap (in px units) between bounding boxes"));
/* TRANSLATORS: Vertical gap */
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);
}
private :
virtual void on_button_click()
{
if (!SP_ACTIVE_DESKTOP) return;
// see comment in ActionAlign above
int saved_compensation = prefs_get_int_attribute("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 (!SP_ACTIVE_DESKTOP) return;
// see comment in ActionAlign above
int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
// restore compensation setting
_("Arrange connector network"));
}
};
class ActionUnclump : public Action {
public :
{}
private :
virtual void on_button_click()
{
if (!SP_ACTIVE_DESKTOP) return;
// see comment in ActionAlign above
int saved_compensation = prefs_get_int_attribute("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_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
++it)
{
if (item_box) {
// find new center, staying within bbox
// displacement is the new center minus old:
}
}
// restore compensation setting
_("Randomize positions"));
}
};
struct Baselines
{
{}
};
{
}
class ActionBaseline : public Action {
public :
{}
private :
bool _distribute;
virtual void on_button_click()
{
if (!desktop) return;
if (!selection) return;
//Check 2 or more selected objects
++it)
{
}
}
//sort baselines
bool changed = false;
if (_distribute) {
changed = true;
}
if (changed) {
_("Distribute text baselines"));
}
} else {
++it)
{
changed = true;
}
}
if (changed) {
_("Align text baselines"));
}
}
}
};
void on_tool_changed(Inkscape::Application *inkscape, SPEventContext *context, AlignAndDistribute *daad)
{
}
void on_selection_changed(Inkscape::Application *inkscape, Inkscape::Selection *selection, AlignAndDistribute *daad)
{
}
/////////////////////////////////////////////////////////
_alignFrame(_("Align")),
_distributeFrame(_("Distribute")),
_removeOverlapFrame(_("Remove overlaps")),
_graphLayoutFrame(_("Connector network layout")),
_nodesFrame(_("Nodes")),
_anchorLabel(_("Relative to: "))
{
//Instanciate the align buttons
addAlignButton("al_left_out",
_("Align right sides of objects to left side of anchor"),
0, 0);
addAlignButton("al_left_in",
_("Align left sides"),
0, 1);
addAlignButton("al_center_hor",
_("Center on vertical axis"),
0, 2);
addAlignButton("al_right_in",
_("Align right sides"),
0, 3);
addAlignButton("al_right_out",
_("Align left sides of objects to right side of anchor"),
0, 4);
addAlignButton("al_top_out",
_("Align bottoms of objects to top of anchor"),
1, 0);
addAlignButton("al_top_in",
_("Align tops"),
1, 1);
addAlignButton("al_center_ver",
_("Center on horizontal axis"),
1, 2);
addAlignButton("al_bottom_in",
_("Align bottoms"),
1, 3);
addAlignButton("al_bottom_out",
_("Align tops of objects to bottom of anchor"),
1, 4);
//Baseline aligns
addBaselineButton("al_baselines_vert",
_("Align baseline anchors of texts vertically"),
addBaselineButton("al_baselines_hor",
_("Align baseline anchors of texts horizontally"),
//The distribute buttons
addDistributeButton("distribute_hdist",
_("Make horizontal gaps between objects equal"),
addDistributeButton("distribute_left",
_("Distribute left sides equidistantly"),
addDistributeButton("distribute_hcentre",
_("Distribute centers equidistantly horizontally"),
addDistributeButton("distribute_right",
_("Distribute right sides equidistantly"),
addDistributeButton("distribute_vdist",
_("Make vertical gaps between objects equal"),
addDistributeButton("distribute_top",
_("Distribute tops equidistantly"),
addDistributeButton("distribute_vcentre",
_("Distribute centers equidistantly vertically"),
addDistributeButton("distribute_bottom",
_("Distribute bottoms equidistantly"),
//Baseline distribs
addBaselineButton("distribute_baselines_hor",
_("Distribute baseline anchors of texts horizontally"),
addBaselineButton("distribute_baselines_vert",
_("Distribute baseline anchors of texts vertically"),
//Randomize & Unclump
addRandomizeButton("distribute_randomize",
_("Randomize centers in both dimensions"),
2, 2);
addUnclumpButton("unclump",
_("Unclump objects: try to equalize edge-to-edge distances"),
2, 4);
//Remove overlaps
addRemoveOverlapsButton("remove_overlaps",
_("Move objects as little as possible so that their bounding boxes do not overlap"),
0, 0);
//Graph Layout
addGraphLayoutButton("graph_layout",
_("Nicely arrange selected connector network"),
0, 0);
//Node Mode buttons
addNodeButton("node_halign",
_("Align selected nodes horizontally"),
0, NR::X, false);
addNodeButton("node_valign",
_("Align selected nodes vertically"),
1, NR::Y, false);
addNodeButton("node_hdistribute",
_("Distribute selected nodes horizontally"),
2, NR::X, true);
addNodeButton("node_vdistribute",
_("Distribute selected nodes vertically"),
3, NR::Y, true);
//Rest of the widgetry
// Notebook for individual transformations
//Connect to the global tool change signal
// Connect to the global selection change, to invalidate cached randomize_bbox
g_signal_connect (G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (on_selection_changed), this);
}
{
it ++)
delete *it;
}
void AlignAndDistribute::on_ref_change(){
//Make blink the master
}
{
//Act on widgets used in node mode
//Act on widgets used in selection mode
((_alignFrame).*(mSel))();
((_distributeFrame).*(mSel))();
((_removeOverlapFrame).*(mSel))();
((_graphLayoutFrame).*(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(
);
}
{
new ActionUnclump(
);
}
{
new ActionRandomize(
);
}
{
new ActionBaseline(
}
std::list<SPItem *>::iterator AlignAndDistribute::find_master( std::list<SPItem *> &list, bool horizontal){
switch (getAlignTarget()) {
case LAST:
break;
case FIRST:
break;
case BIGGEST:
{
if (b) {
}
}
}
return master;
break;
}
case SMALLEST:
{
if (b) {
}
}
}
return master;
break;
}
default:
break;
} // end of switch statement
return master;
}
}
} // 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:encoding=utf-8:textwidth=99 :