/***************************** LICENSE START ***********************************

 Copyright 2015 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include "ViewEditor.h"

#include <QAction>
#include <QCloseEvent>
#include <QComboBox>
#include <QDialogButtonBox>
#include <QFrame>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QMouseEvent>
#include <QPushButton>
#include <QSpinBox>
#include <QSplitter>
#include <QStackedWidget>
#include <QToolBar>
#include <QToolButton>
#include <QUndoStack>
#include <QUndoView>

#include "EditorFactory.h"
#include "SimpleEditor.h"
#include "IconObject.h"
#include "IconClass.h"
#include "IconFactory.h"
#include "MvIconLanguage.h"
//#include "Log.h"
//#include "PageView.h"
//#include "Dropping.h"
//#include "EditorButton.h"
#include "Folder.h"

#include "MvQPageView.h"
#include "MvQMethods.h"

std::vector<PaperSize*> PaperSizeHandler::std_;


//==============================================
//
// ViewEditor
//
//==============================================


ViewEditor::ViewEditor(const IconClass& name, const std::string& kind) :
    MvQEditor(name, kind)
{
    paper_ = new PaperSizeHandler;
    snap_  = new GridSnapHandler;

    undoStack_ = new QUndoStack(this);

    //-----------------------
    // Toolbar
    //-----------------------

    QList<QToolButton*> tbLst;

    //redo
    QAction* redoAc = undoStack_->createRedoAction(this, tr("&Redo"));
    redoAc->setShortcuts(QKeySequence::Redo);
    redoAc->setIcon(QPixmap(":/desktop/redo.svg"));

    //undo
    QAction* undoAc = undoStack_->createUndoAction(this, tr("&Undo"));
    undoAc->setShortcuts(QKeySequence::Undo);
    undoAc->setIcon(QPixmap(":/desktop/undo.svg"));

    //Duplicate
    auto* duplicateAc = new QAction(this);
    duplicateAc->setToolTip(tr("Duplicate selected frames"));
    duplicateAc->setIcon(QPixmap(":/desktop/duplicate.svg"));

    connect(duplicateAc, SIGNAL(triggered(bool)),
            this, SLOT(slotDuplicateSelection(bool)));

    //page setup
    auto* pageAc = new QAction(this);
    pageAc->setToolTip(tr("Page setup"));
    pageAc->setIcon(QPixmap(":/desktop/page_setup.svg"));

    connect(pageAc, SIGNAL(triggered(bool)),
            this, SLOT(slotSetupPage(bool)));

    snapAc_ = new QAction(this);
    snapAc_->setToolTip(tr("Snap to grid"));
    snapAc_->setCheckable(true);
    snapAc_->setChecked(true);
    snapAc_->setIcon(QPixmap(":/desktop/snap_to_grid.svg"));

    connect(snapAc_, SIGNAL(toggled(bool)),
            this, SLOT(slotSnap(bool)));

    gridAc_ = new QAction(this);
    gridAc_->setToolTip(tr("View grid"));
    gridAc_->setCheckable(true);
    gridAc_->setChecked(true);
    gridAc_->setIcon(QPixmap(":/desktop/view_grid.svg"));

    connect(gridAc_, SIGNAL(toggled(bool)),
            this, SLOT(slotGrid(bool)));

    auto* selectAllAc = new QAction(this);
    selectAllAc->setToolTip(tr("Select all frames"));
    selectAllAc->setIcon(QPixmap(":/desktop/page_select_all.svg"));

    connect(selectAllAc, SIGNAL(triggered(bool)),
            this, SLOT(slotSelectAll(bool)));

    auto* sep = new QAction(this);
    sep->setSeparator(true);

    auto* sep1 = new QAction(this);
    sep1->setSeparator(true);

    QList<QAction*> acLst;
    acLst << pageAc << sep << undoAc << redoAc << duplicateAc << selectAllAc << sep1 << snapAc_ << gridAc_;
    addToToolBar(acLst);

    //--------------------------------------
    // Define view layout in the middle
    //--------------------------------------

    auto* viewLayout = new QHBoxLayout();
    centralLayout_->addLayout(viewLayout);

    //-----------------------
    // Sidebar on the right
    //-----------------------

    //Sidebar on the right
    auto* rightVb = new QVBoxLayout;
    rightVb->setSpacing(0);
    rightVb->setContentsMargins(1, 1, 1, 1);
    viewLayout->addLayout(rightVb);

    auto* sidebar_ = new QToolBar(this);
    sidebar_->setIconSize(QSize(24, 24));
    sidebar_->setOrientation(Qt::Vertical);
    rightVb->addWidget(sidebar_);

    auto* addAc = new QAction(this);
    addAc->setToolTip(tr("Add new frame"));
    addAc->setIcon(QPixmap(":/desktop/page_add.svg"));

    auto* joinAc = new QAction(this);
    joinAc->setToolTip(tr("Join selected frames"));
    joinAc->setIcon(QPixmap(":/desktop/page_join.svg"));

    auto* splitAc = new QAction(this);
    splitAc->setToolTip(tr("Split selected frames"));
    splitAc->setIcon(QPixmap(":/desktop/page_split.svg"));

    auto* deleteAc = new QAction(this);
    deleteAc->setToolTip(tr("Delete selected frames"));
    deleteAc->setIcon(QPixmap(":/desktop/page_delete.svg"));

    auto* expandAc = new QAction(this);
    expandAc->setToolTip(tr("Expand selected frames"));
    expandAc->setIcon(QPixmap(":/desktop/page_expand.svg"));

    auto* connectAc = new QAction(this);
    connectAc->setToolTip(tr("Connect selected frames - NOT YET SUPPORTED"));
    connectAc->setIcon(QPixmap(":/desktop/page_connect.svg"));
    connectAc->setEnabled(false);

    auto* disconnectAc = new QAction(this);
    disconnectAc->setToolTip(tr("Disonnect selected frames"));
    disconnectAc->setIcon(QPixmap(":/desktop/page_disconnect.svg"));

    connect(addAc, SIGNAL(triggered(bool)),
            this, SLOT(slotAddPage(bool)));

    connect(joinAc, SIGNAL(triggered(bool)),
            this, SLOT(slotJoinSelection(bool)));

    connect(deleteAc, SIGNAL(triggered(bool)),
            this, SLOT(slotDeleteSelection(bool)));

    connect(splitAc, SIGNAL(triggered(bool)),
            this, SLOT(slotSplitSelection(bool)));

    connect(expandAc, SIGNAL(triggered(bool)),
            this, SLOT(slotExpandSelection(bool)));

    //connect(connectAc,SIGNAL(triggered(bool)),
    //this,SLOT(slotConnectSelection(bool)));

    connect(disconnectAc, SIGNAL(triggered(bool)),
            this, SLOT(slotDisconnectSelection(bool)));

    connect(selectAllAc, SIGNAL(triggered(bool)),
            this, SLOT(slotSelectAll(bool)));

    sidebar_->addAction(addAc);
    sidebar_->addAction(deleteAc);
    sidebar_->addAction(joinAc);
    sidebar_->addAction(splitAc);
    sidebar_->addAction(expandAc);
    sidebar_->addSeparator();
    sidebar_->addAction(connectAc);
    sidebar_->addAction(disconnectAc);

    //--------------------
    // View
    //--------------------

    MvIconLanguage& lang                    = IconClass::find("PLOT_PAGE").language();
    Request interface                       = lang.interfaceRequest("VIEW");
    const std::vector<std::string>& classes = interface.get("class");

    view_ = new MvQPageView(classes, undoStack_, this);
    viewLayout->addWidget(view_, 1);

    connect(view_, SIGNAL(changed()),
            this, SLOT(slotViewChanged()));

    connect(view_, SIGNAL(selectionChanged(int)),
            this, SLOT(updateButtonStatus(int)));

    updateButtonStatus(0);
}

ViewEditor::~ViewEditor() = default;

void ViewEditor::apply()
{
    savePaperSize(request_);
    savePages(current_, request_);
    current_->setRequest(request_);
}

void ViewEditor::reset()
{
    //cache_.clear();
    //save_.clear();
    request_ = current_->request();
    request_.print();

    view_->clear();
    loadPaperSize(request_);
    loadPages(current_, request_);

    //PaperSetPages(paper_,loadPages(current_,request_));
    //updateDrawers();
    //PaperSelectFirst(paper_);
}

void ViewEditor::close()
{
    view_->clear();
    //save_.clear();
}

void ViewEditor::merge(IconObjectH /*o*/)
{
}

