/*
 * Authors:
 *   Bryce Harrington <bryce@bryceharrington.org>
 *   Jon A. Cruz <jon@joncruz.org>
 *   Gustav Broberg <broberg@kth.se>
 *
 * Copyright (C) 2004 Bryce Harrington
 * Copyright (C) 2005 Jon A. Cruz
 * Copyright (C) 2007 Gustav Broberg
 *
 * Released under GNU GPL.  Read the file 'COPYING' for more information
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H
#include <glibmm/threads.h>
#endif

#include <gtkmm/dialog.h> // for Gtk::RESPONSE_*
#include <gtkmm/menu.h>
#include <gtkmm/stock.h>
#include <gtkmm/radiobutton.h>
#include <gtkmm/radiomenuitem.h>
#include <gtkmm/separatormenuitem.h>

#include <glibmm/i18n.h>

#include <gtk/gtk.h>

#include "panel.h"
#include "icon-size.h"
#include "preferences.h"
#include "desktop-handles.h"
#include "inkscape.h"
#include "widgets/eek-preview.h"
#include "ui/previewfillable.h"

namespace Inkscape {
namespace UI {
namespace Widget {

static const int PANEL_SETTING_SIZE = 0;
static const int PANEL_SETTING_MODE = 1;
static const int PANEL_SETTING_SHAPE = 2;
static const int PANEL_SETTING_WRAP = 3;
static const int PANEL_SETTING_BORDER = 4;
static const int PANEL_SETTING_NEXTFREE = 5;


void Panel::prep() {
    GtkIconSize sizes[] = {
        Inkscape::getRegisteredIconSize(Inkscape::ICON_SIZE_DECORATION),
        GTK_ICON_SIZE_MENU,
        GTK_ICON_SIZE_SMALL_TOOLBAR,
        GTK_ICON_SIZE_BUTTON,
        GTK_ICON_SIZE_DND, // Not used by options, but included to make the last size larger
        GTK_ICON_SIZE_DIALOG
    };
    eek_preview_set_size_mappings( G_N_ELEMENTS(sizes), sizes );
}

Panel::Panel(Glib::ustring const &label, gchar const *prefs_path,
             int verb_num, Glib::ustring const &apply_label,
             bool menu_desired) :
    _prefs_path(prefs_path),
    _menu_desired(menu_desired),
    _desktop(SP_ACTIVE_DESKTOP),
    _label(label),
    _apply_label(apply_label),
    _verb_num(verb_num),
    _temp_arrow(Gtk::ARROW_LEFT, Gtk::SHADOW_ETCHED_OUT),
    _menu(0),
    _action_area(0),
    _fillable(0)
{
    _init();
}

Panel::~Panel()
{
    delete _menu;
}

void Panel::_popper(GdkEventButton* event)
{
    if ( (event->type == GDK_BUTTON_PRESS) && (event->button == 3 || event->button == 1) ) {
        if (_menu) {
            _menu->popup(event->button, event->time);
        }
    }
}

void Panel::_init()
{
    Glib::ustring tmp("<");
    _anchor = SP_ANCHOR_CENTER;

    guint panel_size = 0, panel_mode = 0, panel_ratio = 100, panel_border = 0;
    bool panel_wrap = 0;
    if (!_prefs_path.empty()) {
        Inkscape::Preferences *prefs = Inkscape::Preferences::get();
        panel_wrap = prefs->getBool(_prefs_path + "/panel_wrap");
        panel_size = prefs->getIntLimited(_prefs_path + "/panel_size", 1, 0, PREVIEW_SIZE_HUGE);
        panel_mode = prefs->getIntLimited(_prefs_path + "/panel_mode", 1, 0, 10);
        panel_ratio = prefs->getIntLimited(_prefs_path + "/panel_ratio", 100, 0, 500 );
        panel_border = prefs->getIntLimited(_prefs_path + "/panel_border", BORDER_NONE, 0, 2 );
    }

    _menu = new Gtk::Menu();

    {
        Gtk::RadioMenuItem::Group group;
        Glib::ustring one_label(_("List"));
        Glib::ustring two_label(_("Grid"));
        Gtk::RadioMenuItem *one = Gtk::manage(new Gtk::RadioMenuItem(group, one_label));
        Gtk::RadioMenuItem *two = Gtk::manage(new Gtk::RadioMenuItem(group, two_label));

        if (panel_mode == 0) {
            one->set_active(true);
        } else if (panel_mode == 1) {
            two->set_active(true);
        }

        _menu->append(*one);
        _non_horizontal.push_back(one);
        _menu->append(*two);
        _non_horizontal.push_back(two);
        Gtk::MenuItem* sep = Gtk::manage(new Gtk::SeparatorMenuItem());
        _menu->append(*sep);
        _non_horizontal.push_back(sep);
        one->signal_activate().connect(sigc::bind<int, int>(sigc::mem_fun(*this, &Panel::_bounceCall), PANEL_SETTING_MODE, 0));
        two->signal_activate().connect(sigc::bind<int, int>(sigc::mem_fun(*this, &Panel::_bounceCall), PANEL_SETTING_MODE, 1));
    }

    {
        Glib::ustring heightItemLabel(C_("Swatches", "Size"));

        //TRANSLATORS: Indicates size of colour swatches
        const gchar *heightLabels[] = {
            NC_("Swatches height", "Tiny"),
            NC_("Swatches height", "Small"),
            NC_("Swatches height", "Medium"),
            NC_("Swatches height", "Large"),
            NC_("Swatches height", "Huge")
        };

        Gtk::MenuItem *sizeItem = Gtk::manage(new Gtk::MenuItem(heightItemLabel));
        Gtk::Menu *sizeMenu = Gtk::manage(new Gtk::Menu());
        sizeItem->set_submenu(*sizeMenu);

        Gtk::RadioMenuItem::Group heightGroup;
        for (unsigned int i = 0; i < G_N_ELEMENTS(heightLabels); i++) {
            Glib::ustring _label(g_dpgettext2(NULL, "Swatches height", heightLabels[i]));
            Gtk::RadioMenuItem* _item = Gtk::manage(new Gtk::RadioMenuItem(heightGroup, _label));
            sizeMenu->append(*_item);
            if (i == panel_size) {
                _item->set_active(true);
            }
            _item->signal_activate().connect(sigc::bind<int, int>(sigc::mem_fun(*this, &Panel::_bounceCall), PANEL_SETTING_SIZE, i));
       }

       _menu->append(*sizeItem);
    }

    {
        Glib::ustring widthItemLabel(C_("Swatches", "Width"));

        //TRANSLATORS: Indicates width of colour swatches
        const gchar *widthLabels[] = {
            NC_("Swatches width", "Narrower"),
            NC_("Swatches width", "Narrow"),
            NC_("Swatches width", "Medium"),
            NC_("Swatches width", "Wide"),
            NC_("Swatches width", "Wider")
        };

        Gtk::MenuItem *item = Gtk::manage( new Gtk::MenuItem(widthItemLabel));
        Gtk::Menu *type_menu = Gtk::manage(new Gtk::Menu());
        item->set_submenu(*type_menu);
        _menu->append(*item);

        Gtk::RadioMenuItem::Group widthGroup;

        guint values[] = {0, 25, 50, 100, 200, 400};
        guint hot_index = 3;
        for ( guint i = 0; i < G_N_ELEMENTS(widthLabels); ++i ) {
            // Assume all values are in increasing order
            if ( values[i] <= panel_ratio ) {
                hot_index = i;
            }
        }
        for ( guint i = 0; i < G_N_ELEMENTS(widthLabels); ++i ) {
            Glib::ustring _label(g_dpgettext2(NULL, "Swatches width", widthLabels[i]));
            Gtk::RadioMenuItem *_item = Gtk::manage(new Gtk::RadioMenuItem(widthGroup, _label));
            type_menu->append(*_item);
            if ( i <= hot_index ) {
                _item->set_active(true);
            }
            _item->signal_activate().connect(sigc::bind<int, int>(sigc::mem_fun(*this, &Panel::_bounceCall), PANEL_SETTING_SHAPE, values[i]));
        }
    }

    {
        Glib::ustring widthItemLabel(C_("Swatches", "Border"));

        //TRANSLATORS: Indicates border of colour swatches
        const gchar *widthLabels[] = {
            NC_("Swatches border", "None"),
            NC_("Swatches border", "Solid"),
            NC_("Swatches border", "Wide"),
        };

        Gtk::MenuItem *item = Gtk::manage( new Gtk::MenuItem(widthItemLabel));
        Gtk::Menu *type_menu = Gtk::manage(new Gtk::Menu());
        item->set_submenu(*type_menu);
        _menu->append(*item);

        Gtk::RadioMenuItem::Group widthGroup;

        guint values[] = {0, 1, 2};
        guint hot_index = 0;
        for ( guint i = 0; i < G_N_ELEMENTS(widthLabels); ++i ) {
            // Assume all values are in increasing order
            if ( values[i] <= panel_border ) {
                hot_index = i;
            }
        }
        for ( guint i = 0; i < G_N_ELEMENTS(widthLabels); ++i ) {
            Glib::ustring _label(g_dpgettext2(NULL, "Swatches border", widthLabels[i]));
            Gtk::RadioMenuItem *_item = Gtk::manage(new Gtk::RadioMenuItem(widthGroup, _label));
            type_menu->append(*_item);
            if ( i <= hot_index ) {
                _item->set_active(true);
            }
            _item->signal_activate().connect(sigc::bind<int, int>(sigc::mem_fun(*this, &Panel::_bounceCall), PANEL_SETTING_BORDER, values[i]));
        }
    }

    {
        //TRANSLATORS: "Wrap" indicates how colour swatches are displayed
        Glib::ustring wrap_label(C_("Swatches","Wrap"));
        Gtk::CheckMenuItem *check = Gtk::manage(new Gtk::CheckMenuItem(wrap_label));
        check->set_active(panel_wrap);
        _menu->append(*check);
        _non_vertical.push_back(check);

        check->signal_toggled().connect(sigc::bind<Gtk::CheckMenuItem*>(sigc::mem_fun(*this, &Panel::_wrapToggled), check));
    }

    Gtk::SeparatorMenuItem *sep;
    sep = Gtk::manage(new Gtk::SeparatorMenuItem());
    _menu->append(*sep);

    _menu->show_all_children();
    for ( std::vector<Gtk::Widget*>::iterator iter = _non_vertical.begin(); iter != _non_vertical.end(); ++iter ) {
        (*iter)->hide();
    }

    // _close_button.set_label("X");

    if (!_label.empty()) {
        _tab_title.set_label(_label);
        _top_bar.pack_start(_tab_title);
    }

    // _top_bar.pack_end(_close_button, false, false);


    if ( _menu_desired ) {
        _top_bar.pack_end(_menu_popper, false, false);
        gint width = 0;
        gint height = 0;

        if ( gtk_icon_size_lookup( Inkscape::getRegisteredIconSize(Inkscape::ICON_SIZE_DECORATION), &width, &height ) ) {
            _temp_arrow.set_size_request(width, height);
        }

        _menu_popper.add(_temp_arrow);
        _menu_popper.signal_button_press_event().connect_notify(sigc::mem_fun(*this, &Panel::_popper));
    }

    pack_start(_top_bar, false, false);

    Gtk::HBox* boxy = Gtk::manage(new Gtk::HBox());

    boxy->pack_start(_contents, true, true);
    boxy->pack_start(_right_bar, false, true);

    pack_start(*boxy, true, true);

    signalResponse().connect(sigc::mem_fun(*this, &Panel::_handleResponse));

    signalActivateDesktop().connect(sigc::hide<0>(sigc::mem_fun(*this, &Panel::setDesktop)));

    show_all_children();

    _bounceCall(PANEL_SETTING_SIZE, panel_size);
    _bounceCall(PANEL_SETTING_MODE, panel_mode);
    _bounceCall(PANEL_SETTING_SHAPE, panel_ratio);
    _bounceCall(PANEL_SETTING_WRAP, panel_wrap);
    _bounceCall(PANEL_SETTING_BORDER, panel_border);
}

void Panel::setLabel(Glib::ustring const &label)
{
    if (_label.empty() && !label.empty())
        _top_bar.pack_start(_tab_title);
    else if (!_label.empty() && label.empty())
        _top_bar.remove(_tab_title);

    _label = label;
    _tab_title.set_label(_label);
}

void Panel::setOrientation(SPAnchorType how)
{
    if (_anchor != how) {
        _anchor = how;
        switch (_anchor) {
            case SP_ANCHOR_NORTH:
            case SP_ANCHOR_SOUTH:
            {
                if (_menu_desired) {
                    _menu_popper.reference();
                    _top_bar.remove(_menu_popper);
                    _right_bar.pack_start(_menu_popper, false, false);
                    _menu_popper.unreference();

                    for (std::vector<Gtk::Widget*>::iterator iter = _non_horizontal.begin(); iter != _non_horizontal.end(); ++iter) {
                        (*iter)->hide();
                    }
                    for (std::vector<Gtk::Widget*>::iterator iter = _non_vertical.begin(); iter != _non_vertical.end(); ++iter) {
                        (*iter)->show();
                    }
                }
                // Ensure we are not in "list" mode
                _bounceCall(PANEL_SETTING_MODE, 1);
                if (!_label.empty())
                    _top_bar.remove(_tab_title);
            }
            break;

            default:
            {
                if ( _menu_desired ) {
                    for (std::vector<Gtk::Widget*>::iterator iter = _non_horizontal.begin(); iter != _non_horizontal.end(); ++iter) {
                        (*iter)->show();
                    }
                    for (std::vector<Gtk::Widget*>::iterator iter = _non_vertical.begin(); iter != _non_vertical.end(); ++iter) {
                        (*iter)->hide();
                    }
                }
            }
        }
    }
}

void Panel::present()
{
    _signal_present.emit();
}


void Panel::restorePanelPrefs()
{
    guint panel_size = 0, panel_mode = 0, panel_ratio = 100, panel_border = 0;
    bool panel_wrap = 0;
    if (!_prefs_path.empty()) {
        Inkscape::Preferences *prefs = Inkscape::Preferences::get();
        panel_wrap = prefs->getBool(_prefs_path + "/panel_wrap");
        panel_size = prefs->getIntLimited(_prefs_path + "/panel_size", 1, 0, PREVIEW_SIZE_HUGE);
        panel_mode = prefs->getIntLimited(_prefs_path + "/panel_mode", 1, 0, 10);
        panel_ratio = prefs->getIntLimited(_prefs_path + "/panel_ratio", 000, 0, 500 );
        panel_border = prefs->getIntLimited(_prefs_path + "/panel_border", BORDER_NONE, 0, 2 );
    }
    _bounceCall(PANEL_SETTING_SIZE, panel_size);
    _bounceCall(PANEL_SETTING_MODE, panel_mode);
    _bounceCall(PANEL_SETTING_SHAPE, panel_ratio);
    _bounceCall(PANEL_SETTING_WRAP, panel_wrap);
    _bounceCall(PANEL_SETTING_BORDER, panel_border);
}

sigc::signal<void, int> &Panel::signalResponse()
{
    return _signal_response;
}

sigc::signal<void> &Panel::signalPresent()
{
    return _signal_present;
}

void Panel::_bounceCall(int i, int j)
{
    _menu->set_active(0);
    switch (i) {
    case PANEL_SETTING_SIZE:
        if (!_prefs_path.empty()) {
            Inkscape::Preferences *prefs = Inkscape::Preferences::get();
            prefs->setInt(_prefs_path + "/panel_size", j);
        }
        if (_fillable) {
            ViewType curr_type = _fillable->getPreviewType();
            guint curr_ratio = _fillable->getPreviewRatio();
            ::BorderStyle curr_border = _fillable->getPreviewBorder();

            switch (j) {
            case 0:
            {
                _fillable->setStyle(::PREVIEW_SIZE_TINY, curr_type, curr_ratio, curr_border);
            }
            break;
            case 1:
            {
                _fillable->setStyle(::PREVIEW_SIZE_SMALL, curr_type, curr_ratio, curr_border);
            }
            break;
            case 2:
            {
                _fillable->setStyle(::PREVIEW_SIZE_MEDIUM, curr_type, curr_ratio, curr_border);
            }
            break;
            case 3:
            {
                _fillable->setStyle(::PREVIEW_SIZE_BIG, curr_type, curr_ratio, curr_border);
            }
            break;
            case 4:
            {
                _fillable->setStyle(::PREVIEW_SIZE_HUGE, curr_type, curr_ratio, curr_border);
            }
            break;
            default:
                ;
            }
        }
        break;
    case PANEL_SETTING_MODE:
        if (!_prefs_path.empty()) {
            Inkscape::Preferences *prefs = Inkscape::Preferences::get();
            prefs->setInt(_prefs_path + "/panel_mode", j);
        }
        if (_fillable) {
            ::PreviewSize curr_size = _fillable->getPreviewSize();
            guint curr_ratio = _fillable->getPreviewRatio();
            ::BorderStyle curr_border = _fillable->getPreviewBorder();
            switch (j) {
            case 0:
            {
                _fillable->setStyle(curr_size, VIEW_TYPE_LIST, curr_ratio, curr_border);
            }
            break;
            case 1:
            {
                _fillable->setStyle(curr_size, VIEW_TYPE_GRID, curr_ratio, curr_border);
            }
            break;
            default:
                break;
            }
        }
        break;
    case PANEL_SETTING_SHAPE:
        if (!_prefs_path.empty()) {
            Inkscape::Preferences *prefs = Inkscape::Preferences::get();
            prefs->setInt(_prefs_path + "/panel_ratio", j);
        }
        if ( _fillable ) {
            ViewType curr_type = _fillable->getPreviewType();
            ::PreviewSize curr_size = _fillable->getPreviewSize();
            ::BorderStyle curr_border = _fillable->getPreviewBorder();

            _fillable->setStyle(curr_size, curr_type, j, curr_border);
        }
        break;
    case PANEL_SETTING_BORDER:
        if (!_prefs_path.empty()) {
            Inkscape::Preferences *prefs = Inkscape::Preferences::get();
            prefs->setInt(_prefs_path + "/panel_border", j);
        }
        if ( _fillable ) {
            ::PreviewSize curr_size = _fillable->getPreviewSize();
            ViewType curr_type = _fillable->getPreviewType();
            guint curr_ratio = _fillable->getPreviewRatio();

            switch (j) {
            case 0:
            {
                _fillable->setStyle(curr_size, curr_type, curr_ratio, BORDER_NONE);
            }
            break;
            case 1:
            {
                _fillable->setStyle(curr_size, curr_type, curr_ratio, BORDER_SOLID);
            }
            break;
            case 2:
            {
                _fillable->setStyle(curr_size, curr_type, curr_ratio, BORDER_WIDE);
            }
            break;
            default:
                break;
            }
        }
        break;
    case PANEL_SETTING_WRAP:
        if (!_prefs_path.empty()) {
            Inkscape::Preferences *prefs = Inkscape::Preferences::get();
            prefs->setBool(_prefs_path + "/panel_wrap", j);
        }
        if ( _fillable ) {
            _fillable->setWrap(j);
        }
        break;
    default:
        _handleAction(i - PANEL_SETTING_NEXTFREE, j);
    }
}


void Panel::_wrapToggled(Gtk::CheckMenuItem* toggler)
{
    if (toggler) {
        _bounceCall(PANEL_SETTING_WRAP, toggler->get_active() ? 1 : 0);
    }
}

gchar const *Panel::getPrefsPath() const
{
    return _prefs_path.data();
}

Glib::ustring const &Panel::getLabel() const
{
    return _label;
}

int const &Panel::getVerb() const
{
    return _verb_num;
}

Glib::ustring const &Panel::getApplyLabel() const
{
    return _apply_label;
}

void Panel::setDesktop(SPDesktop *desktop)
{
    _desktop = desktop;
}

void Panel::_setTargetFillable(PreviewFillable *target)
{
    _fillable = target;
}

void Panel::_regItem(Gtk::MenuItem* item, int group, int id)
{
    _menu->append(*item);
    item->signal_activate().connect(sigc::bind<int, int>(sigc::mem_fun(*this, &Panel::_bounceCall), group + PANEL_SETTING_NEXTFREE, id));
    item->show();

}

void Panel::_handleAction(int /*set_id*/, int /*item_id*/)
{
// for subclasses to override
}

