/*
 * Helper object for showing selected items
 *
 * Authors:
 *   bulia byak <bulia@users.sf.net>
 *   Carl Hetherington <inkscape@carlh.net>
 *   Abhishek Sharma
 *
 * Copyright (C) 2004 Authors
 *
 * Released under GNU GPL, read the file 'COPYING' for more information
 */

#include <string.h>

#include "desktop-handles.h"
#include "selection.h"
#include "display/sp-canvas-util.h"
#include "display/sodipodi-ctrl.h"
#include "display/sodipodi-ctrlrect.h"
#include "libnrtype/Layout-TNG.h"
#include "text-editing.h"
#include "sp-text.h"
#include "sp-flowtext.h"
#include "preferences.h"
#include "selcue.h"

Inkscape::SelCue::BoundingBoxPrefsObserver::BoundingBoxPrefsObserver(SelCue &sel_cue) :
    Observer("/tools/bounding_box"),
    _sel_cue(sel_cue)
{
}

void Inkscape::SelCue::BoundingBoxPrefsObserver::notify(Preferences::Entry const &val)
{
    _sel_cue._boundingBoxPrefsChanged(static_cast<int>(val.getBool()));
}

Inkscape::SelCue::SelCue(SPDesktop *desktop)
    : _desktop(desktop),
      _bounding_box_prefs_observer(*this)
{
    _selection = sp_desktop_selection(_desktop);

    _sel_changed_connection = _selection->connectChanged(
        sigc::hide(sigc::mem_fun(*this, &Inkscape::SelCue::_newItemBboxes))
        );

    {
        void(SelCue::*modifiedSignal)() = &SelCue::_updateItemBboxes;
        _sel_modified_connection = _selection->connectModified(
            sigc::hide(sigc::hide(sigc::mem_fun(*this, modifiedSignal)))
        );
    }

    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
    _updateItemBboxes(prefs);
    prefs->addObserver(_bounding_box_prefs_observer);
}

Inkscape::SelCue::~SelCue()
{
    _sel_changed_connection.disconnect();
    _sel_modified_connection.disconnect();

    for (std::vector<SPCanvasItem*>::iterator i = _item_bboxes.begin(); i != _item_bboxes.end(); ++i) {
        sp_canvas_item_destroy(*i);
    }
    _item_bboxes.clear();

    for (std::vector<SPCanvasItem*>::iterator i = _text_baselines.begin(); i != _text_baselines.end(); ++i) {
        sp_canvas_item_destroy(*i);
    }
    _text_baselines.clear();
}

void Inkscape::SelCue::_updateItemBboxes()
{
    _updateItemBboxes(Inkscape::Preferences::get());
}

void Inkscape::SelCue::_updateItemBboxes(Inkscape::Preferences *prefs)
{
    gint mode = prefs->getInt("/options/selcue/value", MARK);
    if (mode == NONE) {
        return;
    }

    g_return_if_fail(_selection != NULL);

    int prefs_bbox = prefs->getBool("/tools/bounding_box");

    _updateItemBboxes(mode, prefs_bbox);
}

void Inkscape::SelCue::_updateItemBboxes(gint mode, int prefs_bbox)
{
    GSList const *items = _selection->itemList();
    if (_item_bboxes.size() != g_slist_length((GSList *) items)) {
        _newItemBboxes();
        return;
    }

    int bcount = 0;
    for (GSList const *l = _selection->itemList(); l != NULL; l = l->next) {
        SPItem *item = static_cast<SPItem *>(l->data);
        SPCanvasItem* box = _item_bboxes[bcount ++];

        if (box) {
            Geom::OptRect const b = (prefs_bbox == 0) ?
                item->desktopVisualBounds() : item->desktopGeometricBounds();

            if (b) {
                sp_canvas_item_show(box);
                if (mode == MARK) {
                    SP_CTRL(box)->moveto(Geom::Point(b->min()[Geom::X], b->max()[Geom::Y]));
                } else if (mode == BBOX) {
                    SP_CTRLRECT(box)->setRectangle(*b);
                }
            } else { // no bbox
                sp_canvas_item_hide(box);
            }
        }
    }

    _newTextBaselines();
}


