/**************************************************************************
 *  This file is part of QXmlEdit                                         *
 *  Copyright (C) 2011 by Luca Bellonda and individual contributors       *
 *    as indicated in the AUTHORS file                                    *
 *  lbellonda _at_ gmail.com                                              *
 *                                                                        *
 * This library is free software; you can redistribute it and/or          *
 * modify it under the terms of the GNU Library General Public            *
 * License as published by the Free Software Foundation; either           *
 * version 2 of the License, or (at your option) any later version.       *
 *                                                                        *
 * This library 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      *
 * Library General Public License for more details.                       *
 *                                                                        *
 * You should have received a copy of the GNU Library General Public      *
 * License along with this library; if not, write to the                  *
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,       *
 * Boston, MA  02110-1301  USA                                            *
 **************************************************************************/

#include "xmleditwidgetprivate.h"

#include <QXmlSchema>
#include <QXmlSchemaValidator>
#include "xmleditwidget.h"
#include "xsdeditor/xschemaloader.h"
#include "validatormessagehandler.h"
#include "validationresults.h"
#include "schemavalidator.h"
#include "elementitemdelegate.h"
#include "alloweditemsinsert.h"
#include "qattributedelegate.h"
#include "config.h"
#include "findtextparams.h"
#include "regola.h"
#include "utils.h"
#include "xsdeditor/xsdplugin.h"
#include "extraction/extractresults.h"
#include "xmleditwidgetprivate.h"
#include "xsdeditor/xsdcompare.h"
#include "modules/graph/nodesrelationsdialog.h"

void ShowTextInDialog(QWidget *parent, const QString &text);

#define CS_ELEMENT_TEXT "E"
#define CS_ELEMENT_TAG "T"
#define CS_ATTRIBUTE_NAME "N"
#define CS_ATTRIBUTE_VALUE "V"


XmlEditWidgetPrivate::XmlEditWidgetPrivate(XmlEditWidget *theOwner):
    styleActionGroup(this)
{
    p = theOwner ;
    _appData = &_defaultData;
    started = false ;
    internalStateOk = false;

    _uiDelegate = NULL ;
    _schemaRoot = NULL ;
    attrDelegate = NULL ;
    _displayMode = qxmledit::NORMAL;
    paintInfo.setExploreMode(false);
    isSlave = false ;

    styleMenu = NULL ;
    copyMenu = NULL ;

    application = NULL ;
    paintInfo.loadState();
    regola = newRegola();
    regola->assignCollectSizeDataFlag(paintInfo.showElementSize());
    clipBoardItem = NULL;
}

Regola *XmlEditWidgetPrivate::newRegola()
{
    Regola *newRule = new Regola();
    newRule->setPaintInfo(&paintInfo);
    connect(newRule, SIGNAL(wasModified()), this, SLOT(regolaIsModified()));
    connect(newRule, SIGNAL(undoStateChanged()), this, SLOT(regolaUndoChanged()));
    connect(newRule, SIGNAL(docTypeChanged(const QString &)), this, SLOT(docTypeChanged(const QString &)));
    return newRule ;
}

void XmlEditWidgetPrivate::secondStepConstructor()
{
    internalStateOk = finishSetUpUi();
    if(!internalStateOk) {
        Utils::error(tr("Error preparing user interface."));
    }
    init();
    loadSearchSettings();
    p->setAcceptDrops(true);

    connect(p->ui->treeWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, SLOT(elementDoubleClicked(QTreeWidgetItem *, int)));

    started = true ;
    internalStateOk = true ;

}

XmlEditWidgetPrivate::~XmlEditWidgetPrivate()
{
    resetStyleMenu();
    deleteRegola();
    cleanClipBoard();
    deleteSchema();
}


bool XmlEditWidgetPrivate::isReady()
{
    return started && internalStateOk;
}

void XmlEditWidgetPrivate::deleteSchema()
{
    if(NULL != _schemaRoot) {
//        disconnect(_schemaRoot, SIGNAL(loadComplete(XSDSchema*)), this, SLOT(schemaLoadComplete(XSDSchema*, const bool)));
        delete _schemaRoot ;
        _schemaRoot = NULL ;
    }
}

/*
//autotest
void XmlEditWidgetPrivate::autoTest()
{
#ifdef  _DEBUG_BALSAMIQ
    void testBALSAMIQ(QWidget * parent);
    this->show();
    testBALSAMIQ(this);
#endif
}
*/


void XmlEditWidgetPrivate::setData(QApplication *newApplication, QXmlEditData *newData, const bool newIsSlave, UIDelegate *newUiDelegate)
{
    application = newApplication ;
    if(NULL != newData) {
        _appData = newData ;
    }
    isSlave = newIsSlave;
    _uiDelegate = newUiDelegate ;
    init();
}

bool XmlEditWidgetPrivate::init()
{
    p->ui->ok->setEnabled(isSlave);
    p->ui->ok->setVisible(isSlave);

    p->ui->cdataElement->setEnabled(isSlave);
    p->ui->cdataElement->setVisible(isSlave);

    p->ui->cancel->setEnabled(isSlave);
    p->ui->cancel->setVisible(isSlave);

    return internalStateOk  ;
}

/*
//autotest
void XmlEditWidgetPrivate::autoTest()
{
#ifdef  _DEBUG_BALSAMIQ
    void testBALSAMIQ(QWidget * parent);
    this->show();
    testBALSAMIQ(this);
#endif
}
*/


void XmlEditWidgetPrivate::deleteRegola()
{
    if(NULL != regola) {
        disconnect(regola, SIGNAL(wasModified()), this, SLOT(regolaIsModified()));
        disconnect(regola, SIGNAL(undoStateChanged()), this, SLOT(regolaUndoChanged()));
        disconnect(regola, SIGNAL(docTypeChanged(const QString &)), this, SLOT(docTypeChanged(const QString &)));
        delete regola;
        regola = NULL;
    }
}

void XmlEditWidgetPrivate::startUIState()
{
    //p->ui->actionPaste->setEnabled(false);
    //p->ui->actionResizeToContents->setEnabled(true);

    computeSelectionState();
    showNavigationBox();
}

qxmledit::EDisplayMode XmlEditWidgetPrivate::displayMode()
{
    return _displayMode ;
}

bool XmlEditWidgetPrivate::finishSetUpUi()
{
    connect(p->ui->ok, SIGNAL(clicked()), this, SLOT(on_ok_clicked()));
    connect(p->ui->cancel, SIGNAL(clicked()), this, SLOT(on_cancel_clicked()));
    connect(p->ui->moveUp, SIGNAL(clicked()), this, SLOT(on_moveUp_clicked()));
    connect(p->ui->moveDown, SIGNAL(clicked()), this, SLOT(on_moveDown_clicked()));
    connect(p->ui->addChild, SIGNAL(clicked()), this, SLOT(on_addChild_clicked()));
    connect(p->ui->addBrother, SIGNAL(clicked()), this, SLOT(on_addBrother_clicked()));
    connect(p->ui->editItem, SIGNAL(clicked()), this, SLOT(on_editItem_clicked()));
    connect(p->ui->deleteItem, SIGNAL(clicked()), this, SLOT(on_deleteItem_clicked()));
    connect(p->ui->closeSearchPanel, SIGNAL(clicked()), this, SLOT(on_closeSearchPanel_clicked()));
    connect(p->ui->viewAsXsdCmd, SIGNAL(clicked()), this, SLOT(on_viewAsXsdCmd_clicked()));
    connect(p->ui->treeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(on_treeWidget_itemSelectionChanged()));

    // connect to search widget. In the original code these actions were inseted into the UI
    connect(p->ui->findButton, SIGNAL(clicked()), this, SLOT(findText()));
    connect(p->ui->clearText, SIGNAL(clicked()), this, SLOT(clearSearchText()));
    connect(p->ui->counting, SIGNAL(clicked()), this, SLOT(countTextOccurrences()));

    // turn off autoscroll
    p->ui->treeWidget->setAutoScroll(_appData->isAutoscroll());

    connect(p->ui->navigation, SIGNAL(navigateTo(const int)), this, SLOT(navigateToPage(const int)));

    p->ui->searchEditBox->installEventFilter(p);

    mainFontSize = p->ui->treeWidget->font().pointSize();
    p->ui->horizontalLowLayout->setAlignment(Qt::AlignLeft);

    p->ui->testNext->setVisible(false);
    p->ui->testPrev->setVisible(false);

    resetTree();
    startUIState();
    p->ui->treeWidget->setAlternatingRowColors(true);

    p->ui->treeWidget->setUniformRowHeights(paintInfo.compactView());

    p->ui->searchGroup->setVisible(false);
    p->ui->searchLocation->insertItem(0, tr("All"), (int)FindTextParams::FIND_ALL);
    p->ui->searchLocation->insertItem(1, tr("Element Tag"), (int)FindTextParams::FIND_TAG);
    p->ui->searchLocation->insertItem(2, tr("Attribute names"), (int)FindTextParams::FIND_ATTRIBUTE_NAME);
    p->ui->searchLocation->insertItem(3, tr("Attribute values"), (int)FindTextParams::FIND_ATTRIBUTE_VALUE);
    p->ui->searchLocation->insertItem(4, tr("Text"), (int)FindTextParams::FIND_TEXT);
    p->ui->searchLocation->setCurrentIndex(0);

    p->ui->treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(p->ui->treeWidget, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(treeContextMenu(const QPoint &)));
    //TODO connect( p->ui->searchLocation, SIGNAL(currentIndexChanged(int)), this, SLOT(searchTypeChanged(int)) );
    connect(p->ui->searchEditBox, SIGNAL(textChanged(const QString &)), this, SLOT(searchTextChanged(const QString &)));
    p->ui->clearText->setEnabled(false);

    /***************************************************************
    p->ui->ok->setEnabled(isSlave);
    p->ui->ok->setVisible(isSlave);
    */
    p->ui->cdataElement->setEnabled(isSlave);
    p->ui->cdataElement->setVisible(isSlave);
    /*
    p->ui->cancel->setEnabled(isSlave);
    p->ui->cancel->setVisible(isSlave);
    ***************************************************************/
    bool isNavigation = (_displayMode == qxmledit::SCAN);
    p->ui->navigation->setVisible(isNavigation);
    p->ui->navigation->setEnabled(isNavigation);
    p->ui->docTypeLabel->setVisible(false);

    VStyle *style = loadStyleMenu();
    setNewStyle(style);

    bool isOk = true;

    if(!setUpDelegates()) {
        isOk = false ;
    }
    return isOk;
}

