/***************************************************************************
 *   Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>  *
 ***************************************************************************/
/** @file
 * A tree view with more features.
 *
 * @author Stephane MANKOWSKI / Guillaume DE BURE
 */
#include "skgtreeview.h"
#include "skgtraces.h"
#include "skgobjectmodelbase.h"
#include "skgservices.h"
#include "skgdocument.h"
#include "skgerror.h"
#include "skgtransactionmng.h"
#include "skgobjectbase.h"
#include "skgmainpanel.h"
#include "skgtableview.h"

#include <ksavefile.h>
#include <kicon.h>
#include <kmenu.h>
#include <klocale.h>
#include <kfiledialog.h>

#include <QDomDocument>
#include <QHeaderView>
#include <QSortFilterProxyModel>
#include <QBasicTimer>
#include <QEvent>
#include <QMouseEvent>
#include <QScrollBar>
#include <QApplication>
#include <QPrinter>
#include <QPainter>
#include <QDesktopServices>
#include <QTimer>
#include <QSvgGenerator>
#include <QClipboard>
#include <qtextcodec.h>
#include <QPushButton>
#include <kstandardaction.h>

SKGTreeView::SKGTreeView(QWidget* parent)
    : QTreeView(parent),
      m_autoResize(true), m_autoResizeDone(false), m_actAutoResize(NULL),
      m_document(NULL), m_textResizable(true),
      m_model(NULL), m_proxyModel(NULL),
      stickH(false), stickV(false)
{
    //Initialize
    setTextElideMode(Qt::ElideMiddle);
    setAutoExpandDelay(300);
    setAnimated(true);

    m_timerDelayedResize.setSingleShot(true);
    connect(&m_timerDelayedResize, SIGNAL(timeout()), this, SLOT(resizeColumnsToContents()), Qt::QueuedConnection);

    m_timerSelectionChanged.setSingleShot(true);
    connect(&m_timerSelectionChanged, SIGNAL(timeout()), this, SIGNAL(selectionChangedDelayed()), Qt::QueuedConnection);

    m_timerScrollSelection.setSingleShot(true);
    connect(&m_timerScrollSelection, SIGNAL(timeout()), this, SLOT(scroolOnSelection()), Qt::QueuedConnection);

    //Menu
    QHeaderView* hori = header();
    hori->setContextMenuPolicy(Qt::CustomContextMenu);
    m_headerMenu = new KMenu(this);

    setContextMenuPolicy(Qt::ActionsContextMenu);
    connect(hori, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showHeaderMenu(QPoint)));
    connect(hori, SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(onSortChanged(int,Qt::SortOrder)));

    //
    KAction* actCopy = KStandardAction::copy(this, SLOT(copy()), NULL);
    if (SKGMainPanel::getMainPanel()) SKGMainPanel::getMainPanel()->registerGlobalAction("edit_copy", actCopy);
    insertGlobalAction("edit_copy");

    m_actExpandAll = new KAction(KIcon("format-indent-more"), i18nc("Noun, user action", "Expand all"), this);
    connect(m_actExpandAll, SIGNAL(triggered(bool)), this, SLOT(expandAll()));
    if (SKGMainPanel::getMainPanel()) SKGMainPanel::getMainPanel()->registerGlobalAction("edit_expandall", m_actExpandAll);
    insertGlobalAction("edit_expandall");

    m_actCollapseAll = new KAction(KIcon("format-indent-less"), i18nc("Noun, user action", "Collapse all"), this);
    connect(m_actCollapseAll, SIGNAL(triggered(bool)), this, SLOT(collapseAll()));
    if (SKGMainPanel::getMainPanel()) SKGMainPanel::getMainPanel()->registerGlobalAction("edit_collapseal", m_actCollapseAll);
    insertGlobalAction("edit_collapseal");

    connect(horizontalScrollBar(), SIGNAL(actionTriggered(int)), this, SLOT(onActionTriggered(int)));
    connect(verticalScrollBar(), SIGNAL(actionTriggered(int)), this, SLOT(onActionTriggered(int)));
    connect(horizontalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(onRangeChanged()));
    connect(verticalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(onRangeChanged()));

    //Headers
    hori->setMovable(true);
    hori->setResizeMode(QHeaderView::Fixed);
    setWordWrap(false);

    connect(header(), SIGNAL(sectionMoved(int,int,int)), this, SLOT(setupHeaderMenu()), Qt::QueuedConnection);

    connect(this, SIGNAL(clicked(QModelIndex)), this, SLOT(onClick(QModelIndex)));
    connect(this, SIGNAL(collapsed(QModelIndex)), this, SLOT(onCollapse(QModelIndex)));
    connect(this, SIGNAL(expanded(QModelIndex)), this, SLOT(onExpand(QModelIndex)));


    QAbstractScrollArea* scrollArea = qobject_cast<QAbstractScrollArea*>(this);
    if (scrollArea) {
        QWidget* viewport = scrollArea->viewport();
        if (viewport) {
            viewport->installEventFilter(this);
            scrollArea->installEventFilter(this);
        }
    }

    //Save original size
    m_fontOriginalPointSize = this->font().pointSize();
    m_iconOriginalSize = this->iconSize().height();
    if (m_iconOriginalSize <= 0) m_iconOriginalSize = 16;
}