void ViewEditor::replace(IconObjectH /*o*/)
{
}


void ViewEditor::loadPaperSize(const Request& r)
{
    paper_->set(r);

    double w = paper_->width();
    double h = paper_->height();

    setPaperSize(h, w);

    //cout << "h = " << h << " w = " << w << std::endl;

    /*	
  std::cout << "paperSize " << std::endl;
	r.print();

	// Set paper size...
	double w = r("CUSTOM_HEIGHT");
	double h = r("CUSTOM_WIDTH");

	const char* size  = r("LAYOUT_SIZE");
	const char* orien = r("LAYOUT_ORIENTATION");

	if(size && orien)
	{  	
		for(int i = 0; i < static_cast<int>(paperSizes_.size()) ; i++)
		{
            if(paperSizes_[i].name() == std::string(size))
			{
				if(strcmp(orien,"PORTRAIT") == 0)
				{
					h = paperSizes_[i].height();
					w = paperSizes_[i].width();
					break;
				}
				else
				{
					w = paperSizes_[i].height();
					h = paperSizes_[i].width();
					break;
				}	
			}
		}	
	}

	paperSizeReq_("LAYOUT_SIZE")=size;
	paperSizeReq_("LAYOUT_SIZE")=orien;
	paperSizeReq_("CUSTOM_HEIGHT")=h;
	paperSizeReq_("CUSTOM_WIDTH")=w;
	
	setPaperSize(h,w);

	cout << "h = " << h << " w = " << w << std::endl;
	*/
}