bool XmlEditWidgetPrivate::setUpDelegates()
{
    bool isOk = true ;
    QAbstractItemDelegate *oldDelegate = p->ui->treeWidget->itemDelegate();
    if(NULL != oldDelegate) {
        ElementItemDelegate *newDelegate = new ElementItemDelegate(oldDelegate, this);
        if(NULL == newDelegate) {
            Utils::error(tr("Unable to create graphic elements"));
            return false ;
        }
        p->ui->treeWidget->setItemDelegateForColumn(0, newDelegate);
    }
    // TODO: column swap?
    if(Config::getBool(Config::KEY_VIEW_ITEMRENDERERATTR, true)) {
        attrDelegate = new QAttributeDelegate(this);
        if(NULL != attrDelegate) {
            p->ui->treeWidget->setItemDelegateForColumn(paintInfo.columnForAttributes, attrDelegate);
            paintInfo.setAttributesHTML(true);
        } else {
            isOk = false ;
        }
    }
    return isOk ;
}

void XmlEditWidgetPrivate::onCopySpecial()
{
    QAction *action = qobject_cast<QAction *>(sender());
    if(NULL == action) {
        return ;
    }
    Element *element = getSelectedItem();
    if(NULL == element) {
        return ;
    }
    QClipboard *clipboard = QApplication::clipboard();
    QString tag = action->data().toString();
    if(tag == CS_ELEMENT_TEXT) {
        clipboard->setText(element->getAsText());
    } else if(tag == CS_ELEMENT_TAG) {
        clipboard->setText(element->tag());
    } else if(tag.startsWith(CS_ATTRIBUTE_NAME)) {
        clipboard->setText(tag.right(tag.length() - 1));
    } else if(tag.startsWith(CS_ATTRIBUTE_VALUE)) {
        Attribute *attribute = element->getAttribute(tag.right(tag.length() - 1));
        if(NULL != attribute) {
            clipboard->setText(attribute->value);
        }
    }
}

void XmlEditWidgetPrivate::buildCopyMenu()
{
    Element *element = getSelectedItem();
    resetCopyMenu();
    if(NULL == element) {
        return ;
    }
    copyMenu = new QMenu();

    QAction *elementTextAction = createAnAction(copyMenu, QString(tr("Copy Element Text '%1'")).arg(element->tag()), CS_ELEMENT_TEXT, tr("Copy Element Text"));
    connect(elementTextAction , SIGNAL(triggered()), this, SLOT(onCopySpecial()));
    copyMenu->addAction(elementTextAction);
    QAction *elementTagAction = createAnAction(copyMenu, QString(tr("Copy Element Tag <%1>")).arg(element->tag()), CS_ELEMENT_TAG, tr("Copy Element Tag"));
    connect(elementTagAction , SIGNAL(triggered()), this, SLOT(onCopySpecial()));
    copyMenu->addAction(elementTagAction);
    QList<Attribute*>attributes = element->getAttributesList();
    foreach(Attribute * attribute, attributes) {
        QAction *attributeNameAction = createAnAction(copyMenu, QString(tr("Copy Attribute Name '%1'")).arg(attribute->name), CS_ATTRIBUTE_NAME + attribute->name, tr("Copy Attribute Name"));
        connect(attributeNameAction , SIGNAL(triggered()), this, SLOT(onCopySpecial()));
        copyMenu->addAction(attributeNameAction);
        QAction *attributeValueAction = createAnAction(copyMenu, QString(tr("Copy Attribute Value ='%1'")).arg(attribute->name), CS_ATTRIBUTE_VALUE + attribute->name, tr("Copy Attribute Value"));
        connect(attributeValueAction , SIGNAL(triggered()), this, SLOT(onCopySpecial()));
        copyMenu->addAction(attributeValueAction);

    }
    p->ui->copySpecial->setMenu(copyMenu);
    p->ui->copySpecial->setPopupMode(QToolButton::InstantPopup);
}

// load style menu: NEED data pointer
VStyle* XmlEditWidgetPrivate::loadStyleMenu()
{
    VStyle* selectedStyle = NULL;
    QString selected = Config::getString(Config::KEY_VIEW_STYLE, _appData->predefinedStyleName());
    resetStyleMenu();
    styleMenu = new QMenu();
    QAction *defaultAction = createStyleAction(styleMenu, tr("== No style =="), "", tr("don't use a style"));
    connect(defaultAction, SIGNAL(triggered()), this, SLOT(onStyleChanged()));
    if(selected == "") {
        defaultAction->setChecked(true);
        selectedStyle = NULL ;
    }
    styleMenu->addAction(defaultAction);
    styleActionGroup.addAction(defaultAction);
    foreach(VStyle * style, _appData->getStyles()) {
        QString name = style->name();
        QAction *action = createStyleAction(styleMenu, name, name, "");
        connect(action, SIGNAL(triggered()), this, SLOT(onStyleChanged()));
        styleMenu->addAction(action);
        styleActionGroup.addAction(action);
        if(selected == name) {
            action->setChecked(true);
            selectedStyle = style;
        }
    }

    p->ui->styleButton->setMenu(styleMenu);
    p->ui->styleButton->setPopupMode(QToolButton::InstantPopup);

    return selectedStyle ;
}

void XmlEditWidgetPrivate::resetStyleMenu()
{
    p->ui->styleButton->setMenu(NULL);
    if(styleMenu != NULL) {
        foreach(QAction * action, styleMenu->actions()) {
            styleActionGroup.removeAction(action);
        }
        styleMenu->deleteLater();
    }
    styleMenu = NULL ;
}

void XmlEditWidgetPrivate::resetCopyMenu()
{
    p->ui->copySpecial->setMenu(NULL);
    if(copyMenu != NULL) {
        copyMenu->deleteLater();
    }
    copyMenu = NULL ;
}


QAction *XmlEditWidgetPrivate::createStyleAction(QMenu *menu, const QString &label, const QString &tag, const QString &tooltip)
{
    QAction *action = new QAction(menu);
    action->setText(label);
    action->setData(tag);
    action->setToolTip(tooltip);
    action->setCheckable(true);
    return action ;
    //TODO -14-
}

QAction *XmlEditWidgetPrivate::createAnAction(QMenu *menu, const QString &label, const QString &tag, const QString &tooltip)
{
    QAction *action = new QAction(menu);
    action->setText(label);
    action->setData(tag);
    action->setToolTip(tooltip);
    return action ;
    //TODO -14-
}