SKGTreeView::~SKGTreeView()
{
    m_document = NULL;
    m_headerMenu = NULL;
    m_proxyModel = NULL;
    m_model = NULL;
    m_actExpandAll = NULL;
    m_actCollapseAll = NULL;
}

bool SKGTreeView::isAutoResized()
{
    return m_autoResize;
}

QString SKGTreeView::getState()
{
    SKGTRACEIN(10, "SKGTreeView::getState");
    QDomDocument doc("SKGML");
    QDomElement root = doc.createElement("parameters");
    doc.appendChild(root);

    QHeaderView* hHeader = header();
    if (hHeader && m_model) {
        if (isSortingEnabled()) {
            root.setAttribute("sortOrder", SKGServices::intToString((int) hHeader->sortIndicatorOrder()));
            root.setAttribute("sortColumn", m_model->getAttribute(hHeader->sortIndicatorSection()));
        }
        root.setAttribute("groupBy", m_groupby);

        //Memorize order
        int nb = hHeader->count();
        if (nb) {
            QString columns;
            QString columnsSize;
            QString columnsVisibility;
            for (int i = 0; i < nb; ++i) {
                int idx = hHeader->logicalIndex(i);
                if (i) columns += ';';
                columns += m_model->getAttribute(idx);

                if (i) columnsSize += ';';
                columnsSize += SKGServices::intToString(hHeader->sectionSize(idx));

                if (i) columnsVisibility += ';';
                columnsVisibility += (hHeader->isSectionHidden(idx) ? "N" : "Y");
            }
            root.setAttribute("columns", columns);
            if (!m_autoResize) root.setAttribute("columnsSize", columnsSize);
            root.setAttribute("columnsVisibility", columnsVisibility);
            root.setAttribute("columnsAutoResize", m_autoResize ? "Y" : "N");
        }

        //Memorize expanded groups
        if (!m_groupby.isEmpty()) {
            root.setAttribute("expandedGroups", SKGServices::stringsToCsv(m_expandedNodes));
        }
    }
    root.setAttribute("alternatingRowColors", alternatingRowColors() ? "Y" : "N");
    root.setAttribute("zoomPosition", SKGServices::intToString(zoomPosition()));

    QScrollBar* scroll = horizontalScrollBar();
    if (scroll && scroll->value() == scroll->maximum() && scroll->value() != scroll->minimum()) root.setAttribute("stickH", "Y");
    scroll = verticalScrollBar();
    if (scroll && scroll->value() == scroll->maximum() && scroll->value() != scroll->minimum()) root.setAttribute("stickV", "Y");
    return doc.toString();
}