void ViewEditor::savePaperSize(Request& r)
{
    paper_->save(r);
}

#if 0
PageView* ViewEditor::pageView(IconObject* o)
{
	map<IconObjectH,PageViewH>::iterator j = cache_.find(o);
	if(j == cache_.end())
		cache_[o] = new PageView(o);
	return cache_[o];
}
#endif

void ViewEditor::loadPages(IconObject* o, const Request& r)
{
    //Clear the view

    std::vector<IconObjectH> sub = o->subObjects("PAGES", r);

    for (auto& itPage : sub) {
        MvRequest pageReq = itPage->request();

        pageReq.print();

        double top            = pageReq("TOP");
        double left           = pageReq("LEFT");
        double right          = pageReq("RIGHT");
        double bottom         = pageReq("BOTTOM");
        MvQPageItem* pageItem = view_->addPage(left, top, right - left, bottom - top);

        std::vector<IconObjectH> viewVec = itPage->subObjects("VIEW", pageReq);

        IconObject* viewObj = viewVec.size() ? static_cast<IconObject*>(viewVec[0]) : defaultView();
        pageItem->setViewIcon(viewObj);

        loadSubPages(pageItem, itPage, pageReq);
    }
}

void ViewEditor::loadSubPages(MvQPageItem* p, IconObject* o, const Request& r)
{
    // Old style

    /*int rows = r("ROWS");
	int cols = r("COLUMNS");

	if(rows * cols > 1)
	{
		double height = 1.0 / rows;pageItem
		double width  = 1.0 / cols;
		double x = 0;

		for(int i = 0; i < cols; i++) 
		{
			double y = 0;
			for(int j = 0; j < rows; j++)
			{
				PaperRectangle* s = PaperNewPage(
					paper_,
					y, 
					x, 
					(j == rows - 1) ? 1.0 : y + height, 
					(i == cols - 1) ? 1.0 : x + width
					);

				s->next = pages;
				pages = s;
				y += height;
			}
			x +=  width;
		}
	}*/

    // New Style
    std::vector<IconObjectH> sub = o->subObjects("SUB_PAGES", r);
    for (auto& ii : sub) {
        MvRequest subR = ii->request();

        subR.print();

        double top    = subR("TOP");
        double left   = subR("LEFT");
        double right  = subR("RIGHT");
        double bottom = subR("BOTTOM");

        p->addSubPaperRectInPageCoords(left, top, right - left, bottom - top);
    }
}


void ViewEditor::savePages(IconObject* o, Request& r)
{
    r.print();

    FolderH pageFolder = o->embeddedFolder("Pages", true);

    // Clean up old stuff
    pageFolder->destroy();
    pageFolder = o->embeddedFolder("Pages", true);

    std::vector<IconObjectH> pages;

    foreach (MvQPageItem* p, view_->pages()) {
        Request pageReq("PLOT_PAGE");

        saveRectToRequest(p->paperRect(), pageReq);

        pageReq.print();

        IconObjectH page      = IconFactory::create(pageFolder, pageReq, 0, 0);
        FolderH subPageFolder = page->embeddedFolder("Sub Pages", true);
        pages.push_back(page);

        std::vector<IconObjectH> subpages;

        foreach (QRectF subRect, p->subPaperRects()) {
            Request subReq("PLOT_SUBPAGE");
            saveRectToRequest(subRect, subReq);
            subReq.print();
            subpages.push_back(IconFactory::create(subPageFolder, subReq, 0, 0));
        }

        page->setSubObjects("SUB_PAGES", subpages, pageReq);

        IconObjectH view = p->viewIcon();
        if (view)
            page->setSubObjects("VIEW", std::vector<IconObjectH>(1, view), pageReq);

        page->setRequest(pageReq);
    }

    r.print();

    current_->setSubObjects("PAGES", pages, r);
    current_->setRequest(r);
}

void ViewEditor::saveRectToRequest(QRectF rect, Request& r)
{
    double top    = rect.y();
    double left   = rect.x();
    double bottom = rect.bottomLeft().y();
    double right  = rect.topRight().x();

    r("TOP")    = top;
    r("LEFT")   = left;
    r("BOTTOM") = bottom;
    r("RIGHT")  = right;
}

IconObject* ViewEditor::defaultView()
{
    return IconClass::find("GEOVIEW").defaultObject();
}

//-----------------------------------------------------------