void XmlEditWidgetPrivate::onStyleChanged()
{
    QAction *action = qobject_cast<QAction *>(sender());
    if(NULL == action) {
        return ;
    }
    QString tag = action->data().toString();
    VStyle * newStyle = _appData->getStyle(tag);
    setNewStyle(newStyle);
}

void XmlEditWidgetPrivate::setNewStyle(VStyle *newStyle)
{
    if(NULL == newStyle) {
        Config::saveString(Config::KEY_VIEW_STYLE, "");
        VStyle *style = paintInfo.currentStyle();
        if(NULL != style) {
            style->deactivate();
        }
        paintInfo.setCurrentStyle(NULL) ;
        repaint();
        return ;
    }
    if(newStyle != paintInfo.currentStyle()) {
        Config::saveString(Config::KEY_VIEW_STYLE, newStyle->name());
        if(!newStyle->activate(p->ui->treeWidget->font(), paintInfo.zoom())) {
            Utils::error(tr("Error activating style"));
        }
        paintInfo.setCurrentStyle(newStyle) ;
        repaint();
    }
}

void XmlEditWidgetPrivate::repaint()
{
    if(NULL != regola) {
        p->ui->treeWidget->setUpdatesEnabled(false);
        regola->redisplay();
        p->ui->treeWidget->setUpdatesEnabled(true);
    }
}

PaintInfo* XmlEditWidgetPrivate::getPaintInfo()
{
    return &paintInfo;
}

void XmlEditWidgetPrivate::display()
{
    if(NULL != regola) {
        p->ui->treeWidget->setUpdatesEnabled(false);
        regola->caricaValori(p->ui->treeWidget);
        p->ui->treeWidget->setUpdatesEnabled(true);
    }
}

void XmlEditWidgetPrivate::treeContextMenu(const QPoint& position)
{
    p->emitTreeContextMenuRequested(position);
}

// Solo il salva come...
void XmlEditWidgetPrivate::regolaIsModified()
{
    //TRACEQ(QString("XmlEditWidgetPrivate::regolaIsModified():%1").arg(regola->isModified()));
    p->emitDocumentIsModified(regola->isModified());
}

//cambia la sel clipb.
void XmlEditWidgetPrivate::setClipBoardActionsState()
{
    bool isAction = false;
    if(NULL != clipBoardItem) {
        isAction = true ;
    }
    p->emitSignalSetClipBoardActionsState(isAction && isActionMode());
}

void XmlEditWidgetPrivate::on_treeWidget_itemSelectionChanged()
{
    regola->handleSelection(getSelectedItem());
    computeSelectionState();
}


void XmlEditWidgetPrivate::computeSelectionState()
{
    bool isSomeItemSelected = false;
    bool selectSpecials = false;
    bool selectSpecialsUp = false;
    bool selectSpecialsDown = false;
    //bool isNormalViewState = true;
    bool canAddChild = false;
    //bool isShownAsBase64 = false ;
    bool isExplore = (_displayMode != qxmledit::NORMAL) && (_displayMode != qxmledit::SCAN) ;
    int numberSelected = p->ui->treeWidget->selectedItems().size();
    if(numberSelected > 0) {
        selectSpecials = true ;
        selectSpecialsUp = true ;
        selectSpecialsDown = true ;
        Element *element = Element::fromItemData(p->ui->treeWidget->selectedItems().at(0));
        selectSpecialsUp = !element->isFirstChild();
        selectSpecialsDown = !element->isLastChild();
        if(element->getType() == Element::ET_ELEMENT)
            canAddChild = true;
        //isNormalViewState = element->isNormalViewState();
        isSomeItemSelected = true ;
        //isShownAsBase64 = element->isShownBase64();
        //if il primo item
        //disabilita accoda, mvup e down
        //altrimenti se il selected si trova al primo o ultimo posto della catena deselezmv up o down
    } else {
        if(regola->isEmpty(true))
            canAddChild = true;
    }
    p->ui->addChild->setEnabled(canAddChild && !isExplore);
    p->ui->addBrother->setEnabled(selectSpecials && !isExplore);
    p->ui->deleteItem->setEnabled(isSomeItemSelected && !isExplore);
    p->ui->editItem->setEnabled(isSomeItemSelected && !isExplore);
    p->ui->moveUp->setEnabled(selectSpecialsUp && !isExplore);
    p->ui->moveDown->setEnabled(selectSpecialsDown && !isExplore);
    p->ui->copySpecial->setEnabled(isSomeItemSelected);
    if(isSomeItemSelected) {
        buildCopyMenu();
    }
    // TODO: test
    //p->ui->testNext->setEnabled((NULL!=regola)?regola->nextBookmark()>=0:false);
    //p->ui->testPrev->setEnabled((NULL!=regola)?regola->previousBookmark()>=0:false);

    p->ui->viewAsXsdCmd->setEnabled(isValidXsd() && !isExplore);
    p->emitReevaluateSelectionState();
}

//void XmlEditWidgetPrivate::enableZoom()
//{
//    p->ui->actionZoomIn->setEnabled(paintInfo.canZoomIn());
//    p->ui->actionZoomOut->setEnabled(paintInfo.canZoomOut());
//}

Element *XmlEditWidgetPrivate::getSelectedItem()
{
    if((NULL != p->ui->treeWidget) && (p->ui->treeWidget->selectedItems().size() > 0)) {
        Element *element = Element::fromItemData(p->ui->treeWidget->selectedItems().at(0));
        return element;
    }
    return NULL;
}

void XmlEditWidgetPrivate::resetTree()
{
    p->ui->treeWidget->setColumnCount(6);
    p->ui->treeWidget->clear();
    QStringList headers;
    headers << tr("Elements");
    headers << tr("#Ch.");
    headers << tr("Size");
    headers << tr("Attributes");
    headers << tr("Text");
    headers << tr("Decoded text");
    p->ui->treeWidget->setHeaderLabels(headers);
    setDataColumnTitle(paintInfo.compactView());
    calcColumnState();
}

void XmlEditWidgetPrivate::calcColumnState()
{
    bool isShowSize = paintInfo.showElementSize();
    if(isShowSize) {
        p->ui->treeWidget->showColumn(paintInfo.columnForChildren);
        p->ui->treeWidget->showColumn(paintInfo.columnForSize);
    } else {
        p->ui->treeWidget->hideColumn(paintInfo.columnForChildren);
        p->ui->treeWidget->hideColumn(paintInfo.columnForSize);
    }
    //TODO
    //TODOif( isShowSize ) {
    //TODO    pTree->setAColAlignment();
    //TODO}
}


void XmlEditWidgetPrivate::onActionNewFromClipboard()
{
    const QClipboard *clipBoard = QApplication::clipboard();
    const QMimeData *mimeData = clipBoard->mimeData();
    if(mimeData->hasText()) {
        loadText(mimeData->text());
    }
}

void XmlEditWidgetPrivate::onActionResizeToContents()
{
    resizeTreeColumns();
}

void XmlEditWidgetPrivate::setEncodingLabelVisible(bool isShow)
{
    p->ui->encoding->setVisible(isShow);
}

bool XmlEditWidgetPrivate::isEncodingLabelVisible()
{
    return p->ui->encoding->isVisible();
}

void XmlEditWidgetPrivate::setDocTypeVisible(bool isShow)
{
    p->ui->docTypeLabel->setVisible(isShow);
}

bool XmlEditWidgetPrivate::isDocTypeVisible()
{
    return p->ui->docTypeLabel->isVisible();
}

void XmlEditWidgetPrivate::setViewAsXSDButtonVisible(bool isShow)
{
    if(p->ui->viewAsXsdCmd) {
        p->ui->viewAsXsdCmd->setVisible(isShow);
    }
}

bool XmlEditWidgetPrivate::isViewAsXSDButtonVisible()
{
    return p->ui->viewAsXsdCmd->isVisible();
}

void XmlEditWidgetPrivate::setMoveButtonsVisible(bool isShow)
{
    p->ui->moveDown->setVisible(isShow);
    p->ui->moveUp->setVisible(isShow);
}

bool XmlEditWidgetPrivate::areMoveButtonsVisible()
{
    return p->ui->moveDown->isVisible();
}

ulong XmlEditWidgetPrivate::versionNumber()
{
    return VERSION_NUMBER ;
}