void Panel::_apply()
{
    g_warning("Apply button clicked for panel [Panel::_apply()]");
}

Gtk::Button *Panel::addResponseButton(const Glib::ustring &button_text, int response_id, bool pack_start)
{
    Gtk::Button *button = new Gtk::Button(button_text);
    _addResponseButton(button, response_id, pack_start);
    return button;
}

Gtk::Button *Panel::addResponseButton(const Gtk::StockID &stock_id, int response_id, bool pack_start)
{
    Gtk::Button *button = new Gtk::Button(stock_id);
    _addResponseButton(button, response_id, pack_start);
    return button;
}

void Panel::_addResponseButton(Gtk::Button *button, int response_id, bool pack_start)
{
    // Create a button box for the response buttons if it's the first button to be added
    if (!_action_area) {
#if WITH_GTKMM_3_0
        _action_area = new Gtk::ButtonBox();
        _action_area->set_layout(Gtk::BUTTONBOX_END);
        _action_area->set_spacing(6);
#else
        _action_area = new Gtk::HButtonBox(Gtk::BUTTONBOX_END, 6);
#endif
        _action_area->set_border_width(4);
        pack_end(*_action_area, Gtk::PACK_SHRINK, 0);
    }

    _action_area->pack_end(*button);

    if (pack_start) {
        _action_area->set_child_secondary( *button , true);
    }

    if (response_id != 0) {
        // Re-emit clicked signals as response signals
        button->signal_clicked().connect(sigc::bind(_signal_response.make_slot(), response_id));
        _response_map[response_id] = button;
    }
}

void Panel::setDefaultResponse(int response_id)
{
    ResponseMap::iterator widget_found;
    widget_found = _response_map.find(response_id);

    if (widget_found != _response_map.end()) {
        widget_found->second->activate();
        widget_found->second->property_can_default() = true;
        widget_found->second->grab_default();
    }
}

void Panel::setResponseSensitive(int response_id, bool setting)
{
    if (_response_map[response_id])
        _response_map[response_id]->set_sensitive(setting);
}

sigc::signal<void, SPDesktop *, SPDocument *> &
Panel::signalDocumentReplaced()
{
    return _signal_document_replaced;
}

sigc::signal<void, Inkscape::Application *, SPDesktop *> &
Panel::signalActivateDesktop()
{
    return _signal_activate_desktop;
}

sigc::signal<void, Inkscape::Application *, SPDesktop *> &
Panel::signalDeactiveDesktop()
{
    return _signal_deactive_desktop;
}

void Panel::_handleResponse(int response_id)
{
    switch (response_id) {
        case Gtk::RESPONSE_APPLY: {
            _apply();
            break;
        }
    }
}

Inkscape::Selection *Panel::_getSelection()
{
    return sp_desktop_selection(_desktop);
}

} // namespace Widget
} // 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 :