void SKGTreeView::setState(const QString& iState)
{
    SKGTRACEIN(10, "SKGTreeView::setState");
    resetColumnsOrder();

    QDomDocument doc("SKGML");

    QString viewState = iState;
    if (viewState.isEmpty() && m_document) {
        //Get default state
        viewState = m_document->getParameter(m_parameterName);
    }

    Qt::SortOrder qtsortorder = Qt::AscendingOrder;
    if (doc.setContent(viewState)) {
        QDomElement root = doc.documentElement();

        QString sortOrder = root.attribute("sortOrder");
        QString sortColumn = root.attribute("sortColumn");
        m_groupby = root.attribute("groupBy");
        QString columns = root.attribute("columns");
        QString columnsSize = root.attribute("columnsSize");
        QString columnsVisibility = root.attribute("columnsVisibility");
        QString columnsAutoResize = root.attribute("columnsAutoResize");
        QString alternatingRowColors = root.attribute("alternatingRowColors");
        QString zoomPosition = root.attribute("zoomPosition");
        QString expandedGroups = root.attribute("expandedGroups");
        stickH = (root.attribute("stickH") == "Y");
        stickV = (root.attribute("stickV") == "Y");

        //Set column order
        QStringList listAtt;
        if (!columns.isEmpty()) {
            listAtt = SKGServices::splitCSVLine(columns, ';');
            QStringList sizes = SKGServices::splitCSVLine(columnsSize, ';');
            QStringList visibilities = SKGServices::splitCSVLine(columnsVisibility, ';');

            int nb = listAtt.count();
            int nbvisibilities = visibilities.count();
            int nbsizes = sizes.count();
            for (int i = 0; i < nb; ++i) {
                if (nbvisibilities == nb) {
                    listAtt[i] = listAtt[i] % '|' % visibilities[i];
                    if (nbsizes == nb) {
                        listAtt[i] = listAtt[i] % '|' % sizes[i];
                    }
                }
            }
        }
        if (m_model) {
            m_model->setSupportedAttributes(listAtt);
        }

        //Set autoResize
        if (!columnsAutoResize.isEmpty()) {
            m_autoResize = (columnsAutoResize == "Y");
            header()->setResizeMode(m_autoResize ? QHeaderView::Fixed : QHeaderView::Interactive);
            if (!m_autoResize) {
                m_timerDelayedResize.stop();
                m_autoResizeDone = false;
            }
        }

        //Set sort
        if (m_model && isSortingEnabled() && !sortOrder.isEmpty() && !sortColumn.isEmpty()) {
            int index = SKGServices::splitCSVLine(columns, ';').indexOf(sortColumn);
            if (index == -1) index = m_model->getIndexAttribute(sortColumn);
            if (index == -1) index = 0;
            qtsortorder = (Qt::SortOrder) SKGServices::stringToInt(sortOrder);
            this->sortByColumn(index, qtsortorder);
        }

        if (m_model) {
            QString att = m_groupby;
            if (att == "#") att = sortColumn;
            m_model->setGroupBy(att);
            m_model->dataModified();

            bool treeMode = !m_model->getParentChildAttribute().isEmpty();
            setRootIsDecorated(treeMode && m_groupby.isEmpty());
            if (m_actExpandAll) m_actExpandAll->setVisible(treeMode || !m_groupby.isEmpty());
            if (m_actCollapseAll) m_actCollapseAll->setVisible(treeMode || !m_groupby.isEmpty());
        }

        //Set alternatingRowColors
        if (!alternatingRowColors.isEmpty()) setAlternatingRowColors(alternatingRowColors == "Y");
        if (!zoomPosition.isEmpty()) setZoomPosition(SKGServices::stringToInt(zoomPosition));

        //Set expanded groups
        m_expandedNodes = SKGServices::splitCSVLine(expandedGroups);
        resetSelection();
    } else {
        if (m_model) {
            m_model->setSupportedAttributes(QStringList());
            m_groupby = "";
            m_model->setGroupBy(m_groupby);
            m_model->dataModified();

            bool treeMode = !m_model->getParentChildAttribute().isEmpty();
            setRootIsDecorated(treeMode && m_groupby.isEmpty());
            if (m_actExpandAll) m_actExpandAll->setVisible(treeMode || !m_groupby.isEmpty());
            if (m_actCollapseAll) m_actCollapseAll->setVisible(treeMode || !m_groupby.isEmpty());
        }

        this->sortByColumn(0, qtsortorder);
    }
}

void SKGTreeView::onRangeChanged()
{
    QScrollBar* scroll = qobject_cast<QScrollBar*>(sender());
    if ((stickH && scroll == horizontalScrollBar()) || (stickV && scroll == verticalScrollBar())) {
        scroll->setValue(scroll->maximum());
    }
}

void SKGTreeView::onActionTriggered(int action)
{
    QScrollBar* scroll = qobject_cast<QScrollBar*>(sender());
    if (scroll && action == QAbstractSlider::SliderToMaximum) {
        if (scroll == horizontalScrollBar()) stickH = true;
        if (scroll == verticalScrollBar()) stickV = true;
    } else {
        if (scroll == horizontalScrollBar()) stickH = false;
        if (scroll == verticalScrollBar()) stickV = false;
    }
}

void SKGTreeView::insertGlobalAction(const QString& iRegisteredAction)
{
    if (iRegisteredAction.isEmpty()) {
        QAction* sep = new QAction(this);
        sep->setSeparator(true);
        this->insertAction(0, sep);
    } else if (SKGMainPanel::getMainPanel()) {
        KAction* act = SKGMainPanel::getMainPanel()->getGlobalAction(iRegisteredAction);
        this->insertAction(0, act);
    }
}

void SKGTreeView::resizeColumnsToContentsDelayed()
{
    SKGTRACEIN(10, "SKGTreeView::resizeColumnsToContentsDelayed");
    m_timerDelayedResize.start(300);
}

void SKGTreeView::resizeColumnsToContents()
{
    SKGTRACEIN(10, "SKGTreeView::resizeColumnsToContents");
    int nb = header()->count();
    for (int i = nb - 1; i >= 0; --i)
        resizeColumnToContents(i);
}

void SKGTreeView::showHeaderMenu()
{
    showHeaderMenu(header()->mapFromGlobal(QCursor::pos()));
}

void SKGTreeView::showHeaderMenu(const QPoint& pos)
{
    if (m_headerMenu) m_headerMenu->popup(header()->mapToGlobal(pos));
}

void SKGTreeView::setDefaultSaveParameters(SKGDocument* iDocument, const QString& iParameterName)
{
    m_document = iDocument;
    m_parameterName = iParameterName;
}