void XmlEditWidgetPrivate::resizeTreeColumns()
{
    p->ui->treeWidget->setUpdatesEnabled(false);
    int columns = p->ui->treeWidget->columnCount();
    for(int i = 0 ; i < columns ; i ++) {
        p->ui->treeWidget->resizeColumnToContents(i);
    }
    p->ui->treeWidget->setUpdatesEnabled(true);
}

bool XmlEditWidgetPrivate::editElement(QTreeWidgetItem *item)
{
    /*if(!isEditState()) { TODO
        return false;
    }*/
    if(NULL == regola) {
        errorNoRule();
        return false ;
    }
    regola->editElement(p, item, _uiDelegate);
    bool result = false;
    computeSelectionState();
    return result ;
}

void XmlEditWidgetPrivate::elementDoubleClicked(QTreeWidgetItem * item, int /*column*/)
{
    if(isActionMode()) {
        editElement(item);
    }
}

QTreeWidgetItem *XmlEditWidgetPrivate::getSelItem()
{
    QList<QTreeWidgetItem *>selItems =  p->ui->treeWidget->selectedItems();
    if(selItems.count() < 1)
        return NULL;
    return selItems.at(0);
}

void XmlEditWidgetPrivate::on_editItem_clicked()
{
    editItem();
}

void XmlEditWidgetPrivate::editItem()
{
    if(!isActionMode()) {
        return ;
    }
    QTreeWidgetItem *itemSel = getSelItem();
    if(NULL == itemSel) {
        Utils::errorNoSel(p);
        return;
    }
    editElement(itemSel) ;
}


void XmlEditWidgetPrivate::on_addChild_clicked()
{
    addChild();
}

void XmlEditWidgetPrivate::addChild()
{
    if(!isActionMode()) {
        return ;
    }
    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    regola->addChild(p, p->ui->treeWidget);
}

// Can't add a brother to the first child (XML)
void XmlEditWidgetPrivate::addBrother()
{
    if(!isActionMode()) {
        return ;
    }
    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    regola->addBrother(p, p->ui->treeWidget);
}

void XmlEditWidgetPrivate::on_addBrother_clicked()
{
    addBrother();
}

void XmlEditWidgetPrivate::errorNoRule()
{
    error(tr("No main structure"));
}

void XmlEditWidgetPrivate::onActionAddChildElement()
{
    addChild();
}

void XmlEditWidgetPrivate::onActionAppendChildElement()
{
    addBrother();
}
void XmlEditWidgetPrivate::onActionEdit()
{
    editItem();
}

void XmlEditWidgetPrivate::onActionDelete()
{
    deleteItem();
}
void XmlEditWidgetPrivate::onActionMoveUp()
{
    moveUp();
}

void XmlEditWidgetPrivate::onActionMoveDown()
{
    moveDown();
}

void XmlEditWidgetPrivate::deleteItem()
{
    if(!isActionMode()) {
        return ;
    }
    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    // Regola vuota, aggiungo il primo ed ultimo
    QTreeWidgetItem *currItem = getSelItem();
    if(NULL == currItem) {
        Utils::errorNoSel(p);
        return;
    }
    if(!Utils::askYN(p,
                     tr("This operation will destroy the element and all his children. Do you really want to continue?"))) {
        return ;
    }
    regola->deleteElement(Element::fromItemData(currItem));
}

void XmlEditWidgetPrivate::on_deleteItem_clicked()
{
    deleteItem();
}

void XmlEditWidgetPrivate::on_moveUp_clicked()
{
    moveUp();
}

// Move up in parent containment.
void XmlEditWidgetPrivate::moveUp()
{
    if(!isActionMode()) {
        return ;
    }
    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    // Regola vuota, aggiungo il primo ed ultimo
    QTreeWidgetItem *currItem = getSelItem();
    if(NULL == currItem) {
        Utils::errorNoSel(p);
        return;
    }
    Element *element = Element::fromItemData(currItem);
    if(NULL == element) {
        return ;
    }
    regola->moveUp(element);
}

// Move up in parent containment.
void XmlEditWidgetPrivate::moveDown()
{
    if(!isActionMode()) {
        return ;
    }
    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    QTreeWidgetItem *currItem = getSelItem();
    if(NULL == currItem) {
        Utils::errorNoSel(p);
        return;
    }
    Element *element = Element::fromItemData(currItem);
    if(NULL == element) {
        return ;
    }
    regola->moveDown(element);
}

void XmlEditWidgetPrivate::on_moveDown_clicked()
{
    moveDown();
}

void XmlEditWidgetPrivate::cleanClipBoard()
{
    if(clipBoardItem != NULL) {
        delete clipBoardItem;
    }
    clipBoardItem = NULL ;
}

void XmlEditWidgetPrivate::setClipBoardItem(Element *pElement)
{
    clipBoardItem = pElement ;
}

void XmlEditWidgetPrivate::onActionCut()
{
    if(!isActionMode()) {
        return ;
    }

    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    QTreeWidgetItem *currItem = getSelItem();
    if(NULL == currItem) {
        Utils::errorNoSel(p);
        return;
    }
    setClipBoardItem(Element::fromItemData(currItem)->cutToClipboard());
    regola->setModified(true);
    setClipBoardActionsState();
}

void XmlEditWidgetPrivate::onActionCopy()
{
    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    QTreeWidgetItem *currItem = getSelItem();
    if(NULL == currItem) {
        Utils::errorNoSel(p);
        return;
    }
    Element *element = Element::fromItemData(currItem);
    setClipBoardItem(element->copyToClipboard());

    QDomDocument    document;
    if(element->generateDom(document, document)) {
        QString clipboardText = document.toString(4);
        QClipboard *clipboard = QApplication::clipboard();
        clipboard->setText(clipboardText);
    }
    setClipBoardActionsState();
}

void XmlEditWidgetPrivate::onActionPaste()
{
    if(isActionMode()) {
        regola->paste(p, p->ui->treeWidget, getClipBoardItem());
    }
}

Element* XmlEditWidgetPrivate::getClipBoardItem() const
{
    return clipBoardItem;
}


void XmlEditWidgetPrivate::onActionExpandAll()
{
    if(regola != NULL)
        regola->expand(p->ui->treeWidget);
}

void XmlEditWidgetPrivate::onActionShowAttrLine(const bool state)
{
    paintInfo.setOneAttrPerLine(state);
    repaint();
}

void XmlEditWidgetPrivate::onActionAddComment()
{
    if(!isActionMode()) {
        return ;
    }

    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    regola->addComment(p, p->ui->treeWidget);
}


void XmlEditWidgetPrivate::onActionAppendComment()
{
    if(!isActionMode()) {
        return ;
    }

    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    regola->appendComment(p, p->ui->treeWidget);
}

void XmlEditWidgetPrivate::onActionAppendProcessingInstruction()
{
    if(!isActionMode()) {
        return ;
    }

    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    regola->appendProcessingInstruction(p, p->ui->treeWidget);
}

void XmlEditWidgetPrivate::onActionAddProcessingInstruction()
{
    if(!isActionMode()) {
        return ;
    }

    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    regola->addProcessingInstruction(p, p->ui->treeWidget);
}

/* TODO: I dont think it is relevant anymore
void XmlEditWidgetPrivate::dragEnterEvent(QDragEnterEvent *event)
{
    if(event->mimeData()->hasFormat("text/uri-list"))
        event->acceptProposedAction();
}
*/

/*
void XmlEditWidgetPrivate::dropEvent(QDropEvent *event)
{
    if(event->mimeData()->hasFormat("text/uri-list")) {
        QString filePath = "" ;
        event->acceptProposedAction();
        if(event->mimeData()->hasUrls()) {
            foreach(QUrl url, event->mimeData()->urls()) {
                filePath = url.toLocalFile();
                break;
            }
        }
        if(filePath.length() > 0) {
            if(!XmlEditWidgetPrivate::checkAbandonChanges()) {
                event->ignore();
                return ;
            }
            loadFile(filePath);
        }
        event->acceptProposedAction();
    }
}
*/

void XmlEditWidgetPrivate::onActionFind()
{
    p->ui->searchGroup->setVisible(true);
    p->ui->searchEditBox->setFocus(Qt::OtherFocusReason);
}

void XmlEditWidgetPrivate::on_closeSearchPanel_clicked()
{
    if(NULL != regola) {
        regola->unhiliteAll();
    }
    p->ui->searchGroup->setVisible(false);
    p->setFocus(Qt::OtherFocusReason);
}

void XmlEditWidgetPrivate::findText()
{
    findTextOperation(true);
}