void ViewEditor::updateButtonStatus(int /*selected*/)
{
    return;

    /*if(selected ==0)
	{
	  	joinAc->setEnabled(false);
		splitAc->setEnabled(false);
  		deleteAc->setEnabled(false);
		expandAc->setEnabled(false);
	}
	
	else
	{
		splitAc->setEnabled(true);
  		deleteAc->setEnabled(true);
		expandAc->setEnabled(true);
		
		if(selected > 1)
		  	joinAc->setEnabled(true);
		else
		  	joinAc->setEnabled(false);
	}*/
}


void ViewEditor::setPaperSize(double height, double width)
{
    view_->setSuperPage(width, height, snapAc_->isChecked(), snap_->x(), snap_->y(), gridAc_->isChecked());
}

std::string ViewEditor::alternateEditor()
{
    return "SimpleEditor";
}

void ViewEditor::slotSnap(bool on)
{
    view_->enableSnap(on);
}

void ViewEditor::slotGrid(bool on)
{
    view_->enableGrid(on);
}

void ViewEditor::slotAddPage(bool)
{
    MvQPageItem* pageItem = view_->addPage(0, 0, 25, 25);
    pageItem->setViewIcon(defaultView());
    changed();
}

void ViewEditor::slotDuplicateSelection(bool)
{
    view_->duplicateSelection();
    changed();
}

void ViewEditor::slotDeleteSelection(bool)
{
    view_->deleteSelection();
    changed();
}

void ViewEditor::slotJoinSelection(bool)
{
    view_->joinSelection();
    changed();
}

void ViewEditor::slotSplitSelection(bool)
{
    MvQSplitDialog dialog(this);
    if (dialog.exec() == QDialog::Accepted) {
        view_->splitSelection(dialog.row() + 1, dialog.column() + 1);
        changed();
    }
}

void ViewEditor::slotExpandSelection(bool)
{
    view_->expandSelection();
    changed();
}

void ViewEditor::slotSelectAll(bool)
{
    view_->selectAll();
    changed();
}

void ViewEditor::slotConnectSelection(bool)
{
    view_->connectSelection();
    changed();
}

void ViewEditor::slotDisconnectSelection(bool)
{
    view_->disconnectSelection();
    changed();
}

void ViewEditor::slotSetupPage(bool)
{
    MvQPageSetupDialog dialog(paper_, snap_, this);
    if (dialog.exec() == QDialog::Accepted) {
        setPaperSize(paper_->height(), paper_->width());
        view_->setSnap(snap_->x(), snap_->y());
        changed();
    }
}

void ViewEditor::slotViewChanged()
{
    changed();
}
void ViewEditor::readSettings(QSettings& settings)
{
    settings.beginGroup("layout_editor");

    QVariant v = settings.value("grid");
    if (!v.isNull())
        gridAc_->setChecked(v.toBool());

    v           = settings.value("grid_x");
    QVariant v1 = settings.value("grid_y");

    if (!v.isNull() && !v1.isNull()) {
        snap_->setCurrentValues(v.toFloat(), v1.toFloat());
        view_->setSnap(snap_->x(), snap_->y());
    }

    v = settings.value("snap");
    if (!v.isNull())
        snapAc_->setChecked(v.toBool());

    settings.endGroup();
}

void ViewEditor::writeSettings(QSettings& settings)
{
    settings.beginGroup("layout_editor");

    settings.setValue("grid", gridAc_->isChecked());
    settings.setValue("grid_x", snap_->x());
    settings.setValue("grid_y", snap_->y());
    settings.setValue("snap", snapAc_->isChecked());

    settings.endGroup();
}

//==================================================================================
//==================================================================================


//==============================================
//
// PaperSizeHandler
//
//==============================================

PaperSizeHandler::PaperSizeHandler() :
    orientation_("Landscape"),
    current_(0)
{
    if (std_.size() == 0) {
        std_.push_back(new PaperSize("A4", 21, 29.7, "PORTRAIT"));
        std_.push_back(new PaperSize("A4", 29.7, 21, "LANDSCAPE"));
        std_.push_back(new PaperSize("A3", 29.7, 42, "PORTRAIT"));
        std_.push_back(new PaperSize("A3", 42, 29.7, "LANDSCAPE"));
    }

    custom_ = new PaperSize("CUSTOM", 29.7, 21, "");

    current_ = std_[0];
}


void PaperSizeHandler::set(const Request& r)
{
    double h = r("CUSTOM_HEIGHT");
    double w = r("CUSTOM_WIDTH");

    const char* size  = r("LAYOUT_SIZE");
    const char* orien = r("LAYOUT_ORIENTATION");

    std::string sizeStr = "A4";
    if (size)
        sizeStr = std::string(size);

    orientation_ = "LANDSCAPE";
    if (orien)
        orientation_ = std::string(orien);

    bool found = false;
    for (auto& i : std_) {
        if (i->name() == sizeStr && i->orientation() == orientation_) {
            current_ = i;
            found    = true;
            break;
        }
    }

    if (!found) {
        custom_->setWidth(w);
        custom_->setHeight(h);
        current_ = custom_;
    }
}