void SKGTreeView::setupHeaderMenu()
{
    SKGTRACEIN(10, "SKGTreeView::setupHeaderMenu");
    if (m_model && m_model->isRefreshBlocked()) return;

    setCornerWidget(NULL);
    if (m_model && m_headerMenu) {
        //Corner button on tables to show the contextual menu
        QPushButton* btn = new QPushButton(this);
        btn->setIcon(KIcon("configure"));
        connect(btn, SIGNAL(clicked()), this, SLOT(showHeaderMenu()));
        setCornerWidget(btn);

        m_headerMenu->clear();

        //Processing
        QMenu* columns = m_headerMenu->addMenu(i18nc("Noun, Menu name", "Columns"));

        //Get current groupby column
        QMenu* groupbymenu = NULL;
        QActionGroup* groupby = NULL;
        if (!m_model->getWhereClause().contains("ORDER BY")) {
            groupbymenu = m_headerMenu->addMenu(i18nc("Noun, Menu name", "Group by"));
            groupby = new QActionGroup(groupbymenu);
            m_actGroupByNone = groupbymenu->addAction(i18nc("Noun, grouping option", "None"));
            if (m_actGroupByNone) {
                m_actGroupByNone->setIcon(KIcon("dialog-cancel"));
                m_actGroupByNone->setCheckable(true);
                m_actGroupByNone->setChecked(m_groupby == "");
                m_actGroupByNone->setData("");
                groupby->addAction(m_actGroupByNone);
            }
            if (m_proxyModel) {
                QAction* actSort = groupbymenu->addAction(i18nc("Noun, grouping option", "Sorted column"));
                if (actSort) {
                    actSort->setIcon(KIcon("view-sort-ascending"));
                    actSort->setCheckable(true);
                    actSort->setChecked(m_groupby == "#");
                    actSort->setData("#");
                    groupby->addAction(actSort);
                }
            }
            groupbymenu->addSeparator();
        }

        // Set right click menu
        if (m_model) {
            QList<SKGDocument::SKGModelTemplate> schemas = m_model->getSchemas();
            int nbSchemas = schemas.count();
            if (nbSchemas) {
                QMenu* viewAppearanceMenu = columns->addMenu(KIcon("view-file-columns"), i18nc("Noun, user action", "View appearance"));

                for (int i = 0; i < nbSchemas; ++i) {
                    SKGDocument::SKGModelTemplate schema = schemas.at(i);
                    QAction* act = viewAppearanceMenu->addAction(schema.name);
                    if (!schema.icon.isEmpty()) act->setIcon(KIcon(schema.icon));
                    act->setData(schema.schema);

                    connect(act, SIGNAL(triggered(bool)), this, SLOT(changeSchema()));
                }
            }
        }

        QAction* actResize = columns->addAction(KIcon("zoom-fit-width"), i18nc("Noun, user action", "Resize to content"));
        connect(actResize, SIGNAL(triggered(bool)), this, SLOT(resizeColumnsToContents()));

        m_actAutoResize = columns->addAction(i18nc("Noun, user action", "Auto resize"));
        m_actAutoResize->setCheckable(true);
        m_actAutoResize->setChecked(m_autoResize);
        connect(m_actAutoResize, SIGNAL(triggered(bool)), this, SLOT(switchAutoResize()));

        QAction* actAlternatingRowColors = m_headerMenu->addAction(i18nc("Noun, user action", "Alternate row colors"));
        if (actAlternatingRowColors) {
            actAlternatingRowColors->setCheckable(true);
            actAlternatingRowColors->setChecked(alternatingRowColors());
            connect(actAlternatingRowColors, SIGNAL(triggered(bool)), this, SLOT(setAlternatingRowColors(bool)));
        }

        if (m_document) {
            QAction* actDefault = m_headerMenu->addAction(KIcon("document-save"), i18nc("Noun, user action", "Save parameters"));
            connect(actDefault, SIGNAL(triggered(bool)), this, SLOT(saveDefaultClicked()));
        }

        columns->addSeparator();

        if (m_model) {
            //Build menus for columns
            QHeaderView* hHeader = header();
            int nbcol = hHeader->count();
            for (int i = 0; i < nbcol; ++i) {
                int idx = hHeader->logicalIndex(i);
                QString col = m_model->headerData(idx, Qt::Horizontal, Qt::UserRole).toString();
                QStringList values = col.split('|');

                if (!m_autoResizeDone) {
                    if (values.count() > 1) hHeader->setSectionHidden(idx, values.at(1) == "N");
                    if (values.count() > 2) {
                        int s = SKGServices::stringToInt(values.at(2));
                        if (s > 0) hHeader->resizeSection(idx, s);
                    }
                }

                //Column menu
                QAction* act = columns->addAction(values.at(0));
                if (act) {
                    act->setCheckable(true);
                    act->setChecked(!hHeader->isSectionHidden(idx));
                    act->setIcon(m_model->headerData(idx, Qt::Horizontal, Qt::DecorationRole).value<QIcon>());
                    act->setData(idx);
                    act->setEnabled(i > 0);

                    connect(act, SIGNAL(triggered(bool)), this, SLOT(showHideColumn()));

                    //Group by menu
                    QString att = m_model->getAttribute(idx);
                    if (act->isChecked() && groupbymenu && groupby) {
                        QAction* act2 = groupbymenu->addAction(values.at(0));
                        if (act2) {
                            act2->setCheckable(true);
                            act2->setChecked(att == m_groupby);
                            act2->setIcon(act->icon());
                            act2->setData(att);
                            groupby->addAction(act2);
                        }
                    }
                }
            }
            m_autoResizeDone = true;

            //Span first column
            if (m_proxyModel) {
                int grouped_rows_count = m_proxyModel->rowCount(rootIndex());
                for (int row = 0 ; row < grouped_rows_count ; row++) {
                    setFirstColumnSpanned(row, rootIndex(), !m_groupby.isEmpty());
                }
            }
        }
        if (groupby) connect(groupby, SIGNAL(triggered(QAction*)), this, SLOT(groupByChanged(QAction*)));
        m_headerMenu->addSeparator();

        QAction* actExport = m_headerMenu->addAction(KIcon("document-export"), i18nc("Noun, user action", "Export..."));
        connect(actExport, SIGNAL(triggered(bool)), this, SLOT(onExport()));

        if (m_autoResize) resizeColumnsToContentsDelayed();
    }
}