void XmlEditWidgetPrivate::countTextOccurrences()
{
    findTextOperation(false);
}

void XmlEditWidgetPrivate::findTextOperation(const bool isFindOrCount)
{
    if(NULL != regola) {
        p->setEnabled(false);
        p->ui->treeWidget->setUpdatesEnabled(false);
        Utils::showWaitCursor();
        FindTextParams::EFindTarget target = FindTextParams::FIND_ALL;
        int itemIndex = p->ui->searchLocation->currentIndex();
        if(itemIndex >= 0) {
            int trg = p->ui->searchLocation->itemData(itemIndex).toInt();
            target = (FindTextParams::EFindTarget) trg ;
        }
        FindTextParams findArgs(p->ui->searchEditBox->text(), !isFindOrCount, p->ui->isHiglightAll->isChecked(), p->ui->isMatchExactValue->isChecked(),
                                p->ui->isCaseSensitive->isChecked(), p->ui->isOnlyChildren->isChecked(), target,
                                (isFindOrCount ? p->ui->selectionToBookmarks->isChecked() : false),
                                (isFindOrCount ? p->ui->closeUnrelated->isChecked() : false),
                                p->ui->showSize->isChecked(), p->ui->searchScope->text());
        bool isErrorShown = false;
        bool isError = false;
        if(!findArgs.checkParams(isErrorShown)) {
            isError = true ;
            if(!isErrorShown) {
                Utils::error(tr("Impossible start a search; please, check parameters."));
            }
        }
        if(!isError) {
            saveSearchSettings(findArgs);
            regola->unhiliteAll();
            findArgs.start();
            regola->findText(findArgs, getSelectedItem());
            int occurrences = findArgs.getOccurrences();
            QString message ;
            if(occurrences > 0) {
                if(occurrences > 1) {
                    message = QString(tr("Found %1 occurrences.")).arg(occurrences) ;
                } else {
                    message = tr("Found 1 occurrence.") ;
                }
            } else {
                message = tr("No occurrences found.") ;
            }
            p->emitShowStatusMessage(message, true);
            p->ui->occNumber->setText(message);
            if(findArgs.isShowSize()) {
                p->ui->sizeOfSearch->setText(QString(tr("%1")).arg(findArgs.size()));
            }
            p->ui->lblSize->setVisible(findArgs.isShowSize());
            p->ui->sizeOfSearch->setVisible(findArgs.isShowSize());
        }
    }
    p->ui->treeWidget->setUpdatesEnabled(true);
    p->setEnabled(true);
    Utils::restoreCursor();
}

void XmlEditWidgetPrivate::clearSearchText()
{
    p->ui->searchEditBox->setText("");
    regola->unhiliteAll();
}

void XmlEditWidgetPrivate::onActionGoToParent()
{
    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    // Regola vuota, aggiungo il primo ed ultimo
    QTreeWidgetItem *currItem = getSelItem();
    QTreeWidgetItem *parentItem = NULL ;
    if(NULL == currItem) {
        Utils::errorNoSel(p);
        return;
    }
    if((parentItem = currItem->parent()) != NULL) {
        p->ui->treeWidget->setCurrentItem(parentItem);
    }
}

void XmlEditWidgetPrivate::onActionGoToPreviousBrother()
{
    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    // Regola vuota, aggiungo il primo ed ultimo
    QTreeWidgetItem *currItem = getSelItem();
    if(NULL != currItem) {
        Element *element = Element::fromItemData(currItem);
        if(NULL != element) {
            QTreeWidgetItem *itemToSelect = element->goToPreviousBrother();
            if(NULL != itemToSelect) {
                p->ui->treeWidget->setCurrentItem(itemToSelect);
            }
        }
    }
}

void XmlEditWidgetPrivate::onActionGoToNextBrother() //onActionGo_to_Next_Brother()
{
    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    // Regola vuota, aggiungo il primo ed ultimo
    QTreeWidgetItem *currItem = getSelItem();
    if(NULL != currItem) {
        Element *element = Element::fromItemData(currItem);
        if(NULL != element) {
            QTreeWidgetItem *itemToSelect = element->goToNextBrother();
            if(NULL != itemToSelect) {
                p->ui->treeWidget->setCurrentItem(itemToSelect);
            }
        }
    }
}

void XmlEditWidgetPrivate::onActionClearBookmarks()
{
    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    regola->clearBookmarks();
    //p->ui->treeWidget->update();
}

void XmlEditWidgetPrivate::onActionToggleBookmark()
{
    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    // Regola vuota, aggiungo il primo ed ultimo
    QTreeWidgetItem *currItem = getSelItem();
    if(NULL != currItem) {
        Element *element = Element::fromItemData(currItem);
        if(NULL != element) {
            regola->toggleBookmark(element);
        }
    }
}

void XmlEditWidgetPrivate::onActionGotoNextBookmark()
{
    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    Element *current = regola->gotoNextBookmark();
    if(NULL != current) {
        p->ui->treeWidget->setCurrentItem(current->getUI());
    }
}

void XmlEditWidgetPrivate::onActionGotoPreviousBookmark()
{
    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    Element *current = regola->gotoPreviousBookmark();
    if(NULL != current) {
        p->ui->treeWidget->setCurrentItem(current->getUI());
    }
}

void XmlEditWidgetPrivate::onActionShowChildIndex(const bool isChecked)
{
    paintInfo.setIndexPerChild(isChecked);
    repaint();
}

void XmlEditWidgetPrivate::onActionCompactView(const bool isChecked)
{
    paintInfo.setCompactView(isChecked);
    setDataColumnTitle(paintInfo.compactView());
    p->ui->treeWidget->setUniformRowHeights(paintInfo.compactView());
    repaint();
}


void XmlEditWidgetPrivate::onActionFixedSizeAttributes(const bool isChecked)
{
    paintInfo.setUseFixedLengthFont(isChecked);
    repaint();
}

void XmlEditWidgetPrivate::onActionShowAttributesLength(const bool isChecked)
{
    paintInfo.setShowAttributesLength(isChecked);
    repaint();
}

void XmlEditWidgetPrivate::onActionShowElementTextLength(const bool isChecked)
{
    paintInfo.setShowElementTextLength(isChecked);
    repaint();
}

void XmlEditWidgetPrivate::onActionCloseThisAllBrothers()
{
    QTreeWidgetItem *current = getSelItem();
    if(NULL != current) {
        QTreeWidgetItem *parent = current->parent();
        if(NULL != parent) {
            int numChildren = parent->childCount();
            for(int i = 0 ; i < numChildren ; i ++) {
                QTreeWidgetItem *child = parent->child(i);
                child->setExpanded(false);
            }
        }
    }
}

void XmlEditWidgetPrivate::searchTextChanged(const QString & text)
{
    p->ui->clearText->setEnabled(text.length() > 0);
    p->ui->occNumber->setText("");
}

void XmlEditWidgetPrivate::onActionHideBrothers()
{
    QTreeWidgetItem *current = getSelItem();
    if(NULL != current) {
        Element *element = Element::fromItemData(current);
        if(NULL != element) {
            if(element->isNormalViewState()) {
                element->hideBrothers();
            } else {
                element->showBrothers();
            }
        }
    }
}

void XmlEditWidgetPrivate::loadSearchSettings()
{
    FindTextParams lastSearch;
    lastSearch.loadState();
    updateSearchUI(lastSearch);
    setDataColumnTitle(paintInfo.compactView());
}

void XmlEditWidgetPrivate::setDataColumnTitle(const bool isCompactView)
{
    QHeaderView *header = p->ui->treeWidget->header() ;
    QString text;
    if(isCompactView) {
        text = tr("Attributes/Text");
    } else {
        text = tr("Attributes");
    }
    QAbstractItemModel* model = header->model();
    model->setHeaderData(paintInfo.columnForAttributes, header->orientation(), text);
}

void XmlEditWidgetPrivate::updateSearchUI(const FindTextParams &lastSearch)
{
    p->ui->isHiglightAll->setChecked(lastSearch.isHiliteAll());
    p->ui->isMatchExactValue->setChecked(lastSearch.isIsMatchExact());
    p->ui->isCaseSensitive->setChecked(lastSearch.isCaseSensitive());
    p->ui->isOnlyChildren->setChecked(lastSearch.isLookOnlyChildren());
    p->ui->selectionToBookmarks->setChecked(lastSearch.isSelToBookmarks());
    p->ui->closeUnrelated->setChecked(lastSearch.isCloseUnrelated());
    p->ui->showSize->setChecked(lastSearch.isShowSize());
    Utils::selectComboValue(p->ui->searchLocation, lastSearch.getFindTarget());
    p->ui->lblSize->setVisible(lastSearch.isShowSize());
    p->ui->sizeOfSearch->setVisible(lastSearch.isShowSize());
}

