/** @file
 * @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>
 *
 * Copyright (C) 1999-2004, 2005 Authors
 *
 * Released under GNU GPL.  Read the file 'COPYING' for more information.
 */

#ifndef INKSCAPE_UI_DIALOG_ALIGN_AND_DISTRIBUTE_H
#define INKSCAPE_UI_DIALOG_ALIGN_AND_DISTRIBUTE_H

#include <list>
#include "ui/widget/panel.h"
#include "ui/widget/frame.h"
#include <gtkmm/frame.h>
#include <gtkmm/comboboxtext.h>
#include <gtkmm/label.h>
#include "2geom/rect.h"
#include "ui/dialog/desktop-tracker.h"

#if WITH_GTKMM_3_0
#include <gtkmm/checkbutton.h>
#include <gtkmm/grid.h>
#else
#include <gtkmm/table.h>
#endif

class SPItem;

namespace Inkscape {
namespace UI {
namespace Dialog {

class Action;


class AlignAndDistribute : public Widget::Panel {
public:
    AlignAndDistribute();
    virtual ~AlignAndDistribute();

    static AlignAndDistribute &getInstance() { return *new AlignAndDistribute(); }

#if WITH_GTKMM_3_0
    Gtk::Grid &align_table(){return _alignTable;}
    Gtk::Grid &distribute_table(){return _distributeTable;}
    Gtk::Grid &rearrange_table(){return _rearrangeTable;}
    Gtk::Grid &removeOverlap_table(){return _removeOverlapTable;}
    Gtk::Grid &nodes_table(){return _nodesTable;}
#else
    Gtk::Table &align_table(){return _alignTable;}
    Gtk::Table &distribute_table(){return _distributeTable;}
    Gtk::Table &rearrange_table(){return _rearrangeTable;}
    Gtk::Table &removeOverlap_table(){return _removeOverlapTable;}
    Gtk::Table &nodes_table(){return _nodesTable;}
#endif

    void setMode(bool nodeEdit);

    Geom::OptRect randomize_bbox;

protected:

    void on_ref_change();
    void on_selgrp_toggled();
    void addDistributeButton(const Glib::ustring &id, const Glib::ustring tiptext, 
                                      guint row, guint col, bool onInterSpace, 
                                      Geom::Dim2 orientation, float kBegin, float kEnd);
    void addAlignButton(const Glib::ustring &id, const Glib::ustring tiptext, 
                        guint row, guint col);
    void addNodeButton(const Glib::ustring &id, const Glib::ustring tiptext, 
                        guint col, Geom::Dim2 orientation, bool distribute);
    void addRemoveOverlapsButton(const Glib::ustring &id,
                        const Glib::ustring tiptext,
                        guint row, guint col);
    void addGraphLayoutButton(const Glib::ustring &id,
                        const Glib::ustring tiptext,
                        guint row, guint col);
    void addExchangePositionsButton(const Glib::ustring &id,
                        const Glib::ustring tiptext,
                        guint row, guint col);
    void addExchangePositionsByZOrderButton(const Glib::ustring &id,
                        const Glib::ustring tiptext,
                        guint row, guint col);
    void addExchangePositionsClockwiseButton(const Glib::ustring &id,
                        const Glib::ustring tiptext,
                        guint row, guint col);
    void addUnclumpButton(const Glib::ustring &id, const Glib::ustring tiptext, 
                        guint row, guint col);
    void addRandomizeButton(const Glib::ustring &id, const Glib::ustring tiptext, 
                        guint row, guint col);
#if WITH_GTKMM_3_0
    void addBaselineButton(const Glib::ustring &id, const Glib::ustring tiptext,
                           guint row, guint col, Gtk::Grid &table, Geom::Dim2 orientation, bool distribute);
#else
    void addBaselineButton(const Glib::ustring &id, const Glib::ustring tiptext,
                           guint row, guint col, Gtk::Table &table, Geom::Dim2 orientation, bool distribute);
#endif
    void setTargetDesktop(SPDesktop *desktop);

    std::list<Action *> _actionList;
    UI::Widget::Frame _alignFrame, _distributeFrame, _rearrangeFrame, _removeOverlapFrame, _nodesFrame;
#if WITH_GTKMM_3_0
    Gtk::Grid _alignTable, _distributeTable, _rearrangeTable, _removeOverlapTable, _nodesTable;
#else
    Gtk::Table _alignTable, _distributeTable, _rearrangeTable, _removeOverlapTable, _nodesTable;
#endif
    Gtk::HBox _anchorBox;
    Gtk::HBox _selgrpBox;
    Gtk::VBox _alignBox;
    Gtk::HBox _alignTableBox;
    Gtk::HBox _distributeTableBox;
    Gtk::HBox _rearrangeTableBox;
    Gtk::HBox _removeOverlapTableBox;
    Gtk::HBox _nodesTableBox;
    Gtk::Label _anchorLabel;
    Gtk::Label _selgrpLabel;
    Gtk::CheckButton _selgrp;
    Gtk::ComboBoxText _combo;

    SPDesktop *_desktop;
    DesktopTracker _deskTrack;
    sigc::connection _desktopChangeConn;

private:
    AlignAndDistribute(AlignAndDistribute const &d);
    AlignAndDistribute& operator=(AlignAndDistribute const &d);
};


struct BBoxSort
{
    SPItem *item;
    float anchor;
    Geom::Rect bbox;
    BBoxSort(SPItem *pItem, Geom::Rect const &bounds, Geom::Dim2 orientation, double kBegin, double kEnd);
    BBoxSort(const BBoxSort &rhs);
};
bool operator< (const BBoxSort &a, const BBoxSort &b);


class Action {
public :

    Action(const Glib::ustring &id,
           const Glib::ustring &tiptext,
           guint row, guint column,
    #if WITH_GTKMM_3_0
       Gtk::Grid &parent,
    #else
       Gtk::Table &parent,
    #endif
           AlignAndDistribute &dialog);

    virtual ~Action(){}

    AlignAndDistribute &_dialog;

private :
    virtual void on_button_click(){}

    Glib::ustring _id;

#if WITH_GTKMM_3_0
    Gtk::Grid &_parent;
#else
    Gtk::Table &_parent;
#endif
};


class ActionAlign : public Action {
public :
    struct Coeffs {
       double mx0, mx1, my0, my1;
       double sx0, sx1, sy0, sy1;
       int verb_id;
    };
    enum AlignTarget { LAST=0, FIRST, BIGGEST, SMALLEST, PAGE, DRAWING, SELECTION };
    ActionAlign(const Glib::ustring &id,
                const Glib::ustring &tiptext,
                guint row, guint column,
                AlignAndDistribute &dialog,
                guint coeffIndex):
        Action(id, tiptext, row, column,
               dialog.align_table(), dialog),
        _index(coeffIndex),
        _dialog(dialog)
    {}

    /*
     * Static function called to align from a keyboard shortcut
     */
    static void do_verb_action(SPDesktop *desktop, int verb);
    static int verb_to_coeff(int verb);

private :


    virtual void on_button_click() {
        //Retreive selected objects
        SPDesktop *desktop = _dialog.getDesktop();
        if (!desktop) return;

        do_action(desktop, _index);
    }

    static void do_action(SPDesktop *desktop, int index);

    guint _index;
    AlignAndDistribute &_dialog;

    static const Coeffs _allCoeffs[11];

};


} // namespace Dialog
} // namespace UI
} // namespace Inkscape

#endif // INKSCAPE_UI_DIALOG_ALIGN_AND_DISTRIBUTE_H

/*
  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 :
