align-and-distribute.cpp revision 27639415745dc593c05c27ff6c98b08e6c1d5a32
/**
* @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/tool/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"
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;
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
// TODO: either uncomment or remove the following commented lines, depending on which
// behaviour of moving objects makes most sense; also cf. discussion at
/*if (!sel_as_group) { */
/*}*/
//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->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
bool changed = false;
if (sel_as_group)
//Move each item in the selected list separately
it++)
{
if (!sel_as_group)
if (b) {
changed = true;
}
}
}
// restore compensation setting
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"));
}
}
}
};
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")),
_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
#if WITH_GTKMM_2_24
#else
#endif
// Right align the buttons
// 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
}
void AlignAndDistribute::on_selgrp_toggled(){
//Make blink the master
}
{
//Act on widgets used in node mode
#if WITH_GTKMM_2_24
//Act on widgets used in selection mode
#else
//Act on widgets used in selection mode
#endif
((_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(
}
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:fileencoding=utf-8:textwidth=99 :