void XmlEditWidgetPrivate::saveSearchSettings(const FindTextParams &search)
{
    search.saveState();
}

void XmlEditWidgetPrivate::onActionShowCurrentElementTextBase64(const bool isChecked)
{
    QTreeWidgetItem *currItem = getSelItem();
    if(NULL != currItem) {
        Element *element = Element::fromItemData(currItem);
        if(NULL != element) {
            element->setShownBase64(isChecked);
            element->refreshUI();
        }
    }
}

void XmlEditWidgetPrivate::onActionShowBase64(const bool isChecked)
{
    paintInfo.setShowUnBase64(isChecked);
    repaint();
}

void XmlEditWidgetPrivate::onActionShowElementSize(const bool isChecked)
{
    paintInfo.setShowElementSize(isChecked);
    calcColumnState();
    if(NULL != regola) {
        regola->assignCollectSizeDataFlag(isChecked);
    }
    repaint();
}

//TODO: send an event
void XmlEditWidgetPrivate::on_ok_clicked()
{
    p->emitOkClicked();
}

void XmlEditWidgetPrivate::on_cancel_clicked()
{
    p->emitCancelClicked();
}

void XmlEditWidgetPrivate::onActionEditInnerXML()
{
    if(!isActionMode()) {
        return ;
    }

    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    if(isSlave) {
        return ;
    }
    QTreeWidgetItem *item = getSelItem();
    if(NULL != item) {
        regola->editInnerXMLElement(item, _uiDelegate);
    }
}

void XmlEditWidgetPrivate::onActionEditInnerXMLBase64()
{
    if(!isActionMode()) {
        return ;
    }

    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    if(isSlave) {
        return ;
    }
    QTreeWidgetItem *item = getSelItem();
    if(NULL != item) {
        regola->editInnerXMLBase64Element(item, _uiDelegate);
    }
}

void XmlEditWidgetPrivate::onActionEditInnerBase64Text()
{
    if(!isActionMode()) {
        return ;
    }

    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    if(isSlave) {
        return ;
    }
    QTreeWidgetItem *item = getSelItem();
    if(NULL != item) {
        regola->editTextNodeElementBase64(p, item, _uiDelegate);
    }
}

QString XmlEditWidgetPrivate::getContentAsText()
{
    if(NULL != regola) {
        return regola->getAsText();
    }
    return "";
}

bool XmlEditWidgetPrivate::isCDATA()
{
    return p->ui->cdataElement->isChecked();
}

void XmlEditWidgetPrivate::setCDATA(const bool isCData)
{
    return p->ui->cdataElement->setChecked(isCData);
}

void XmlEditWidgetPrivate::onActionZoomIn()
{
    if(paintInfo.zoomIn()) {
        Element::setZoomFactor(paintInfo.zoom());
        QFont font = p->ui->treeWidget->font();
        int newSize = VStyle::getZoomFontSize(mainFontSize, paintInfo.zoom());
        if(newSize > 0) {
            font.setPointSize(newSize);
            p->ui->treeWidget->setFont(font);
        }
        p->emitEnableZoom();
        repaint();
    }
}

void XmlEditWidgetPrivate::onActionZoomOut()
{
    if(paintInfo.zoomOut()) {
        Element::setZoomFactor(paintInfo.zoom());
        QFont font = p->ui->treeWidget->font();
        int newSize = VStyle::getZoomFontSize(mainFontSize, paintInfo.zoom());
        if(newSize > 0) {
            font.setPointSize(VStyle::getZoomFontSize(mainFontSize, paintInfo.zoom()));
            p->ui->treeWidget->setFont(font);
        }
        p->emitEnableZoom();
        repaint();
    }
}
//da decidere se emette o torna un flag

void XmlEditWidgetPrivate::onActionCopyPathToClipboard()
{
    if((NULL != regola) && !regola->fileName().isEmpty()) {
        QClipboard *clipboard = QApplication::clipboard();
        if(NULL != clipboard) {
            clipboard->setText(regola->fileName());
        }
    }
}

void XmlEditWidgetPrivate::onActionValidate()
{
    if(!isActionMode()) {
        return ;
    }
    if(NULL == regola) {
        return;
    }
    QByteArray dataXml = regola->getAsText().toUtf8();
    QXmlSchema schemaHandler;
    ValidatorMessageHandler messageHandler;
    schemaHandler.setMessageHandler(&messageHandler);
    QXmlSchemaValidator schemaValidator(schemaHandler);
    if(schemaValidator.validate(dataXml)) {
        Utils::message(p, tr("XML is valid."));
    } else {
        Utils::error(p, tr("XML does not conform to schema. Validation failed."));
        showValidationResults(regola->getAsText(), messageHandler, p) ;
    }
}

void XmlEditWidgetPrivate::onActionValidateFile()
{
    if(!isActionMode()) {
        return ;
    }

    if(NULL == regola) {
        return;
    }
    if(!regola->userDefinedXsd().isEmpty()) {
        validateWithFile(regola->userDefinedXsd());
    }
}

void XmlEditWidgetPrivate::validateWithFile(const QString &filePath)
{
    if(!isActionMode()) {
        return ;
    }

    if(NULL == regola) {
        return;
    }
    if(!filePath.isEmpty()) {
        QFile schemaFile(filePath);
        schemaFile.unsetError();
        schemaFile.open(QIODevice::ReadOnly);
        QByteArray schema = schemaFile.readAll() ;
        if(schemaFile.error()) {
            Utils::error(tr("Error opening schema file."));
            return ;
        }
        QByteArray dataXml = regola->getAsText().toUtf8();
        QXmlSchema schemaHandler;
        ValidatorMessageHandler messageHandler;
        schemaHandler.load(schema);
        schemaHandler.setMessageHandler(&messageHandler);
        if(!schemaHandler.isValid()) {
            Utils::error(p, tr("Schema is invalid"));
        } else {
            QXmlSchemaValidator schemaValidator(schemaHandler);
            if(schemaValidator.validate(dataXml)) {
                Utils::message(p, tr("XML is valid."));
            } else {
                Utils::error(p, tr("XML does not conform to schema. Validation failed."));
                showValidationResults(QString::fromUtf8(dataXml), messageHandler, _uiDelegate->getMainWidget()) ;
            }
        }
    }
}

void XmlEditWidgetPrivate::onActionValidateNewFile()
{
    if(!isActionMode()) {
        return ;
    }

    if(NULL == regola) {
        return;
    }
    QString filePath = QFileDialog::getOpenFileName(p, tr("Open Schema File"),
                       regola->fileName(), tr("XML Schema files (*.xsd);;All files (*);;"));
    if(!filePath.isEmpty()) {
        regola->setUserDefinedXsd(filePath);
        validateWithFile(filePath);
        computeSelectionState();
    }
}

void XmlEditWidgetPrivate::insertSnippet(Regola *newRegola)
{
    if(NULL != newRegola) {
        regola->pasteXML(p, p->ui->treeWidget, newRegola) ;
        computeSelectionState();
    }
}

void XmlEditWidgetPrivate::onActionDeleteBookmarked()
{
    if(NULL != regola) {
        Utils::showWaitCursor();
        p->ui->treeWidget->setUpdatesEnabled(false);
        regola->deleteBookmarked();
        p->ui->treeWidget->setUpdatesEnabled(true);
        Utils::restoreCursor();
    }
}

void XmlEditWidgetPrivate::onActionCleanBookmarkedContent()
{
    if(NULL != regola) {
        Utils::showWaitCursor();
        p->ui->treeWidget->setUpdatesEnabled(false);
        regola->cleanBookmarkedContents();
        p->ui->treeWidget->setUpdatesEnabled(true);
        Utils::restoreCursor();
    }
}

bool XmlEditWidgetPrivate::isActionMode()
{
    return (_displayMode == qxmledit::NORMAL) || (_displayMode == qxmledit::SCAN);
}

bool XmlEditWidgetPrivate::isActionNoScanMode()
{
    return _displayMode == qxmledit::NORMAL;
}