void Inkscape::SelCue::_newItemBboxes()
{
    for (std::vector<SPCanvasItem*>::iterator i = _item_bboxes.begin(); i != _item_bboxes.end(); ++i) {
        sp_canvas_item_destroy(*i);
    }
    _item_bboxes.clear();

    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
    gint mode = prefs->getInt("/options/selcue/value", MARK);
    if (mode == NONE) {
        return;
    }

    g_return_if_fail(_selection != NULL);

    int prefs_bbox = prefs->getBool("/tools/bounding_box");
    
    for (GSList const *l = _selection->itemList(); l != NULL; l = l->next) {
        SPItem *item = static_cast<SPItem *>(l->data);

        Geom::OptRect const b = (prefs_bbox == 0) ?
            item->desktopVisualBounds() : item->desktopGeometricBounds();

        SPCanvasItem* box = NULL;

        if (b) {
            if (mode == MARK) {
                box = sp_canvas_item_new(sp_desktop_controls(_desktop),
                                         SP_TYPE_CTRL,
                                         "mode", SP_CTRL_MODE_XOR,
                                         "shape", SP_CTRL_SHAPE_DIAMOND,
                                         "size", 5.0,
                                         "filled", TRUE,
                                         "fill_color", 0x000000ff,
                                         "stroked", FALSE,
                                         "stroke_color", 0x000000ff,
                                         NULL);
                sp_canvas_item_show(box);
                SP_CTRL(box)->moveto(Geom::Point(b->min()[Geom::X], b->max()[Geom::Y]));

                sp_canvas_item_move_to_z(box, 0); // just low enough to not get in the way of other draggable knots

            } else if (mode == BBOX) {
                box = sp_canvas_item_new(sp_desktop_controls(_desktop),
                                         SP_TYPE_CTRLRECT,
                                         NULL);

                SP_CTRLRECT(box)->setRectangle(*b);
                SP_CTRLRECT(box)->setColor(0x000000a0, 0, 0);
                SP_CTRLRECT(box)->setDashed(true);
                SP_CTRLRECT(box)->setShadow(1, 0xffffffff);

                sp_canvas_item_move_to_z(box, 0);
            }
        }

        if (box) {
            _item_bboxes.push_back(box);
        }
    }

    _newTextBaselines();
}

void Inkscape::SelCue::_newTextBaselines()
{
    for (std::vector<SPCanvasItem*>::iterator i = _text_baselines.begin(); i != _text_baselines.end(); ++i) {
        sp_canvas_item_destroy(*i);
    }
    _text_baselines.clear();

    for (GSList const *l = _selection->itemList(); l != NULL; l = l->next) {
        SPItem *item = static_cast<SPItem *>(l->data);

        SPCanvasItem* baseline_point = NULL;
        if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item)) { // visualize baseline
            Inkscape::Text::Layout const *layout = te_get_layout(item);
            if (layout != NULL && layout->outputExists()) {
                boost::optional<Geom::Point> pt = layout->baselineAnchorPoint();
                if (pt) {
                    baseline_point = sp_canvas_item_new(sp_desktop_controls(_desktop), SP_TYPE_CTRL,
                        "mode", SP_CTRL_MODE_XOR,
                        "size", 4.0,
                        "filled", 0,
                        "stroked", 1,
                        "stroke_color", 0x000000ff,
                        NULL);

                    sp_canvas_item_show(baseline_point);
                    SP_CTRL(baseline_point)->moveto((*pt) * item->i2dt_affine());
                    sp_canvas_item_move_to_z(baseline_point, 0);
                }
            }
        }

        if (baseline_point) {
               _text_baselines.push_back(baseline_point);
        }
    }
}

void Inkscape::SelCue::_boundingBoxPrefsChanged(int prefs_bbox)
{
    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
    gint mode = prefs->getInt("/options/selcue/value", MARK);
    if (mode == NONE) {
        return;
    }

    g_return_if_fail(_selection != NULL);

    _updateItemBboxes(mode, prefs_bbox);
}

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