void SKGTreeView::setSelectionModel(QItemSelectionModel* selectionModel)
{
    if (this->selectionModel()) disconnect(this->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(onSelectionChanged()));
    QTreeView::setSelectionModel(selectionModel);
    if (selectionModel) connect(selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(onSelectionChanged()));
}

void SKGTreeView::onSelectionChanged()
{
    SKGObjectBase::SKGListSKGObjectBase selection;
    QItemSelectionModel* selModel = selectionModel();
    if (selModel) {
        if (m_model) {
            QModelIndexList indexes = selModel->selectedRows();
            foreach(const QModelIndex & index, indexes) {
                QModelIndex idxs = (m_proxyModel ? m_proxyModel->mapToSource(index) : index);
                SKGObjectBase obj = m_model->getObject(idxs);
                selection.push_back(obj);
            }
        }
    }

    if (selection != m_lastSelection) {
        m_lastSelection = selection;
        m_timerSelectionChanged.start(300);
    }
}

void SKGTreeView::saveDefaultClicked()
{
    if (m_document) {
        SKGError err;
        SKGBEGINTRANSACTION(*m_document, i18nc("Noun, name of the user action", "Save default parameters"), err);
        err = m_document->setParameter(m_parameterName, getState());
    }
}

void SKGTreeView::switchAutoResize()
{
    m_autoResize = m_actAutoResize->isChecked();
    header()->setResizeMode(m_autoResize ? QHeaderView::Fixed : QHeaderView::Interactive);
    if (m_autoResize) resizeColumnsToContentsDelayed();
    else {
        m_timerDelayedResize.stop();
        m_autoResizeDone = false;
    }
}

void SKGTreeView::groupByChanged(QAction* iAction)
{
    if (m_model && m_model->isRefreshBlocked()) return;

    if (iAction && m_model) {
        m_groupby = iAction->data().toString();;
        QString att = m_groupby;
        if (att == "#" && m_proxyModel) att = m_model->getAttribute(m_proxyModel->sortColumn());
        m_model->setGroupBy(att);
        m_model->refresh();

        bool treeMode = !m_model->getParentChildAttribute().isEmpty();
        setRootIsDecorated(treeMode && m_groupby.isEmpty());
        if (m_actExpandAll) m_actExpandAll->setVisible(treeMode || !m_groupby.isEmpty());
        if (m_actCollapseAll) m_actCollapseAll->setVisible(treeMode || !m_groupby.isEmpty());
    }
}

void SKGTreeView::onSortChanged(int iIndex, Qt::SortOrder iOrder)
{
    Q_UNUSED(iOrder);
    if (m_groupby == "#" && m_model) {
        m_model->setGroupBy(m_model->getAttribute(iIndex));
        m_model->refresh();
    }

    m_timerScrollSelection.start(300);
}

void SKGTreeView::showHideColumn()
{
    QAction* sender = static_cast<QAction*>(this->sender());
    if (sender && m_model) {
        QHeaderView* hHeader = header();

        int idx = sender->data().toInt();
        bool hidden = !hHeader->isSectionHidden(idx);
        hHeader->setSectionHidden(idx, hidden);

        m_model->setSupportedAttributes(getCurrentSchema());
        if (!hidden) resizeColumnToContents(idx);

        //If the group by attibute is hidden the the group is removed
        QString att = m_model->getAttribute(idx);
        QString groupBy = m_model->getGroupBy();
        if (hidden &&  att == groupBy) groupByChanged(m_actGroupByNone);
        else m_model->dataModified();
    }
}

void SKGTreeView::resetColumnsOrder()
{
    QHeaderView* hHeader = header();
    int nbcol = hHeader->count();
    for (int i = 0; i < nbcol; ++i) {
        int idx = hHeader->visualIndex(i);
        if (idx != i) hHeader->moveSection(idx, i);
    }
}