void PaperSizeHandler::set(const std::string& sizeStr, const std::string& orien, double w, double h)
{
    orientation_ = orien;

    bool found = false;
    for (auto& i : std_) {
        if (i->name() == sizeStr && i->orientation() == orien) {
            current_ = i;
            found    = true;
            break;
        }
    }

    if (!found) {
        custom_->setWidth(w);
        custom_->setHeight(h);
        current_ = custom_;
    }
}

void PaperSizeHandler::save(Request& r)
{
    r("LAYOUT_SIZE")   = current_->name().c_str();
    r("CUSTOM_WIDTH")  = current_->width();
    r("CUSTOM_HEIGHT") = current_->height();

    if (current_ != custom_) {
        r("LAYOUT_ORIENTATION") = current_->orientation().c_str();
        r.unsetParam("CUSTOM_WIDTH");
        r.unsetParam("CUSTOM_HEIGHT");
    }
    else {
        r.unsetParam("LAYOUT_ORIENTATION");
    }
}


double PaperSizeHandler::width()
{
    return (current_) ? current_->width() : 0;
}

double PaperSizeHandler::height()
{
    return (current_) ? current_->height() : 0;
}

//================================================
//
//  GridSnapHandler
//
//================================================

GridSnapHandler::GridSnapHandler()
{
    units_ = "%";

    float v[] = {1, 2, 4, 5, 10, 12, 5, 20, 25, 50};
    vals_.assign(v, v + sizeof(v) / sizeof(v[0]));

    //vals_ << 1 << 2 << 4 << 5 << 10 << 12.5 << 20 << 25 << 50;
    xIndex_ = 3;
    yIndex_ = 3;
}

void GridSnapHandler::setCurrent(int xi, int yi)
{
    if (xi >= 0 && xi < static_cast<int>(vals_.size())) {
        xIndex_ = xi;
    }
    if (yi >= 0 && yi < static_cast<int>(vals_.size())) {
        yIndex_ = yi;
    }
}

void GridSnapHandler::setCurrentValues(float x, float y)
{
    for (unsigned int i = 0; i < vals_.size(); i++) {
        if (x == vals_[i])
            xIndex_ = i;
        if (y == vals_[i])
            yIndex_ = i;
    }
}

float GridSnapHandler::x() const
{
    return vals_[xIndex_];
}

float GridSnapHandler::y() const
{
    return vals_[yIndex_];
}

//================================================
//
//  MvQPaperSizeWidget
//
//================================================


MvQPaperSizeWidget::MvQPaperSizeWidget(PaperSizeHandler* paper, QWidget* parent) :
    QWidget(parent),
    paper_(paper)

{
    auto* grid = new QGridLayout;
    setLayout(grid);

    QLabel* label;

    int row = 0;

    //Size
    label   = new QLabel(tr("&Size:"));
    sizeCb_ = new QComboBox;
    label->setBuddy(sizeCb_);

    std::set<std::string> nameSet;
    for (auto it : paper_->stdSizes()) {
        //We do not allow duplicated names
        if (nameSet.find(it->name()) == nameSet.end()) {
            sizeCb_->addItem(QString::fromStdString(it->name()));
            sizeCb_->setItemData(sizeCb_->count() - 1, QString::fromStdString(it->name()));
            nameSet.insert(it->name());
        }
    }

    sizeCb_->addItem(tr("Custom"));
    sizeCb_->setItemData(sizeCb_->count() - 1, "CUSTOM");

    grid->addWidget(label, row, 0);
    grid->addWidget(sizeCb_, row, 1);
    row++;

    //orientation
    oriLabel_ = new QLabel(tr("&Orientation:"));
    oriCb_    = new QComboBox;
    oriLabel_->setBuddy(oriCb_);

    oriCb_->addItem(tr("Portrait"));
    oriCb_->setItemData(0, "PORTRAIT");

    oriCb_->addItem(tr("Landscape"));
    oriCb_->setItemData(1, "LANDSCAPE");

    grid->addWidget(oriLabel_, row, 0);
    grid->addWidget(oriCb_, row, 1);
    row++;

    //width
    widthLabel_ = new QLabel(tr("&Width:"));
    widthEdit_  = new QLineEdit;
    widthLabel_->setBuddy(widthEdit_);

    grid->addWidget(widthLabel_, row, 0);
    grid->addWidget(widthEdit_, row, 1);
    row++;

    //height
    heightLabel_ = new QLabel(tr("&Height:"));
    heightEdit_  = new QLineEdit;
    heightLabel_->setBuddy(heightEdit_);

    grid->addWidget(heightLabel_, row, 0);
    grid->addWidget(heightEdit_, row, 1);
    row++;

    //----------------------------------
    // Init
    //----------------------------------

    //Size
    if (paper_->isCustom()) {
        sizeCb_->setCurrentIndex(sizeCb_->count() - 1);
    }
    else {
        for (int i = 0; i < sizeCb_->count(); i++) {
            if (sizeCb_->itemText(i).toStdString() == paper_->current()->name()) {
                sizeCb_->setCurrentIndex(i);
                break;
            }
        }
    }

    //Orientation
    if (paper_->orientation() == "PORTRAIT")
        oriCb_->setCurrentIndex(0);
    else
        oriCb_->setCurrentIndex(1);

    //Width and height
    widthEdit_->setText(QString::number(paper_->width()));
    heightEdit_->setText(QString::number(paper_->height()));

    //State
    updateState();

    //Combo box signals
    connect(sizeCb_, SIGNAL(currentIndexChanged(int)),
            this, SLOT(slotSize(int)));
}