void XmlEditWidgetPrivate::setDisplayMode(const qxmledit::EDisplayMode value)
{
    _displayMode = value ;
    //navigationModeIndicator->setExploreMode(value);
    p->ui->treeWidget->setUniformRowHeights(value == qxmledit::EXPLORE);
    p->ui->treeWidget->setVisible(value != qxmledit::BLIND);
}


/**
  changestate if the state is to be reset to normal after loading new document
  */
void XmlEditWidgetPrivate::loadText(const QString &text, const bool isChangeState, const bool isAskForReview)
{
    if(!text.isEmpty()) {
        QDomDocument document;
        if(document.setContent(text)) {
            setDocument(document, QString(""), isChangeState);
            autoLoadValidation();
        } else {
            if(isAskForReview) {
                if(Utils::askYN(tr("Unable to parse XML. Sometimes this is caused by parser informations.\n Do you want to examine data as text?"))) {
                    ShowTextInDialog(p, text);
                }
            } else {
                Utils::error(tr("Unable to parse XML"));
            }
        }
    } else {
        Utils::error(tr("Data empty. Unable to load."));
    }
}

//---------------------- load data

//TODO: error checking
void XmlEditWidgetPrivate::doLoadFileXplore(const QString &filePath)
{
    Regola *newModel = newRegola();
    if(!newModel->loadXplore(filePath)) {
        Utils::error(tr("Unable to load XML"));
    }
    setDisplayMode(qxmledit::EXPLORE);
    p->emitDataReadyMessage(tr("Data loaded"));
    deleteRegola();
    regola = newModel;
    regola->setModified(false);
    resetTree();
    p->ui->treeWidget->setUniformRowHeights(true);
    display();
    startUIState();
    regolaIsModified();
    if(isExpandTreeOnLoad()) {
        onActionExpandAll();
    }
    resizeTreeColumns();
    showControls(true);
}

//TODO: error checking
void XmlEditWidgetPrivate::setDocument(QDomDocument &document, const QString &filePath, const bool isSetState)
{
    Regola *newModel = new Regola(document, filePath);
    newModel->setPaintInfo(&paintInfo);
    p->emitDataReadyMessage(tr("Data loaded"));
    deleteRegola();
    regola = newModel;
    regola->assignCollectSizeDataFlag(paintInfo.showElementSize());
    docTypeChanged(regola->docType());
    if(isSetState) {
        setDisplayMode(qxmledit::NORMAL);
    }
    connect(regola, SIGNAL(wasModified()), this, SLOT(regolaIsModified()));
    connect(regola, SIGNAL(undoStateChanged()), this, SLOT(regolaUndoChanged()));
    connect(regola, SIGNAL(docTypeChanged(const QString &)), this, SLOT(docTypeChanged(const QString &)));
    resetTree();
    display();
    startUIState();
    regolaIsModified();
    if(isExpandTreeOnLoad()) {
        onActionExpandAll();
    }
    resizeTreeColumns();
    showControls(true);
    //setWindowModified(false); TODO delete
}

void XmlEditWidgetPrivate::docTypeChanged(const QString &docType)
{
    if(docType.isEmpty()) {
        p->ui->docTypeLabel->setVisible(false);
        p->ui->docTypeLabel->setText("");
    } else {
        p->ui->docTypeLabel->setText(tr("doc type: %1").arg(docType));
        p->ui->docTypeLabel->setVisible(true);
    }
}

void XmlEditWidgetPrivate::showControls(const bool how)
{
    p->ui->treeWidget->setVisible(how);
    p->ui->addBrother->setVisible(how);
    p->ui->addChild->setVisible(how);
    p->ui->deleteItem->setVisible(how);
    p->ui->editItem->setVisible(how);
    p->ui->moveUp->setVisible(how);
    p->ui->moveDown->setVisible(how);
}

void XmlEditWidgetPrivate::onActionHideView(const bool isChecked)
{
    paintInfo.setHideView(isChecked);
    showControls(!paintInfo.hideView());
    if(!paintInfo.hideView()) {
        display();
    }
}

bool XmlEditWidgetPrivate::isValidXsd()
{
    if(NULL != regola) {
        return regola->isValidXsd();
    }
    return false;
}

void XmlEditWidgetPrivate::on_viewAsXsdCmd_clicked()
{
    p->emitViewAsXsdRequested();
}


Regola *XmlEditWidgetPrivate::getRegola()
{
    return regola;
}


void XmlEditWidgetPrivate::onActionViewAsXsd()
{
    XsdPlugin plugin ;
    plugin.go(p->parentWidget(), regola);
}

void XmlEditWidgetPrivate::loadSchema(const QString &schemaURL)
{
    if(NULL == _appData) {
        return ;
    }

    deleteSchema();
    XSchemaLoader *loader = NULL ;
    if((loader = new XSchemaLoader(NULL, NULL)) != NULL)  {
        try {
            connect(loader, SIGNAL(loadComplete(XSchemaLoader*, XSDSchema*, const bool)), this, SLOT(schemaLoadComplete(XSchemaLoader*, XSDSchema*, const bool)));

            // finds the file path if any
            QString fileName ;
            if(NULL != regola) {
                fileName = regola->fileName();
            }
            QFile file(fileName);
            QString dirPath = QFileInfo(file).absolutePath();

            loader->readFromURL(NULL, schemaURL, _appData->xsdNetworkAccessManager(), dirPath);

        } catch(XsdException *ex) {
            Utils::error(tr("Error loading schema.\n%1").arg(ex->cause()));
        } catch(...) {
            Utils::error(tr("Unknown exception."));
        }
    } else {
        Utils::error(tr("No root item"));
    }
}

void XmlEditWidgetPrivate::schemaLoadComplete(XSchemaLoader *loader, XSDSchema *schema, const bool isError)
{
    if(NULL != loader) {
        loader->deleteLater();
    }
    if(isError) {
        p->emitSchemaLabelChanged(tr("error loading schema"));
        if(NULL != schema) {
            delete schema ;
        }
    } else {
        _schemaRoot = schema ;
        if(NULL == regola) {
            p->emitSchemaLabelChanged(tr("schema: ?"));
        } else {
            p->emitSchemaLabelChanged(tr("schema: %1").arg(regola->documentXsd()));
        }
    }
}

void XmlEditWidgetPrivate::onActionAllowedSchemaElements()
{
    if(!isActionMode()) {
        return ;
    }
    if(NULL == regola) {
        errorNoRule();
        return ;
    }

    insertAllowedElements(getSelectedItem());
}

bool XmlEditWidgetPrivate::findAllowedItemsElement(XElementContent *content, Element *element, bool &startAsRoot)
{
    SchemaValidator validator;
    bool go = false;
    startAsRoot = false;

    if(NULL == regola->root()) {
        go = true ;
        startAsRoot = true ;
    } else if((NULL != element) && (Element::ET_ELEMENT == element->getType())) {
        go = true ;
    }

    if(go) {
        if(validator.getInsertionPointAllowedItems(content, _schemaRoot, element, startAsRoot)) {
            return true ;
        }
    }
    return false;
}

void XmlEditWidgetPrivate::insertAllowedElements(Element *element)
{
    XElementContent content;
    bool startAsRoot = false ;
    if(findAllowedItemsElement(&content, element, startAsRoot)) {

        bool isModified = false;
        regola->emptyUndoStack();
        QList<XSchemaObject*> result;
        if(ChooseItemsBySchema(p, &content, &result)) {
            foreach(XSchemaObject * object, result) {
                if(startAsRoot) {
                    regola->addChildToElement(_uiDelegate->getMainWidget(), p->getMainTreeWidget(), NULL, object->name(), false);
                    isModified = true ;
                    break;
                }
                regola->addChildToElement(p, p->ui->treeWidget, element, object->name(), false);
                isModified = true ;
            }
            if(isModified) {
                p->emitDocumentIsModified(regola->isModified());
            }
        }
    }
}

/** Substitute the element text
  */
void XmlEditWidgetPrivate::onActionPasteAndSubstituteText()
{
    if(!isActionMode()) {
        return ;
    }
    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    Element *element = getSelectedItem();
    if(NULL != element) {
        regola->editAndSubstituteTextInNodeElement(p, element, _uiDelegate);
    }
}

void XmlEditWidgetPrivate::onActionNewUsingXMLSchema(const QString &schemaURL)
{
    doNew();
    regola->setUserAndDocumentXsd(schemaURL);
    loadSchema(schemaURL);
}