void SKGTreeView::changeSchema()
{
    QStringList list;

    QAction* sender = static_cast<QAction*>(this->sender());
    if (sender)  list = SKGServices::splitCSVLine(sender->data().toString(), ';');

    if (m_model) {
        //Reset column oder
        resetColumnsOrder();

        m_model->setSupportedAttributes(list);
        bool tmp = m_autoResizeDone;
        m_autoResizeDone = false;
        m_model->dataModified();
        m_autoResizeDone = tmp;
        header()->setSortIndicator(0, Qt::AscendingOrder);
    }
}

QStringList SKGTreeView::getCurrentSchema() const
{
    QStringList list;
    QHeaderView* hHeader = header();
    if (hHeader && m_model) {
        int nb = hHeader->count();
        if (nb) {
            QString att;
            for (int i = 0; i < nb; ++i) {
                int idx = hHeader->logicalIndex(i);
                att = m_model->getAttribute(idx);
                att += QString("|") % (hHeader->isSectionHidden(idx) ? "N" : "Y");
                att += QString("|") % SKGServices::intToString(hHeader->sectionSize(idx));

                list.push_back(att);
            }
        }
    }
    return list;
}
void SKGTreeView::setAlternatingRowColors(bool enable)
{
    QTreeView::setAlternatingRowColors(enable);
}

SKGObjectBase SKGTreeView::getFirstSelectedObject()
{
    return m_lastSelection.at(0);
}

SKGObjectBase::SKGListSKGObjectBase SKGTreeView::getSelectedObjects()
{
    return m_lastSelection;
}

int SKGTreeView::getNbSelectedObjects()
{
    return m_lastSelection.count();
}

void SKGTreeView::saveSelection()
{
    SKGTRACEIN(10, "SKGTreeView::saveSelection");

    m_selection.clear();

    SKGObjectBase::SKGListSKGObjectBase objs = getSelectedObjects();
    int nb = objs.count();
    //We save the selection only if not too big
    if (nb <= 100) {
        for (int i = 0; i < nb; ++i) {
            QString id = objs.at(i).getUniqueID();
            m_selection.push_back(id);
        }
    }
    SKGTRACEL(10) << m_selection.count() << " objects saved" << endl;
}

void SKGTreeView::selectObject(const QString& iUniqueID)
{
    SKGTRACEIN(10, "SKGTreeView::selectObject");
    QStringList tmp;
    tmp.push_back(iUniqueID);
    selectObjects(tmp, true);
}

void SKGTreeView::selectObjects(const QStringList& iUniqueIDs, bool iFocusOnFirstOne)
{
    SKGTRACEIN(10, "SKGTreeView::selectObjects");
    int nbset = 0;
    QItemSelectionModel* selModel = selectionModel();
    if (selModel) {
        bool previous = selModel->blockSignals(true);

        selModel->clearSelection();

        if (m_model) {
            //Get all indexes
            QList<QModelIndex> items;
            items.push_back(QModelIndex());
            for (int i = 0; i < items.count(); ++i) { //Dynamic size because the list is modified
                QModelIndex mi = items.at(i);
                int nbRows = m_model->rowCount(mi);
                for (int j = 0; j < nbRows; ++j) {
                    items.push_back(m_model->index(j, 0, mi));
                }
            }
            items.removeAt(0);

            int nbRows = items.count();
            if (nbRows) {
                //Expand nodes
                bool previousForThis = this->blockSignals(true);
                foreach(const QString & exp, m_expandedNodes) {
                    for (int i = 0; i < nbRows; ++i) {
                        QModelIndex index = items.at(i);
                        SKGObjectBase obj = m_model->getObject(index);
                        if (obj.getUniqueID() == exp) {
                            QModelIndex idxs = (m_proxyModel ? m_proxyModel->mapFromSource(index) : index);
                            setExpanded(idxs, true);
                            break;
                        }
                    }
                }
                this->blockSignals(previousForThis);

                //Set selection
                bool focusDone = false;
                foreach(const QString & sel, iUniqueIDs) {
                    for (int i = 0; i < nbRows; ++i) {
                        QModelIndex index = items.at(i);
                        SKGObjectBase obj = m_model->getObject(index);
                        if (obj.getUniqueID() == sel) {
                            QModelIndex idxs = (m_proxyModel ? m_proxyModel->mapFromSource(index) : index);
                            selModel->select(idxs, QItemSelectionModel::Select | QItemSelectionModel::Rows);
                            ++nbset;
                            if (iFocusOnFirstOne && !focusDone) {
                                scrollTo(idxs);
                                focusDone = true;
                            }
                            break;
                        }
                    }
                }
            }
        }
        selModel->blockSignals(previous);
    }

    SKGTRACEL(10) << iUniqueIDs.count() << " objects to select" << endl;
    SKGTRACEL(10) << nbset << " objects selected" << endl;

    onSelectionChanged();
}

void SKGTreeView::resetSelection()
{
    SKGTRACEIN(10, "SKGTreeView::resetSelection");

    selectObjects(m_selection);
}