void MvQPaperSizeWidget::updateState()
{
    int index = sizeCb_->currentIndex();

    //A4, A3, ...
    if (sizeCb_->itemData(index).toString() != "CUSTOM") {
        oriLabel_->setEnabled(true);
        oriCb_->setEnabled(true);
        widthLabel_->setEnabled(false);
        widthEdit_->setEnabled(false);
        heightLabel_->setEnabled(false);
        heightEdit_->setEnabled(false);
    }
    else {
        oriLabel_->setEnabled(false);
        oriCb_->setEnabled(false);
        widthLabel_->setEnabled(true);
        widthEdit_->setEnabled(true);
        heightLabel_->setEnabled(true);
        heightEdit_->setEnabled(true);
    }
}

void MvQPaperSizeWidget::slotSize(int)
{
    updateState();
}

void MvQPaperSizeWidget::accept()
{
    int sizeIndex = sizeCb_->currentIndex();
    int oriIndex  = oriCb_->currentIndex();

    if (sizeIndex >= 0 && sizeIndex < sizeCb_->count() &&
        oriIndex >= 0 && oriIndex < oriCb_->count()) {
        paper_->set(sizeCb_->itemData(sizeIndex).toString().toStdString(),
                    oriCb_->itemData(oriIndex).toString().toStdString(),
                    widthEdit_->text().toDouble(),
                    heightEdit_->text().toDouble());
    }
}


//================================================
//
//  MvQSnapWidget
//
//================================================

MvQGridSnapWidget::MvQGridSnapWidget(GridSnapHandler* snap, QWidget* parent) :
    QWidget(parent),
    snap_(snap)

{
    auto* grid = new QGridLayout;
    setLayout(grid);

    QLabel* label;

    int row = 0;

    //x
    label = new QLabel(tr("&X grid step:"), this);
    xCb_  = new QComboBox(this);
    label->setBuddy(xCb_);
    grid->addWidget(label, row, 0);
    grid->addWidget(xCb_, row, 1);
    row++;

    //y
    label = new QLabel(tr("&Y grid step:"), this);
    yCb_  = new QComboBox(this);
    label->setBuddy(yCb_);
    grid->addWidget(label, row, 0);
    grid->addWidget(yCb_, row, 1);
    row++;

    grid->setColumnStretch(1, 1);

    //----------------------------------
    // Init
    //----------------------------------

    for (float i : snap_->values()) {
        xCb_->addItem(QString::number(i) + " " + QString::fromStdString(snap_->units()));
        yCb_->addItem(QString::number(i) + " " + QString::fromStdString(snap_->units()));
    }

    xCb_->setCurrentIndex((snap_->xIndex() != -1) ? snap_->xIndex() : 3);
    yCb_->setCurrentIndex((snap_->yIndex() != -1) ? snap_->yIndex() : 3);
}


void MvQGridSnapWidget::accept()
{
    int xIndex = xCb_->currentIndex();
    int yIndex = yCb_->currentIndex();

    if (xIndex >= 0 && yIndex >= 0) {
        snap_->setCurrent(xIndex, yIndex);
    }
}

//================================================
//
//  MvQSnapWidget
//
//================================================

MvQPageSetupDialog::MvQPageSetupDialog(PaperSizeHandler* paperH, GridSnapHandler* snapH, QWidget* parent) :
    QDialog(parent)
{
    setWindowTitle(tr("Page setup"));

    auto* vb = new QVBoxLayout;
    setLayout(vb);

    auto* tab = new QTabWidget(this);
    vb->addWidget(tab);

    paper_ = new MvQPaperSizeWidget(paperH, this);
    tab->addTab(paper_, tr("Paper size"));

    snap_ = new MvQGridSnapWidget(snapH, this);
    tab->addTab(snap_, tr("Grid"));

    // Buttonbox
    auto* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);

    connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
    connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));

    //Try to find the optimal dialog width
    QFont font;
    QFontMetrics fm(font);
    //setMinimumWidth(fm.width(tr("Orientation:  landscape"))+100);

    vb->addWidget(buttonBox);
}