void XmlEditWidgetPrivate::doNew()
{
    deleteRegola();
    regola = newRegola();
    setDisplayMode(qxmledit::NORMAL);
    regola->assignCollectSizeDataFlag(paintInfo.showElementSize());
    p->ui->treeWidget->clear();
    startUIState();
    setClipBoardActionsState();
    p->emitDocumentIsModified(regola->isModified());
    p->emitSchemaLabelChanged(tr(""));
}

void XmlEditWidgetPrivate::onActionTransformInComment()
{
    if(!isActionMode()) {
        return ;
    }
    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    QTreeWidgetItem *currItem = getSelItem();
    if(NULL == currItem) {
        Utils::errorNoSel(p);
        return;
    }
    Element *elementToTransform = Element::fromItemData(currItem);
    regola->transformInComment(p, p->ui->treeWidget, elementToTransform);
}

void XmlEditWidgetPrivate::onActionExtractElementsFromComment()
{
    if(!isActionMode()) {
        return ;
    }
    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    QTreeWidgetItem *currItem = getSelItem();
    if(NULL == currItem) {
        Utils::errorNoSel(p);
        return;
    }
    Element *elementToRetransform = Element::fromItemData(currItem);
    regola->generateFromComment(p->ui->treeWidget, _uiDelegate, elementToRetransform);
}

void XmlEditWidgetPrivate::autoLoadValidation()
{
    if(_appData->isAutovalidationOn()) {
        if(!regola->documentXsd().isEmpty()) {
            if(regola->hasMoreThanOneXsdFile()) {
                Utils::message(tr("Currently QXmlEdit handles only one XML Schema file, even if data file has references to more schemas."));
            }
            p->emitSchemaLabelChanged(tr("schema: loading %1").arg(regola->documentXsd()));
            loadSchema(regola->documentXsd());
        } else {
            p->emitSchemaLabelChanged(tr("No schema"));
        }
    } else {
        p->emitSchemaLabelChanged(tr(""));
    }
}

void XmlEditWidgetPrivate::onActionInsertNoNamespaceSchemaReferenceAttributes()
{
    if(!isActionMode()) {
        return ;
    }
    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    Regola::EInsSchemaRefInfo error ;
    error = regola->insertNoNamespaceSchemaReferenceAttributes();
    decodeXsdInsertError(error);
}

void XmlEditWidgetPrivate::decodeXsdInsertError(const Regola::EInsSchemaRefInfo error)
{
    switch(error) {
    default:
    case Regola::INSERT_SCHEMA_ATTR_ERROR_NOROOT:
        Utils::error(p, tr("A root element is needed"));
        break;
    case Regola::INSERT_SCHEMA_ATTR_INFO_SCHEMAPRESENT:
        Utils::message(p, tr("Schema definition already present."));
        break;
    case Regola::INSERT_SCHEMA_ATTR_INFO_REFPRESENT:
        Utils::message(p, tr("Schema reference already present."));
        break;
    case Regola::INSERT_SCHEMA_ATTR_NOERROR:
        return ;
    }
}

void XmlEditWidgetPrivate::onActionInsertSchemaReferenceAttributes()
{
    if(!isActionMode()) {
        return ;
    }
    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    Regola::EInsSchemaRefInfo error ;
    error = regola->insertSchemaReferenceAttributes();
    decodeXsdInsertError(error);
}

void XmlEditWidgetPrivate::setNavigationDataAndEnable(const int minFragment, const int maxFragment)
{
    p->ui->navigation->setRange(minFragment, maxFragment);
    setDisplayMode(qxmledit::SCAN);
}

void XmlEditWidgetPrivate::showNavigationBox()
{
    bool isNavigation = (_displayMode == qxmledit::SCAN);
    p->ui->navigation->setVisible(isNavigation);
    p->ui->navigation->setEnabled(isNavigation);
}

void XmlEditWidgetPrivate::navigateToPage(const int page)
{
    /********************************** test code **************************
    Utils::todo(QString("Navigate to %1").arg(page));
    setWindowTitle(QString::number(page));
    ***********************************************************************/
    //loadCurrentPage(page);
    p->emitLoadCurrentPage(page);
}

void XmlEditWidgetPrivate::error(const QString& message)
{
    Utils::error(p, message);
}

void XmlEditWidgetPrivate::warning(const QString& message)
{
    Utils::warning(p, message);
}

void XmlEditWidgetPrivate::message(const QString& message)
{
    Utils::message(p, message);
}

bool XmlEditWidgetPrivate::askYN(const QString & message)
{
    return Utils::askYN(p, message);
}

QTreeWidget *XmlEditWidgetPrivate::getMainTreeWidget()
{
    return p->ui->treeWidget;
}

void XmlEditWidgetPrivate::onActionHideAllLeafChildren()
{
    if(NULL != regola) {
        p->ui->treeWidget->setUpdatesEnabled(false);
        Utils::showWaitCursor();
        regola->hideLeafNodes();
        p->ui->treeWidget->setUpdatesEnabled(true);
        Utils::restoreCursor();
    }
}

Element* XmlEditWidgetPrivate::onActionHideLeafChildren()
{
    Element *element = getSelectedItem();
    if(NULL != element) {
        p->ui->treeWidget->setUpdatesEnabled(false);
        Utils::showWaitCursor();
        element->hideChildrenLeaves();
        //        evaluateSingleItemLeaves(element);
        p->ui->treeWidget->setUpdatesEnabled(true);
        Utils::restoreCursor();
    }
    return element;
}

Element* XmlEditWidgetPrivate::onActionShowLeafChildren()
{
    Element *element = getSelectedItem();
    if(NULL != element) {
        p->ui->treeWidget->setUpdatesEnabled(false);
        Utils::showWaitCursor();
        element->showChildrenLeaves();
        //        evaluateSingleItemLeaves(element);
        p->ui->treeWidget->setUpdatesEnabled(true);
        Utils::restoreCursor();
    }
    return element;
}

void XmlEditWidgetPrivate::onActionShowAllLeafChildren()
{
    if(NULL != regola) {
        p->ui->treeWidget->setUpdatesEnabled(false);
        Utils::showWaitCursor();
        regola->showLeafNodes();
        p->ui->treeWidget->setUpdatesEnabled(true);
        Utils::restoreCursor();
    }
}


XSDSchema *XmlEditWidgetPrivate::schema()
{
    return _schemaRoot;
}

bool XmlEditWidgetPrivate::isExpandTreeOnLoad()
{
    return Config::getBool(Config::KEY_MAIN_EXPANDONLOAD, true);
}

bool XmlEditWidgetPrivate::isUndoPossible()
{
    return regola->canUndo();
}

bool XmlEditWidgetPrivate::isRedoPossible()
{
    return regola->canRedo();
}

void XmlEditWidgetPrivate::undo()
{
    regola->undo();
}

void XmlEditWidgetPrivate::redo()
{
    regola->redo();
}

void XmlEditWidgetPrivate::notifyUndo()
{
    emit p->undoStateUpdated(isUndoPossible(), isRedoPossible());
}

void XmlEditWidgetPrivate::regolaUndoChanged()
{
    notifyUndo();
}

void XmlEditWidgetPrivate::setCurrentItem(Element *newSelection)
{
    if(NULL != newSelection) {
        p->ui->treeWidget->setCurrentItem(newSelection->getUI());
    }
}

void XmlEditWidgetPrivate::onActionCompareXSD(const QString &folderPath)
{
    bool isAllowed = false;
    if(NULL != regola) {
        if(regola->isValidXsd()) {
            XSDCompare xsdCompare;
            xsdCompare.compare(p, regola, folderPath);
            isAllowed = true ;
        }
    }
    if(!isAllowed) {
        Utils::error(tr("No XSD is present in the editor."));
    }
}

// copy the selected element in the clipboard
void XmlEditWidgetPrivate::onActionExportSiblingsAttributesAsCSVClipboard()
{
    // copy the selected element in the clipboard
    Element *element = getSelectedItem();
    if(NULL == element) {
        return ;
    }
    QClipboard *clipboard = QApplication::clipboard();
    QString text = element->makeCSVFromSiblings();
    clipboard->setText(text);
    Utils::message(tr("Attributes copied"));
}

void XmlEditWidgetPrivate::viewNodes()
{
    if(NULL != regola) {
        QByteArray regolaData = regola->writeMemory();
        QBuffer regolaBuffer(&regolaData);
        QList<TagNode*> nodesList ;
        NodesRelationsDialog dialog(false, nodesList);
        dialog.loadNodesFromFile(&regolaBuffer, regola->fileName());
        dialog.exec();
    }
}