void SKGTreeView::scroolOnSelection()
{
    SKGObjectBase::SKGListSKGObjectBase selection;
    QItemSelectionModel* selModel = selectionModel();
    if (selModel) {
        if (m_model) {
            QModelIndexList indexes = selModel->selectedRows();
            if (indexes.count()) scrollTo(indexes.at(0));
        }
    }
}

void SKGTreeView::onExpand(const QModelIndex& index)
{
    SKGTRACEIN(10, "SKGTreeView::onExpand");
    if (index.isValid() && m_model) {
        QModelIndex idxs = (m_proxyModel ? m_proxyModel->mapToSource(index) : index);

        SKGObjectBase obj = m_model->getObject(idxs);
        QString id = obj.getUniqueID();
        m_expandedNodes.push_back(id);
    }

    if (m_autoResize) resizeColumnsToContentsDelayed();
}

void SKGTreeView::expandAll()
{
    SKGTRACEIN(10, "SKGTreeView::onExpandAll");
    QTreeView::expandAll();

    if (m_autoResize) resizeColumnsToContentsDelayed();
}

void SKGTreeView::onCollapse(const QModelIndex& index)
{
    SKGTRACEIN(10, "SKGTreeView::onCollapse");
    if (index.isValid() && m_model) {
        QModelIndex idxs = (m_proxyModel ? m_proxyModel->mapToSource(index) : index);

        SKGObjectBase obj = m_model->getObject(idxs);

        QString id = obj.getUniqueID();
        m_expandedNodes.removeOne(id);
    }

    if (m_autoResize) resizeColumnsToContentsDelayed();
}

void SKGTreeView::onClick(const QModelIndex& index)
{
    SKGTRACEIN(10, "SKGTreeView::onClick");
    if (index.isValid()) {
        this->setExpanded(index, !this->isExpanded(index));
    }
}

void SKGTreeView::copy()
{
    QItemSelectionModel* selection = selectionModel();
    if (selection) {
        QModelIndexList indexes = selection->selectedIndexes();

        if (indexes.size() < 1) return;

        qSort(indexes.begin(), indexes.end());

        // You need a pair of indexes to find the row changes
        QModelIndex previous = indexes.first();
        indexes.removeFirst();
        QString header_text;
        bool header_done = false;
        QString selected_text;
        foreach(const QModelIndex & current, indexes) {
            selected_text.append(model()->data(previous).toString());
            if (!header_done) header_text.append(model()->headerData(previous.column(), Qt::Horizontal).toString());
            if (current.row() != previous.row()) {
                selected_text.append(QLatin1Char('\n'));
                header_done = true;
            } else {
                selected_text.append(QLatin1Char(';'));
                if (!header_done) header_text.append(QLatin1Char(';'));
            }
            previous = current;
        }

        // add last element
        selected_text.append(model()->data(previous).toString());
        selected_text.append(QLatin1Char('\n'));
        QApplication::clipboard()->setText(header_text + '\n' + selected_text);
    }
}

void SKGTreeView::setZoomPosition(int iZoomPosition)
{
    int newZoomPos = qMax(qMin(iZoomPosition, 10), -10);
    if (newZoomPos != zoomPosition() && m_fontOriginalPointSize + newZoomPos > 1) {
        QFont font = this->font();
        font.setPointSize(m_fontOriginalPointSize + newZoomPos);
        int iconSize = qMax(m_iconOriginalSize + newZoomPos, 1);

        this->setFont(font);
        this->setIconSize(QSize(iconSize, iconSize));
        header()->setIconSize(QSize(iconSize, iconSize));

        if (m_autoResize) resizeColumnsToContentsDelayed();

        Q_EMIT zoomChanged(newZoomPos);
    }
}

int SKGTreeView::zoomPosition() const
{
    return this->font().pointSize() - m_fontOriginalPointSize;
}

/**
 * Event filter
 * @param object object
 * @param event event
 * @return traited or not
 */
bool SKGTreeView::eventFilter(QObject* object, QEvent* event)
{
    QWheelEvent* e = dynamic_cast<QWheelEvent*>(event);
    if (m_textResizable && e && e->orientation() == Qt::Vertical && QApplication::keyboardModifiers() &Qt::ControlModifier) {
        int numDegrees = e->delta() / 8;
        int numTicks = numDegrees / 15;

        setZoomPosition(zoomPosition() + (numTicks > 0 ? 1 : -1));
        e->setAccepted(true);
        return true;
    }
    if (object == this) {
        QKeyEvent* kevent = dynamic_cast<QKeyEvent*>(event);
        if (kevent) {
            if (kevent->matches(QKeySequence::Copy) && this->state() != QAbstractItemView::EditingState) {
                copy();
                if (event) event->accept();
                return true; //stop the process
            }
        }
    }
    return QTreeView::eventFilter(object, event);
}