void MvQPageSetupDialog::accept()
{
    paper_->accept();
    snap_->accept();

    QDialog::accept();
}


//================================================
//
//  MvQSplitDialog
//
//================================================

MvQSplitDialog::MvQSplitDialog(QWidget* parent) :
    QDialog(parent),
    row_(0),
    column_(0),
    maxRow_(100),
    maxColumn_(100),
    maxGrRow_(10),
    maxGrColumn_(10)
{
    setWindowTitle(tr("Split"));

    auto* vb = new QVBoxLayout;
    setLayout(vb);

    //Mode selector
    auto* modeFrame = new QFrame(this);
    modeFrame->setFrameShape(QFrame::StyledPanel);

    auto* hb = new QHBoxLayout(this);
    modeFrame->setLayout(hb);
    vb->addWidget(modeFrame);
    vb->addSpacing(4);

    auto* label = new QLabel(tr("Selection mode:"));
    modeCb_     = new QComboBox(this);
    modeCb_->addItem(tr("Manual"));
    modeCb_->addItem(tr("Graphical"));
    hb->addWidget(label);
    hb->addWidget(modeCb_);
    hb->addStretch(1);

    stacked_ = new QStackedWidget(this);
    vb->addWidget(stacked_);

    //---------------------------
    // Manual mode
    //---------------------------

    auto* w       = new QWidget(this);
    auto* mLayout = new QVBoxLayout();
    w->setLayout(mLayout);
    auto* mgLayout = new QGridLayout();
    mLayout->addLayout(mgLayout);
    mLayout->addStretch(1);

    //rows
    label = new QLabel(tr("Number of rows:"), this);
    mgLayout->addWidget(label, 0, 0);
    rowSpin_ = new QSpinBox(this);
    rowSpin_->setRange(1, maxRow_);
    mgLayout->addWidget(rowSpin_, 0, 1);

    //columns
    label = new QLabel(tr("Number of columns:"), this);
    mgLayout->addWidget(label, 1, 0);
    colSpin_ = new QSpinBox(this);
    colSpin_->setRange(1, maxColumn_);
    mgLayout->addWidget(colSpin_, 1, 1);

    stacked_->addWidget(w);

    //---------------------------
    // Graphical mode
    //---------------------------

    w             = new QWidget(this);
    auto* gLayout = new QVBoxLayout;
    w->setLayout(gLayout);

    label_ = new QLabel(this);
    gLayout->addWidget(label_);

    selector_ = new MvQGridSelector(row_, column_, maxGrRow_, maxGrColumn_, this);

    gLayout->addWidget(selector_, 1);

    stacked_->addWidget(w);

    //Initialise combo and stacked
    //The default is the graphical mode
    modeCb_->setCurrentIndex(1);
    stacked_->setCurrentIndex(1);
    updateLabel();

    //Signals and slots

    //Connect the combobox and the stacked widget to change the mode
    connect(modeCb_, SIGNAL(currentIndexChanged(int)),
            this, SLOT(slotChangeMode(int)));

    connect(rowSpin_, SIGNAL(valueChanged(int)),
            this, SLOT(slotRowChanged(int)));

    connect(colSpin_, SIGNAL(valueChanged(int)),
            this, SLOT(slotColumnChanged(int)));

    connect(selector_, SIGNAL(changed(int, int)),
            this, SLOT(slotSelectionChanged(int, int)));

    // Buttonbox
    auto* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);

    connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
    connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));

    //Try to find the optimal dialog width
    QFont font;
    QFontMetrics fm(font);
    setMinimumWidth(MvQ::textWidth(fm, tr("Columns")) + 100);

    vb->addWidget(buttonBox);

    //Read in the settings
    readSettings();

    //This will properly initialise everything
    slotChangeMode(modeCb_->currentIndex());
}

void MvQSplitDialog::closeEvent(QCloseEvent* event)
{
    reject();
    event->accept();
}

void MvQSplitDialog::accept()
{
    writeSettings();
    QDialog::accept();
}

void MvQSplitDialog::reject()
{
    writeSettings();
    QDialog::reject();
}

void MvQSplitDialog::slotChangeMode(int mode)
{
    if (mode == 0) {
        rowSpin_->setValue(row_ + 1);
        colSpin_->setValue(column_ + 1);
    }
    else if (mode == 1) {
        selector_->setSelection(row_, column_);
        updateLabel();
    }
    else
        return;

    stacked_->setCurrentIndex(mode);
}

void MvQSplitDialog::slotRowChanged(int row)
{
    row_ = row - 1;
}