void SKGTreeView::mousePressEvent(QMouseEvent* event)
{
    if (event && event->button() == Qt::LeftButton && !(this->indexAt(event->pos()).isValid())) {
        Q_EMIT clickEmptyArea();
        clearSelection();
    }
    QTreeView::mousePressEvent(event);
}

bool SKGTreeView::isTextResizable() const
{
    return m_textResizable;
}

void SKGTreeView::setTextResizable(bool resizable)
{
    m_textResizable = resizable;
}

SKGStringListList SKGTreeView::getTable()
{
    //Build table
    SKGStringListList table;

    //Get header names
    if (m_model) {
        int nb = m_model->columnCount();
        QStringList cols;
        for (int i = 0; i < nb; ++i) {
            cols.append(m_model->headerData(i, Qt::Horizontal, Qt::UserRole).toString().split('|').at(0));
        }
        table.append(cols);

        //Get content
        int nb2 = m_model->rowCount();
        for (int i = 0; i < nb2; ++i) {
            QStringList row;
            for (int j = 0; j < nb; j++) {
                //We have to check the type for 214849
                SKGServices::AttributeType type = m_model->getAttributeType(j);
                QString display = m_model->data(m_model->index(i, j), type == SKGServices::FLOAT ?  Qt::DisplayRole : Qt::UserRole).toString();
                row.append(display);
            }
            table.append(row);
        }
    }
    return table;
}

void SKGTreeView::onExport()
{
    _SKGTRACEIN(10, "SKGTreeView::onExport");
    QString lastCodecUsed = QTextCodec::codecForLocale()->name();
    QString fileName = SKGMainPanel::getSaveFileName("kfiledialog:///IMPEXP", "text/csv text/plain image/svg+xml application/pdf" , this, QString(), &lastCodecUsed);
    if (fileName.isEmpty()) return;

    SKGError err;
    QString extension = QFileInfo(fileName).suffix().toUpper();
    if (extension == "CSV") {
        //Write file
        KSaveFile file(fileName);
        if (!file.open()) {
            err.setReturnCode(ERR_INVALIDARG);
            err.setMessage(i18nc("Error message",  "Save file '%1' failed" , fileName));
        } else {
            QTextStream out(&file);
            out.setCodec(lastCodecUsed.toAscii().constData());
            QStringList dump = SKGServices::tableToDump(getTable(), SKGServices::DUMP_CSV);
            int nbl = dump.count();
            for (int i = 0; i < nbl; ++i) {
                out << dump[i] << endl;
            }
        }

        //Close file
        file.finalize();
        file.close();
    } else if (extension == "PDF") {
        QImage image(this->size(), QImage::Format_ARGB32);
        QPainter painter(&image);
        this->render(&painter);
        painter.end();

        {
            QPrinter printer(QPrinter::HighResolution);
            printer.setOutputFileName(fileName);
            QPainter painter(&printer);

            QRect rect = painter.viewport();
            QSize size = image.size();
            size.scale(rect.size(), Qt::KeepAspectRatio);
            painter.setViewport(rect.x(), rect.y(), size.width(), size.height());
            painter.setWindow(image.rect());
            painter.drawImage(0, 0, image);
        }
    } else if (extension == "SVG") {
        QSvgGenerator generator;
        generator.setFileName(fileName);
        generator.setSize(QSize(200, 200));
        generator.setViewBox(QRect(0, 0, 200, 200));
        generator.setTitle(i18nc("Title of the content SVG export", "Skrooge SVG export"));
        generator.setDescription(i18nc("Description of the content SVG export", "A SVG drawing created by the Skrooge."));

        QPainter painter(&generator);
        this->render(&painter);
        painter.end();
    } else {
        //Write file
        KSaveFile file(fileName);
        if (!file.open()) {
            err.setReturnCode(ERR_INVALIDARG);
            err.setMessage(i18nc("Error message",  "Save file '%1' failed" , fileName));
        } else {
            QTextStream out(&file);
            out.setCodec(lastCodecUsed.toAscii().constData());
            QStringList dump = SKGServices::tableToDump(getTable(), SKGServices::DUMP_TEXT);
            int nbl = dump.count();
            for (int i = 0; i < nbl; ++i) {
                out << dump[i] << endl;
            }
        }

        //Close file
        file.finalize();
        file.close();
    }
    SKGMainPanel::displayErrorMessage(err);
    QDesktopServices::openUrl(QUrl(fileName));
}

void SKGTreeView::setModel(QAbstractItemModel* iModel)
{
    if (iModel != this->model()) {
        m_model = qobject_cast<SKGObjectModelBase*> (iModel);
        m_proxyModel = qobject_cast<QSortFilterProxyModel*> (iModel);
        if (m_proxyModel) m_model = (SKGObjectModelBase*) m_proxyModel->sourceModel();

        if (m_model) {
            connect(m_model, SIGNAL(afterReset()), this, SLOT(setupHeaderMenu()));
            connect(m_model, SIGNAL(afterReset()), this, SLOT(onSelectionChanged()));
        }
        QTreeView::setModel(iModel);
    }
}

#include "skgtreeview.moc"