void MvQSplitDialog::slotColumnChanged(int column)
{
    column_ = column - 1;
}

//From the grid selector
void MvQSplitDialog::slotSelectionChanged(int row, int column)
{
    row_    = row;
    column_ = column;
    updateLabel();
}

void MvQSplitDialog::updateLabel()
{
    label_->setText(tr("Split selected frames: <b>") + QString::number(row_ + 1) + "x" + QString::number(column_ + 1) + "</b>");
}

void MvQSplitDialog::writeSettings()
{
    QSettings settings(MVQ_QSETTINGS_DEFAULT_ARGS "MV4-Desktop-SplitDialog");

    //We have to clear it so that should not remember all the previous values
    settings.clear();

    settings.beginGroup("main");
    settings.setValue("size", size());
    settings.setValue("mode", modeCb_->currentIndex());
    settings.setValue("row", row_);
    settings.setValue("column", column_);
    settings.endGroup();
}

void MvQSplitDialog::readSettings()
{
    QSettings settings(MVQ_QSETTINGS_DEFAULT_ARGS, "MV4-Desktop-SplitDialog");

    settings.beginGroup("main");
    if (settings.contains("size")) {
        resize(settings.value("size").toSize());
    }
    else {
        resize(QSize(260, 260));
    }

    if (settings.contains("row")) {
        int r = settings.value("row").toInt();
        if (r > 0 && r < maxRow_)
            row_ = r;
    }

    if (settings.contains("column")) {
        int c = settings.value("column").toInt();
        if (c > 0 && c < maxColumn_)
            column_ = c;
    }

    if (settings.contains("mode")) {
        int m = settings.value("mode").toInt();
        if (m == 0 || m == 1) {
            modeCb_->setCurrentIndex(m);
        }
    }


    settings.endGroup();
}


//================================================
//
//  MvQGridSelector
//
//================================================

MvQGridSelector::MvQGridSelector(int row, int column, int rowNum, int colNum, QWidget* parent) :
    QWidget(parent),
    row_(row),
    column_(column),
    rowNum_(rowNum),
    columnNum_(colNum),
    offset_(1),
    dx_(5),
    dy_(5)
{
    selectedColour_ = QColor(151, 193, 219);
    rectColour_     = QColor(210, 210, 210);
    frameColour_    = rectColour_.darker(120);

    setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum));
    setMinimumSize(QSize(128, 128));

    setMouseTracking(true);
}

void MvQGridSelector::setGridSize(int row, int col)
{
    rowNum_    = row;
    columnNum_ = col;
    row_       = 0;
    column_    = 0;
    update();
}

void MvQGridSelector::setSelection(int row, int col)
{
    row_    = row;
    column_ = col;

    if (row_ >= rowNum_) {
        row_ = rowNum_ - 1;
    }

    if (column_ >= columnNum_) {
        column_ = columnNum_ - 1;
    }

    emit changed(row_, column_);
    update();
}

void MvQGridSelector::resizeEvent(QResizeEvent* /*event*/)
{
    int w = width();
    int h = height();
    //gridSize_=(w<h)?w:h;
    //cellSize_=gridSize_/cellsPerRow_;
    dx_ = (w - 2 * offset_) / columnNum_;
    dy_ = (h - 2 * offset_) / rowNum_;
}


void MvQGridSelector::paintEvent(QPaintEvent*)
{
    QPainter painter(this);

    painter.setPen(Qt::black);

    for (int i = 0; i < rowNum_; i++) {
        for (int j = 0; j < columnNum_; j++) {
            QColor col;
            if (i <= row_ && j <= column_)
                col = selectedColour_;
            else
                col = rectColour_;

            painter.fillRect(QRectF(offset_ + j * dx_, offset_ + i * dy_, dx_, dy_), col);
            painter.drawRect(QRectF(offset_ + j * dx_, offset_ + i * dy_, dx_, dy_));
        }
    }
}

bool MvQGridSelector::select(QPoint pos)
{
    int col = (pos.x() - offset_) / dx_;
    int row = (pos.y() - offset_) / dy_;

    if (row >= 0 && row < rowNum_ &&
        col >= 0 && col < columnNum_ &&
        (row != row_ || col != column_)) {
        row_    = row;
        column_ = col;
        emit changed(row_, column_);
        return true;
    }

    return false;
}

void MvQGridSelector::mousePressEvent(QMouseEvent* event)
{
    if (select(event->pos()))
        update();
}

void MvQGridSelector::mouseMoveEvent(QMouseEvent* event)
{
    if (event->buttons() & Qt::LeftButton &&
        select(event->pos()))
        update();
}

void MvQGridSelector::mouseReleaseEvent(QMouseEvent* /*event*/)
{
}


static EditorMaker<ViewEditor> editorMaker("ViewEditor");
