/**************************************************************************
 *  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                                            *
 **************************************************************************/
/*
Rules to edit:
1) If no items at all, this is the first item
2) Cannot add brother to the first item (1! root item)
*/

#include <QtXml/QDomDocument>
#include <QFile>
#include <QtGui>
#include <QClipboard>
#include <QXmlSchema>
#include <QXmlSchemaValidator>

extern const char *APP_TITLE ;

#include "utils.h"
#include "mainwindow.h"
#include "config.h"
#include "configurationdialog.h"
#include "compare.h"
#include "preferreddirs.h"
#include "test.h"
#include "qxmledit.h"
#include "validatormessagehandler.h"
#include "validationresults.h"
#include "snippet_interface.h"
#include "elementitemdelegate.h"
#include "aboutdialog.h"
#include "schemavalidator.h"
#include "alloweditemsinsert.h"
#include "SchemaInterface.h"
#include "welcomedialog.h"
#include "widgets/columnarview.h"
#include "sessions/sessionrequester.h"
#include "visualization/datavisualization.h"
#include "uiservices.h"
#include "attributefilterdialog.h"
#include "modules/graph/nodesrelationsdialog.h"

#define LONG_TIMEOUT    10000
#define SHORT_TIMEOUT    2000
#define HELP_FILE       "QXmlEdit_manual.pdf"


void doOptions(QWidget * const parentWindow, ApplicationData* data);
void executeTests(QWidget *parent);
void extractFragments(ExtractResults *extractResult, QWidget *parent);
void ShowTextInDialoog(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"

#define MAX_LAST_FILES  (20)

MainWindow::MainWindow(const bool setIsSlave, QApplication *newApplication, ApplicationData *newData, QMainWindow *parent) : QMainWindow(parent), uiDelegate(this)
{
    _attrFilter = NULL ;
    isAutoDelete = false;
    started = false ;
    internalStateOk = false;
    _sessionStateWidget = NULL;

    labelSchema = NULL;
    navigationModeIndicator = NULL ;
    maxLastFiles = MAX_LAST_FILES;
    maxPrefDirs = QXmlEditParameters::NUM_PREF_DIRS;
    isSlave = setIsSlave ;
    eventLoop = NULL ;
    _extractResult = NULL ;
    application = newApplication ;
    data = newData ;
    ui.setupUi(this);
    //----------------------------------------------------------------------------------
    internalStateOk = finishSetUpUi();
    if(!internalStateOk) {
        Utils::error(tr("Error preparing user interface."));
    }
    if(!ui.editor->isReady()) {
        Utils::error(tr("Error preparing edit widget."));
    }

    loadRecentFilesSettings();
    loadPreferredDirsSettings();
    setAcceptDrops(true);
    setUnifiedTitleAndToolBarOnMac(true);

    QString windowTitle = APP_TITLE;
    setWindowTitle(windowTitle);

    data->addWindow(this);

    autoTest();
    if(Config::getBool(Config::KEY_TEST_SHOW_XSD_EDITOR, false)) {
        //void xsdTest(); TODO
        //xsdTest();
    }
    started = true ;
    /********************************** test code **************************
    ui.navigation->setRange(1, 200);
    setDisplayMode(qxmledit::SCAN);
    showNavigationBox();
    ************************************************************************/
}

MainWindow::~MainWindow()
{
    data->removeWindow(this);
    cleanExtractResults(); //TODO
    removeAttributesFilter();
}


QString MainWindow::editNodeElementAsXML(const bool isBase64Coded, Element *pElement, const QString & /*text*/, const bool /*isCData*/, bool &isCDataOut, bool &isOk)
{
    QString result;
    MainWindow mainWindow(true, application, data) ;
    mainWindow.setWindowModality(Qt::ApplicationModal);
    mainWindow.ui.editor->loadText(pElement->getAsSimpleText(isBase64Coded));
    mainWindow.ui.editor->setCDATA(pElement->isCDATA());
    QEventLoop eventLoop;
    mainWindow.setEventLoop(&eventLoop);
    mainWindow.show();
    if(eventLoop.exec() > 0) {
        result = mainWindow.getContentAsText();
        isCDataOut = ui.editor->isCDATA();
        isOk = true;
    } else {
        isOk = false;
    }
    return result;
}

void MainWindow::cleanExtractResults()
{
    if(NULL != _extractResult) {
        delete _extractResult ;
        _extractResult = NULL ;
    }
}

void MainWindow::deleteSchema()
{
    ui.editor->deleteSchema(); //TODO: is it private?
}

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

void MainWindow::changeEvent(QEvent *e)
{
    QMainWindow::changeEvent(e);
    switch(e->type()) {
    case QEvent::StyleChange:
        VStyle::changeDefaultBrush(QApplication::palette().text());
        break;
    default:
        break;
    }
}

void MainWindow::on_actionNew_triggered()
{
    if(!verifyAbandonChanges()) {
        return ;
    }
    ui.editor->doNew();
    removeAttributesFilter();
}

void MainWindow::startUIState()
{
    ui.actionPaste->setEnabled(false);
    ui.actionResizeToContents->setEnabled(true);

}

bool MainWindow::finishSetUpUi()
{
    if(ui.editor->versionNumber() != VERSION_NUMBER) {
        Utils::error(this, tr("The XML Editor component is not correct (%1)").arg(ui.editor->versionNumber()));
        return false;
    }
    ui.editor->setData(application, data, isSlave, this);
    if(!ui.editor->init()) {
        Utils::error(this, tr("XML Editor initialization failed"));
        return false;
    }
    connect(ui.editor, SIGNAL(documentIsModified(const bool)), this, SLOT(onDocumentIsModified(const bool)));
    connect(ui.editor, SIGNAL(reevaluateSelectionState()), this, SLOT(onComputeSelectionState()));
    connect(ui.editor, SIGNAL(signalSetClipBoardActionsState(const bool)), this, SLOT(setClipBoardActionsState(const bool)));
    connect(ui.editor, SIGNAL(okClicked()), this, SLOT(on_ok_clicked()));
    connect(ui.editor, SIGNAL(cancelClicked()), this, SLOT(on_cancel_clicked()));
    connect(ui.editor, SIGNAL(viewAsXsdRequested()), this, SLOT(on_actionViewAsXsd_triggered()));
    connect(ui.editor, SIGNAL(schemaLabelChanged(const QString &)), this, SLOT(schemaLoadComplete(const QString &)));
    connect(ui.editor, SIGNAL(dataReadyMessage(const QString &)), this, SLOT(onNewMessage(const QString &)));
    connect(ui.editor, SIGNAL(loadCurrentPage(const int)), this, SLOT(navigateToPage(const int)));
    connect(ui.editor, SIGNAL(showStatusMessage(const QString &, const bool)), this, SLOT(onShowStatusMessage(const QString &, const bool)));
    connect(ui.editor, SIGNAL(undoStateUpdated(const bool, const bool)), this, SLOT(onUndoStateUpdated(const bool, const bool)));

    connect(ui.sessionTree, SIGNAL(fileLoadRequest(const QString&)), this, SLOT(onSessionfileLoadRequest(const QString&)));
    connect(ui.sessionTree, SIGNAL(folderOpenRequest(const QString&)), this, SLOT(onSessionFolderOpenRequest(const QString&)));
    connect(ui.sessionTree, SIGNAL(showSessionManagement()), this, SLOT(onShowSessionManagement()));
    connect(ui.sessionTree, SIGNAL(createSession()), this, SLOT(on_actionNewSession_triggered()));

    //------------------------------ status bar widgets ----------------------------------
    // turn off autoscroll
    navigationModeIndicator = new NavigationMode(statusBar());
    if(NULL == navigationModeIndicator) {
        Utils::error(tr("Error creating user interface"));
        return false;
    }
    navigationModeIndicator->setExploreMode(ui.editor->displayMode());
    statusBar()->addPermanentWidget(navigationModeIndicator);

    labelSchema = new QLabel(statusBar());
    if(NULL == labelSchema) {
        Utils::error(tr("Error creating user interface"));
        return false;
    }
    setSchemaLabel(tr(""));
    statusBar()->addPermanentWidget(labelSchema);

    _sessionStateWidget = new SessionStateWidget(statusBar());
    if(NULL == _sessionStateWidget) {
        Utils::error(tr("Error creating user interface"));
        return false;
    }
    _sessionStateWidget->setSessionManager(data->sessionManager());
    statusBar()->addPermanentWidget(_sessionStateWidget);

    _attributeFilterWidget = new AttrFilterStatus();
    statusBar()->addPermanentWidget(_attributeFilterWidget);

    //------------------------------ status bar widgets ----------------------------------

    startUIState();
    ui.actionShowAttrLine-> setCheckable(true);
    ui.actionShowChildIndex->setCheckable(true);
    ui.actionCompactView->setCheckable(true);
    ui.actionHideBrothers->setCheckable(true);
    ui.actionShowBase64->setCheckable(true);
    ui.actionShowCurrentElementTextBase64->setCheckable(true);
    ui.actionShowElementSize->setCheckable(true);

    ui.actionFixedSizeAttributes->setCheckable(true);
    ui.actionShowAttributesLength->setCheckable(true);
    ui.actionShowElementTextLength->setCheckable(true);
    ui.actionHideView->setCheckable(true);
    PaintInfo *paintInfo = ui.editor->getPaintInfo();
    ui.actionShowAttrLine->setChecked(paintInfo->oneAttrPerLine());
    ui.actionShowElementTextLength->setChecked(paintInfo->showElementTextLength());
    ui.actionShowChildIndex->setChecked(paintInfo->indexPerChild());
    ui.actionCompactView->setChecked(paintInfo->compactView());
    ui.actionFixedSizeAttributes->setChecked(paintInfo->useFixedLengthFont());
    ui.actionShowAttributesLength->setChecked(paintInfo->showAttributesLength());
    ui.actionShowBase64->setChecked(paintInfo->showUnBase64());
    ui.actionShowElementSize->setChecked(paintInfo->showElementSize());
    ui.actionHideView->setChecked(paintInfo->hideView());

    updateUndoState(false, false);

    connect(ui.editor, SIGNAL(treeContextMenuRequested(const QPoint &)), this, SLOT(treeContextMenu(const QPoint &)));
    //TODO connect( ui.searchLocation, SIGNAL(currentIndexChanged(int)), this, SLOT(searchTypeChanged(int)) );
    ui.menu_XML->setTearOffEnabled(true);

    if(!data->sessionManager()->isEnabled() || isSlave) {
        ui.actionShowCurrentSessionPanel->setVisible(false);
        ui.actionShowCurrentSessionPanel->setEnabled(false);
        ui.sessionTree->setVisible(false);
    } else {
        ui.actionShowCurrentSessionPanel->setChecked(true);
        ui.sessionTree->setVisible(true);
    }
    connect(data->sessionManager(), SIGNAL(enablingChanged()), this, SLOT(onSessionEnablingChanged()));
    connect(data->sessionManager(), SIGNAL(sessionStateChanged(Session::SessionState)), this, SLOT(onHandleSessionState()));
    connect(data->sessionManager(), SIGNAL(editFile(const QString &)), this, SLOT(onSessionfileLoadRequest(const QString&)));
    if(!isSlave) {
        ui.sessionTree->setSessionManager(data->sessionManager());
    }

    // insert the edit menu
    bool isOk = true;
    QToolButton *editTextButton = new QToolButton(this);
    QMenu *editTextMenu = new QMenu(this);
    if((NULL == editTextButton) || (editTextMenu == NULL)) {
        isOk = false ;
    } else {
        editTextMenu->addAction(ui.actionPasteAndSubstituteText);
        editTextMenu->addAction(ui.actionEditInnerBase64Text);
        editTextMenu->addAction(ui.actionEditInnerXML);
        editTextMenu->addAction(ui.actionEditInnerXMLBase64);
        editTextButton->setMenu(editTextMenu);
        editTextButton->setPopupMode(QToolButton::InstantPopup);
        editTextButton->setIcon(QIcon(":/commands/modify"));
        ui.toolBar->insertWidget(ui.actionHelpOnQXmlEdit, editTextButton);
        ui.toolBar->insertSeparator(ui.actionHelpOnQXmlEdit);
    }
    /*
        QMenu *editTextMenu = new QMenu(this);
        if(editTextMenu == NULL) {
            isOk = false ;
        } else {
            editTextMenu->setDefaultAction(ui.actionEdit);
            ui.toolBar->addAction(editTextMenu->menuAction());
            editTextMenu->addAction(ui.actionEdit);
            editTextMenu->addAction(ui.actionPasteAndSubstituteText);
            editTextMenu->addAction(ui.actionEditInnerBase64Text);
            editTextMenu->addAction(ui.actionEditInnerXML);
            editTextMenu->addAction(ui.actionEditInnerXMLBase64);
            editTextMenu->setIcon(QIcon(":/commands/modify"));

            ui.toolBar->insertAction(ui.actionHelpOnQXmlEdit, editTextMenu->menuAction());
            ui.toolBar->insertSeparator(ui.actionHelpOnQXmlEdit);

        }
    */
    ui.actionLastFiles->setVisible(false);
    QMenu *recentFiles = new QMenu(this);
    if(NULL == recentFiles) {
        isOk = false ;
    } else {
        recentFiles->setTitle(tr("Recent Files"));
        ui.menuFile->insertMenu(ui.actionLastFiles, recentFiles);

        if(!buildLastObjects(maxLastFiles, lastFiles, SLOT(onRecentFile()), recentFiles)) {
            isOk = false ;
        }
    }

    QMenu *preferredDirsMenu = new QMenu(this);
    if(NULL == preferredDirsMenu) {
        isOk = false ;
    } else {
        preferredDirsMenu->setTitle(tr("Preferred Directories"));
        ui.menuFile->insertMenu(ui.actionLastFiles, preferredDirsMenu);
        preferredDirsMenu->addAction(ui.actionEditPreferredDirectories);
        preferredDirsMenu->addAction(ui.actionAddCurrentDirectory);
        preferredDirsMenu->addSeparator();

        if(!buildLastObjects(maxPrefDirs, preferredDirs, SLOT(onPreferredDir()), preferredDirsMenu)) {
            isOk = false ;
        }
    }

    QMenu *pluginsMenu = new QMenu(this);
    if(NULL == pluginsMenu) {
        isOk = false ;
    } else {
        ui.actionPlugins->setMenu(pluginsMenu);
        if(!buildPluginsMenu(SLOT(onPlugin()), pluginsMenu)) {
            isOk = false ;
        }
    }

    onHandleSessionState();

    //---- region(experimental)
    //---- endregion(experimental)
    onComputeSelectionState();

    return isOk;
}

void MainWindow::onHandleSessionState()
{
    SessionManager *sessionManager = data->sessionManager();
    bool isSessionEnabled = sessionManager->isEnabled();
    Session::SessionState sessionState = sessionManager->state();

    ui.actionNewSession->setEnabled(isSessionEnabled);
    bool isPauseSessionEnabled = isSessionEnabled && (sessionState == Session::Active);
    ui.actionPauseSession->setEnabled(isPauseSessionEnabled);
    ui.actionPauseSession->setVisible(isPauseSessionEnabled);
    bool isResumeSessionEnabled = !isSlave && isSessionEnabled && (sessionState == Session::Paused);
    ui.actionResumeSession->setEnabled(isResumeSessionEnabled);
    ui.actionResumeSession->setVisible(isResumeSessionEnabled);
    ui.actionCloseSession->setEnabled(!isSlave && isSessionEnabled && (sessionState == Session::Active));
    ui.actionManageSessions->setEnabled(isSessionEnabled);
    ui.actionSessionDetails->setEnabled(isSessionEnabled && (sessionState != Session::NoSession));
}


bool MainWindow::buildLastObjects(const int maxObjects, QList<QAction*> &cmdList, const char *method, QMenu *parent)
{
    bool isOk = true ;
    //--- last files and dirs
    for(int i = 0 ; i < maxObjects ; i++) {
        QAction *action = new QAction(this);
        if(NULL != action) {
            cmdList.append(action);
            action->setVisible(false);
            connect(action, SIGNAL(triggered()), this, method);
            parent->addAction(action);
        } else {
            isOk = false;
        }
    }
    return isOk;
}


QAction *MainWindow::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 MainWindow::repaint()
{
    ui.editor->repaint();
}

void MainWindow::display()
{
    ui.editor->display();
}

void MainWindow::treeContextMenu(const QPoint& position)
{
    QMenu contextMenu(this);
    contextMenu.addAction(ui.actionCut);
    contextMenu.addAction(ui.actionCopy);
    bool isActionMode = ui.editor->isActionMode();
    if(isActionMode) {
        contextMenu.addAction(ui.actionPaste);
    }
    contextMenu.addSeparator() ;
    if(isActionMode) {
        contextMenu.addAction(ui.actionPasteAndSubstituteText);
        contextMenu.addAction(ui.actionEditInnerBase64Text);
    }
    if(!isSlave && isActionMode) {
        contextMenu.addAction(ui.actionEditInnerXML);
    }
    if(isActionMode) {
        contextMenu.addAction(ui.actionEditInnerXMLBase64);
        contextMenu.addAction(ui.actionTransforminSnippet);
        contextMenu.addSeparator() ;
        contextMenu.addAction(ui.actionMoveUp);
        contextMenu.addAction(ui.actionMoveDown);
    }
    Element *element = getSelectedItem();
    if(NULL != element) {
        if(element->areChildrenLeavesHidden(element->getUI())) {
            contextMenu.addAction(ui.actionShowLeafChildren);
        } else {
            contextMenu.addAction(ui.actionHideLeafChildren);
        }
    }

#ifdef XSD_INTEGRATION
    if(NULL != ui.editor->schema()) {
        //SchemaValidator validator;
        // TODO: redo
        /*QStringList options = validator.checkInsertionPoint(_schemaRoot, regola, getSelectedItem());
        QMenu *menu = new QMenu(&contextMenu);
        if(NULL != menu) {
            ui.actionAllowedSchemaElements->setMenu(menu);
            foreach( QString item, options ) {
                QAction *action = new QAction( item, &contextMenu );
                menu->addAction( action );
            }
        }*/
        contextMenu.addSeparator() ;
        contextMenu.addAction(ui.actionAllowedSchemaElements);
    }
#endif
    contextMenu.exec(ui.editor->getMainTreeWidget()->mapToGlobal(position));
}

// Solo il salva come...
void MainWindow::onDocumentIsModified(const bool isModified)
{
    TRACEQ(QString("MainWindow::onDocumentIsModified(%1)").arg(isModified));
    bool isFileName = false;
    Regola *regola = ui.editor->getRegola();
    if(!regola->fileName().isEmpty()) {
        isFileName = true ;
    }
    ui.actionSave->setEnabled(isFileName && ui.editor->isActionNoScanMode());
    bool regolaIsEmpty = regola->isEmpty(false);
    ui.actionSaveAs->setEnabled(!regolaIsEmpty);
    ui.actionSaveACopyAs->setEnabled(!regolaIsEmpty);
    setFileTitle();
    setWindowModified(isModified);
}

//cambia la sel clipb.
void MainWindow::setClipBoardActionsState(const bool isAction)
{
    ui.actionPaste->setEnabled(isAction && ui.editor->isActionMode());
}

void MainWindow::onComputeSelectionState()
{
    Regola *regola = getRegola();
    bool isSomeItemSelected = false;
    bool selectSpecials = false;
    bool selectSpecialsUp = false;
    bool selectSpecialsDown = false;
    bool isNormalViewState = true;
    bool canAddChild = false;
    bool isShownAsBase64 = false ;
    bool isXSDPresent = isValidXsd();
    bool isElementSelected = false;

    qxmledit::EDisplayMode displayMode = ui.editor->displayMode();
    bool isExplore = (displayMode != qxmledit::NORMAL) && (displayMode != qxmledit::SCAN) ;
    int numberSelected = ui.editor->getMainTreeWidget()->selectedItems().size();
    if(numberSelected > 0) {
        selectSpecials = true ;
        selectSpecialsUp = true ;
        selectSpecialsDown = true ;
        Element *element = Element::fromItemData(ui.editor->getMainTreeWidget()->selectedItems().at(0));
        selectSpecialsUp = !element->isFirstChild();
        selectSpecialsDown = !element->isLastChild();
        if(element->getType() == Element::ET_ELEMENT) {
            canAddChild = true;
            isElementSelected = 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;
    }
    bool enableGoToParent = false;
    bool enableNextBrother = false;
    bool enablePreviousBrother = false;
    bool isComment = false ;
    Element *element = NULL ;
    if(1 == numberSelected) {
        element = getSelectedItem();
        if(NULL != element) {
            if(element->getType() == Element::ET_COMMENT) {
                isComment = true ;
            }
            if(NULL != element->parent()) {
                enableGoToParent = true;
            }
            bool isLast = false;
            bool isFirst = false;
            element->isFirstOrLastChild(isFirst, isLast);
            enableNextBrother = !isLast;
            enablePreviousBrother = !isFirst ;
        }
    }
    ui.actionAddChildElement->setEnabled(canAddChild && !isExplore);
    ui.actionAppendChildElement->setEnabled(selectSpecials && !isExplore);
    ui.actionAddComment->setEnabled(!isExplore);
    ui.actionAppendComment->setEnabled(selectSpecials && !isExplore);
    ui.actionAppendProcessingInstruction->setEnabled(selectSpecials && !isExplore);
    ui.actionAddProcessingInstruction->setEnabled(!isExplore);
    ui.actionDelete->setEnabled(isSomeItemSelected && !isExplore);
    ui.actionEditInnerBase64Text->setEnabled(isSomeItemSelected && !isExplore);
    ui.actionEditInnerXML->setEnabled(isSomeItemSelected && !isSlave && !isExplore);
    ui.actionEditInnerXMLBase64->setEnabled(isSomeItemSelected && !isExplore);
    ui.actionEdit->setEnabled(isSomeItemSelected);
    ui.actionMoveUp->setEnabled(selectSpecialsUp && !isExplore);
    ui.actionMoveDown->setEnabled(selectSpecialsDown && !isExplore);
    ui.actionCut->setEnabled(isSomeItemSelected);
    ui.actionCopy->setEnabled(isSomeItemSelected);
    ui.actionGo_to_Next_Brother->setEnabled(enableNextBrother);
    ui.actionGo_to_Previous_Brother->setEnabled(enablePreviousBrother);
    ui.actionGo_To_Parent->setEnabled(enableGoToParent);
    ui.actionToggleBookmark->setEnabled(numberSelected > 0);
    ui.actionClearBookmarks->setEnabled((NULL != regola) ? regola->bookmarkSize() > 0 : false);
    ui.actionGotoNextBookmark->setEnabled((NULL != regola) ? regola->nextBookmark() >= 0 : false);
    ui.actionGotoPreviousBookmark->setEnabled((NULL != regola) ? regola->previousBookmark() >= 0 : false);

    ui.actionDeleteBookmarked->setEnabled((NULL != regola) ? (regola->bookmarkSize() >= 0) /*&& !isExplore*/ : false);
    ui.actionCleanBookmarkedContent->setEnabled((NULL != regola) ? (regola->bookmarkSize() >= 0) && !isExplore : false);

    ui.actionHideBrothers->setEnabled(isSomeItemSelected);
    ui.actionHideBrothers->setChecked(!isNormalViewState);
    ui.actionShowCurrentElementTextBase64->setEnabled(isSomeItemSelected && !isExplore);
    ui.actionShowCurrentElementTextBase64->setChecked(isShownAsBase64);
    ui.actionReload->setEnabled((NULL != regola) && !regola->fileName().isEmpty());
    enableZoom();
    ui.actionCopyPathToClipboard->setEnabled((NULL != regola) && !regola->fileName().isEmpty());
    ui.actionAddCurrentDirectory->setEnabled((NULL != regola) && !regola->fileName().isEmpty());
    // TODO: test
    //ui.testNext->setEnabled((NULL!=regola)?regola->nextBookmark()>=0:false);
    //ui.testPrev->setEnabled((NULL!=regola)?regola->previousBookmark()>=0:false);

    ui.actionValidate->setEnabled((NULL != regola) && !isExplore);

    ui.actionValidateFile->setEnabled((NULL != regola) && ! regola->userDefinedXsd().isEmpty() && !isExplore);
    ui.actionValidateNewFile->setEnabled((NULL != regola) && !isExplore);
    ui.actionTransforminSnippet->setEnabled(isSomeItemSelected && !isExplore);

    ui.actionViewAsXsd->setEnabled(isXSDPresent && !isExplore);

    ui.actionTransformInComment->setEnabled(isSomeItemSelected && !isExplore && !isComment);
    ui.actionExtractElementsFromComment->setEnabled(isSomeItemSelected && !isExplore && isComment);

    ui.actionInsertNoNamespaceSchemaReferenceAttributes->setEnabled((NULL != regola) && !isExplore);
    ui.actionInsertSchemaReferenceAttributes->setEnabled((NULL != regola) && !isExplore);
    //TODO: always???? ui.actionExtractFragmentsFromFile->setEnabled(XXX);

    ui.actionNewUsingXMLSchema->setEnabled(true);
    ui.actionNewUsingXMLSchema->setVisible(true);

    evaluateSingleItemLeaves(element);

    ui.actionCompareXSD->setEnabled(isXSDPresent && !isExplore);
    ui.actionExportAttributesSiblingsAsCSVClipboard->setEnabled(isSomeItemSelected);

    ui.actionVisAttrApplyTemporaryProfile->setEnabled(isElementSelected);
    ui.actionVisAttrSaveCurrenteProfile->setEnabled((NULL != _attrFilter) && _attrFilter->isTemporary());
    ui.actionVisAttrRemoveCurrentProfile->setEnabled(NULL != _attrFilter);
    ui.actionViewGraphic->setEnabled(!regola->isEmpty(true));
}

void MainWindow::evaluateSingleItemLeaves(Element *element)
{
    bool isHiddenLeaves = false;
    bool isVisibleLeaves = false;
    if(NULL != element) {
        if(!element->isALeaf()) {
            if(element->areChildrenLeavesHidden(element->getUI())) {
                isHiddenLeaves = true ;
            } else {
                isVisibleLeaves = true;
            }
        }
    }
    ui.actionHideLeafChildren->setEnabled(isVisibleLeaves);
    ui.actionShowLeafChildren->setEnabled(isHiddenLeaves);
}

void MainWindow::enableZoom()
{
    PaintInfo *paintInfo = ui.editor->getPaintInfo();
    ui.actionZoomIn->setEnabled(paintInfo->canZoomIn());
    ui.actionZoomOut->setEnabled(paintInfo->canZoomOut());
}

Element *MainWindow::getSelectedItem()
{
    return ui.editor->getSelectedItem();
}

void MainWindow::resetTree()
{
    ui.editor->resetTree();
}

void MainWindow::on_actionOpen_triggered()
{
    openFileUsingDialog(getRegola()->fileName());
}

void MainWindow::openFileUsingDialog(const QString folderPath)
{
    if(!MainWindow::checkAbandonChanges()) {
        return ;
    }
    QString filePath = QFileDialog::getOpenFileName(
                           this, tr("Open File"),
                           folderPath,
                           tr("XML files (*.xml);;XML Schema files (*.xsd);;All files (*);;")
                       );
    if(!filePath.isEmpty()) {
        loadFile(filePath);
    }
}

void MainWindow::on_actionNewFromClipboard_triggered()
{
    if(!MainWindow::checkAbandonChanges()) {
        return ;
    }
    ui.editor->onActionNewFromClipboard();
    removeAttributesFilter();
}


void MainWindow::on_actionResizeToContents_triggered()
{
    ui.editor->resizeTreeColumns();
}

void MainWindow::on_actionSaveAs_triggered()
{
    Regola * regola = getRegola();
    if(NULL == regola) {
        error(tr("No data to save."));
        return ;
    }
    if(regola->isEmpty(false)) {
        error(tr("Cannot write an empty file."));
        return ;
    }
    QString newFilePath = askFileName(regola->fileName());
    if(newFilePath.isEmpty()) {
        return ;
    }
    if(!regola->write(newFilePath)) {
        error(tr("Error saving data. Old file is still intact."));
        return ;
    }
    if(ui.editor->displayMode() != qxmledit::SCAN) {
        regola->setFileName(newFilePath);
    }
    updateRecentFilesMenu(newFilePath);
    regola->setModified(false);
    data->sessionManager()->enrollFile(newFilePath);
    statusBar()->showMessage(tr("File saved"), SHORT_TIMEOUT);
}

void MainWindow::on_actionSaveACopyAs_triggered()
{
    Regola * regola = getRegola();
    if(NULL == regola) {
        error(tr("No data to save."));
        return ;
    }
    if(regola->isEmpty(false)) {
        error(tr("Cannot write an empty file."));
        return ;
    }
    bool modifiedStatus = regola->isModified();
    QString newFilePath = askFileName(regola->fileName());
    if(newFilePath.isEmpty()) {
        return ;
    }
    if(!regola->write(newFilePath, false)) {
        error(tr("Error saving data. Old file is still intact."));
        return ;
    }
    updateRecentFilesMenu(newFilePath);
    regola->setModified(modifiedStatus);
    statusBar()->showMessage(tr("File saved"), SHORT_TIMEOUT);
}

void MainWindow::on_actionSave_triggered()
{
    Regola * regola = getRegola();
    if(!ui.editor->isActionNoScanMode()) {
        return ;
    }
    if(ui.editor->displayMode() == qxmledit::SCAN) {
        return ;
    }
    if(regola->fileName().isEmpty()) {
        on_actionSaveAs_triggered();
        return ;
    }
    if(regola->isEmpty(false)) {
        error(tr("Cannot write empty file."));
        return ;
    }

    // scrivi il nuovo con il nome vecchio+estensione
    QString newFilePath = regola->fileName() + ".new_new~"  ;
    if(!regola->write(newFilePath)) {
        error(tr("Error saving data. Old file is still intact."));
        return ;
    }
    // rinomina il vecchio
    QString backupFilePath = regola->fileName() + "~"  ;
    if(QFile::exists(backupFilePath)) {
        if(!QFile::remove(backupFilePath)) {
            error(tr("Error saving data: cleaning backup file."));
            return ;
        }
    }
    if(!QFile::rename(regola->fileName(), backupFilePath)) {
        error(tr("Error renaming old file. You can access written data at file '%1'. Old data are untouched").arg(newFilePath));
        return ;
    }
    // rinomina il nuovo nel nome vecchio
    if(!QFile::rename(newFilePath, regola->fileName())) {
        error(tr("Error renaming new file. You can access written data at file '%1'. You can find old data in the backup file").arg(newFilePath));
        return ;
    }
    regola->setModified(false);
    Utils::message(this, tr("Operation terminated"));
    statusBar()->showMessage(tr("File saved"), SHORT_TIMEOUT);
}

QString MainWindow::askFileName(const QString &actualName)
{
    QString filePath = QFileDialog::getSaveFileName(this, tr("Save Data"),
                       actualName, tr("XML files (*.xml);;XML Schema files (*.xsd);;All files (*);;"));

    if(!filePath.isEmpty()) {
        return filePath;
    }
    return "";
}

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

void MainWindow::on_actionAddChildElement_triggered()
{
    ui.editor->onActionAddChildElement();
}

void MainWindow::on_actionAppendChildElement_triggered()
{
    ui.editor->onActionAppendChildElement();
}

void MainWindow::on_actionEdit_triggered()
{
    ui.editor->onActionEdit();
}

void MainWindow::on_actionDelete_triggered()
{
    ui.editor->onActionDelete();
}
void MainWindow::on_actionMoveUp_triggered()
{
    ui.editor->onActionMoveUp();
}

void MainWindow::on_actionMoveDown_triggered()
{
    ui.editor->onActionMoveDown();
}

void MainWindow::on_actionCut_triggered()
{
    ui.editor->onActionCut();
}

void MainWindow::on_actionCopy_triggered()
{
    ui.editor->onActionCopy();
}

void MainWindow::on_actionPaste_triggered()
{
    ui.editor->onActionPaste();
}

void MainWindow::on_actionAbout_triggered()
{
    QList<AuthorInfo*> authors = authorsInfo() ;
    AboutDialog about(this,
                      AuthorInfo::appName, AuthorInfo::version, AuthorInfo::copyright,
                      AuthorInfo::license,
                      AuthorInfo::other,
                      authors);
    about.setModal(true);
    about.exec() ;
    deleteAuthorsInfo(authors);
}


void MainWindow::onShowStatusMessage(const QString &message, const bool isLongTimeout)
{
    statusBar()->showMessage(message, isLongTimeout ? LONG_TIMEOUT : SHORT_TIMEOUT);
}

void MainWindow::setFileTitle()
{
    QString windowTitle = APP_TITLE;
    if(!isSlave) {
        windowTitle.append(" - ");
        if(!getRegola()->fileName().isEmpty()) {
            windowTitle.append(getRegola()->fileName());
        } else {
            windowTitle.append(tr("--No file--"));
        }
        windowTitle.append(" [*]");
    }
    setWindowTitle(windowTitle);
}

void MainWindow::closeEvent(QCloseEvent * event)
{
    if(!MainWindow::checkAbandonChanges()) {
        event->ignore();
        return ;
    }
    event->accept();
    if(isAutoDelete) {
        deleteLater();
    }
}

void MainWindow::on_actionQuit_triggered()
{
    if(MainWindow::checkAbandonChanges()) {
        application->quit();
    }
}

bool MainWindow::checkAbandonChanges()
{
    Regola *regola = getRegola();
    if(NULL == regola) {
        return false;
    }

    if(regola->isModified()) {
        if(QMessageBox::Yes != QMessageBox::question(this, APP_TITLE,
                tr("Abandon changes?"),
                QMessageBox::Yes | QMessageBox::No)) {
            return false ;
        }
    }
    return true ;
}


void MainWindow::on_actionExpandAll_triggered()
{
    ui.editor->onActionExpandAll();
}

void MainWindow::on_actionShowAttrLine_triggered()
{
    ui.editor->onActionShowAttrLine(ui.actionShowAttrLine->isChecked());
}

void MainWindow::on_actionAddComment_triggered()
{
    ui.editor->onActionAddComment();
}


void MainWindow::on_actionAppendComment_triggered()
{
    ui.editor->onActionAppendComment();
}

void MainWindow::on_actionAppendProcessingInstruction_triggered()
{
    ui.editor->onActionAppendProcessingInstruction();
}


void MainWindow::on_actionAddProcessingInstruction_triggered()
{
    ui.editor->onActionAddProcessingInstruction();
}

void MainWindow::dragEnterEvent(QDragEnterEvent *event)
{
    if(event->mimeData()->hasFormat("text/uri-list"))
        event->acceptProposedAction();
}

void MainWindow::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(!MainWindow::checkAbandonChanges()) {
                event->ignore();
                return ;
            }
            loadFile(filePath);
        }
        event->acceptProposedAction();
    }
}

void MainWindow::on_actionFind_triggered()
{
    ui.editor->onActionFind();
}

void MainWindow::on_actionGo_To_Parent_triggered()
{
    ui.editor->onActionGoToParent();
}

void MainWindow::on_actionGo_to_Previous_Brother_triggered()
{
    ui.editor->onActionGoToPreviousBrother();
}

void MainWindow::on_actionGo_to_Next_Brother_triggered()
{
    ui.editor->onActionGoToNextBrother();
}

void MainWindow::on_actionClearBookmarks_triggered()
{
    ui.editor->onActionClearBookmarks();
}

void MainWindow::on_actionToggleBookmark_triggered()
{
    ui.editor->onActionToggleBookmark();
}


void MainWindow::on_actionGotoNextBookmark_triggered()
{
    ui.editor->onActionGotoNextBookmark();
}

void MainWindow::on_actionGotoPreviousBookmark_triggered()
{
    ui.editor->onActionGotoPreviousBookmark();
}

void MainWindow::on_actionShowChildIndex_triggered()
{
    ui.editor->onActionShowChildIndex(ui.actionShowChildIndex->isChecked());
}

void MainWindow::on_actionCompactView_triggered()
{
    ui.editor->onActionCompactView(ui.actionCompactView->isChecked());
}

void MainWindow::on_actionFixedSizeAttributes_triggered()
{
    ui.editor->onActionFixedSizeAttributes(ui.actionFixedSizeAttributes->isChecked());
}

void MainWindow::on_actionShowAttributesLength_triggered()
{
    ui.editor->onActionShowAttributesLength(ui.actionShowAttributesLength->isChecked());
}

void MainWindow::on_actionShowElementTextLength_triggered()
{
    ui.editor->onActionShowElementTextLength(ui.actionShowElementTextLength->isChecked());
}

void MainWindow::on_actionCloseThisAllBrothers_triggered()
{
    ui.editor->onActionCloseThisAllBrothers();
}

void MainWindow::on_actionHideBrothers_triggered()
{
    ui.editor->onActionHideBrothers();
}

void MainWindow::on_actionShowCurrentElementTextBase64_triggered()
{
    ui.editor->onActionShowCurrentElementTextBase64(ui.actionShowCurrentElementTextBase64->isChecked());
}

void MainWindow::on_actionShowBase64_triggered()
{
    ui.editor->onActionShowBase64(ui.actionShowBase64->isChecked());
}

void MainWindow::on_actionShowElementSize_triggered()
{
    ui.editor->onActionShowElementSize(ui.actionShowElementSize->isChecked());
}

void MainWindow::on_actionConfigure_triggered()
{
    ConfigurationDialog::doOptions(this, data) ;
}

void MainWindow::on_ok_clicked()
{
    if(NULL != eventLoop) {
        eventLoop->exit(1);
    }
}

void MainWindow::on_cancel_clicked()
{
    if(NULL != eventLoop) {
        eventLoop->exit(0);
    }
}

void MainWindow::setEventLoop(QEventLoop *value)
{
    eventLoop = value ;
}

void MainWindow::on_actionEditInnerXML_triggered()
{
    ui.editor->onActionEditInnerXML();
}

void MainWindow::on_actionEditInnerXMLBase64_triggered()
{
    ui.editor->onActionEditInnerXMLBase64();
}


void MainWindow::on_actionEditInnerBase64Text_triggered()
{
    ui.editor->onActionEditInnerBase64Text();
}

QString MainWindow::getContentAsText()
{
    return ui.editor->getContentAsText();
}

void MainWindow::on_actionZoomIn_triggered()
{
    ui.editor->onActionZoomIn();
    enableZoom();
}

void MainWindow::on_actionZoomOut_triggered()
{
    ui.editor->onActionZoomOut();
    enableZoom();
}

void MainWindow::wheelEvent(QWheelEvent *event)
{
    if((event->modifiers() & Qt::ControlModifier) == Qt::ControlModifier) {
        int numDegrees = event->delta() / 8;
        if(numDegrees > 10) {
            on_actionZoomIn_triggered();
        } else if(numDegrees < -10) {
            on_actionZoomOut_triggered();
        }
        event->accept();
    } else {
        event->ignore();
    }
}



void MainWindow::on_actionCompare_triggered()
{
#ifdef DEBUG_COMPARE
#define FILE_TEST_C   "../src/data/compare_base.xml"
    loadFile(FILE_TEST_C);
#endif
    Regola *regola = getRegola();
    if(NULL == regola) {
        errorNoRule();
        return ;
    }
    CompareBridge::doCompare(this, true, regola, this, lastFilesNames);
}

void MainWindow::on_actionReload_triggered()
{
    Regola *regola = getRegola();
    if(NULL == regola) {
        return;
    }
    QString filePath = regola->fileName();
    if(filePath.isEmpty()) {
        return ;
    }
    if(!MainWindow::checkAbandonChanges()) {
        return ;
    }
    loadFile(filePath);
}

void MainWindow::onRecentFile()
{
    QAction *actionFile = qobject_cast<QAction*>(sender());
    if(NULL != actionFile) {
        QString filePath = actionFile->data().toString();
        if(!filePath.isEmpty()) {
            if(!MainWindow::checkAbandonChanges()) {
                return ;
            }
            loadFile(filePath);
        }
    }
}

void MainWindow::onPreferredDir()
{
    QAction *actionFile = qobject_cast<QAction*>(sender());
    if(NULL != actionFile) {
        QString dirPath = actionFile->data().toString();
        if(!MainWindow::checkAbandonChanges()) {
            return ;
        }
        QString filePath = QFileDialog::getOpenFileName(this, tr("Open File"),
                           dirPath, tr("XML files (*.xml);;XML Schema files (*.xsd);;All files (*);;"));
        if(!filePath.isEmpty()) {
            loadFile(filePath);
        }
    }
}

void MainWindow::updateMRU(QStringList &entries, const QString &entry, QList<QAction*>actions)
{
    if(entry.isEmpty()) {
        return;
    }
    entries.removeOne(entry);
    entries.insert(0, entry);
    updateEntriesMenu(entries, actions);
    if(!Config::saveStringArray(Config::KEY_MAIN_RECENTFILES, lastFilesNames)) {
        Utils::error(tr("Error registering last open files."));
    }
}

void MainWindow::updateEntriesMenu(QStringList &entries, QList<QAction*>actions)
{
    int listSize = entries.size();
    int nMaxActions = actions.size();
    for(int i = 0 ; i < nMaxActions ; i ++) {
        QAction *action = actions.at(i);
        if(i < listSize) {
            QString data = entries.at(i);
            action->setData(data);
            action->setText(data);
            action->setVisible(true);
        } else {
            action->setVisible(false);
        }
    }
}

void MainWindow::updateRecentFilesMenu(const QString &filePath)
{
    updateMRU(lastFilesNames, filePath, lastFiles);
}

void MainWindow::loadRecentFilesSettings()
{
    lastFilesNames.clear();
    Config::loadStringArray(Config::KEY_MAIN_RECENTFILES, lastFilesNames);
    updateEntriesMenu(lastFilesNames, lastFiles);
}

void MainWindow::on_actionCopyPathToClipboard_triggered()
{
    ui.editor->onActionCopyPathToClipboard();
}

void MainWindow::on_actionAddCurrentDirectory_triggered()
{
    Regola *regola = getRegola();
    if((NULL != regola) && !regola->fileName().isEmpty()) {
        if(!PreferredDirs::checkNewDir(preferredDirsNames.size(), true)) {
            return;
        }
        QFileInfo fileInfo(regola->fileName());
        QString dirPath = fileInfo.path();
        if(!PreferredDirs::checkDuplicateDir(preferredDirsNames, dirPath, true)) {
            return ;
        }
        preferredDirsNames.append(dirPath);
        updatePreferredDirs(preferredDirsNames, preferredDirs);
    }
}

void MainWindow::on_actionEditPreferredDirectories_triggered()
{
    if(configurePreferredDirs(this, preferredDirsNames)) {
        updatePreferredDirs(preferredDirsNames, preferredDirs);
    }
}

void MainWindow::updatePreferredDirs(QStringList &entries, QList<QAction*>actions)
{
    entries.sort();
    updateEntriesMenu(entries, actions);
    if(!Config::saveStringArray(Config::KEY_MAIN_PREFDIRS, preferredDirsNames)) {
        Utils::error(tr("Error registering preferred directories."));
    }
}
void MainWindow::loadPreferredDirsSettings()
{
    preferredDirsNames.clear();
    Config::loadStringArray(Config::KEY_MAIN_PREFDIRS, preferredDirsNames);
    preferredDirsNames.sort();
    updateEntriesMenu(preferredDirsNames, preferredDirs);
}

void MainWindow::on_actionValidate_triggered()
{
    ui.editor->onActionValidate();
}

void MainWindow::on_actionValidateFile_triggered()
{
    ui.editor->onActionValidateFile();
}

void MainWindow::on_actionValidateNewFile_triggered()
{
    ui.editor->onActionValidateNewFile();
}

void MainWindow::on_actionInsertSnippet_triggered()
{
    Regola* regola = getRegola();
    if(NULL == regola) {
        return;
    }
    Regola* newRegola = chooseSnippets(data, this) ;
    if(NULL != newRegola) {
        ui.editor->insertSnippet(newRegola);
        delete newRegola;
    }
}

void MainWindow::on_actionConfigureSnippets_triggered()
{
    editSnippets(data, this);
}

void MainWindow::on_actionExecuteAutoTest_triggered()
{
    Test::executeTests();
}

void MainWindow::on_actionTransforminSnippet_triggered()
{
    if(!ui.editor->isActionMode()) {
        return ;
    }

    Element *element = getSelectedItem();
    if(NULL != element) {
        QDomDocument    document;
        if(element->generateDom(document, document)) {
            QString clipboardText = document.toString(4);
            insertFragmentInSnippets(this, data, clipboardText) ;
        }
    }
}

void MainWindow::onPlugin()
{
    QAction *actionFile = qobject_cast<QAction*>(sender());
    if(NULL != actionFile) {
        QString pluginId = actionFile->data().toString();
        if(!pluginId .isEmpty()) {
            IQXmlEditPlugIn* plugin = data->plugins()[pluginId];
            if(NULL != plugin) {
                plugin->go(this, getRegola());
            }
        }
    }
}

bool MainWindow::buildPluginsMenu(const char *method, QMenu *parent)
{
    bool isOk = true;
    pluginsCmds.clear();
    QMapIterator<QString, IQXmlEditPlugIn*> pluginsIterator(data->plugins());
    while(pluginsIterator.hasNext()) {
        pluginsIterator.next();
        QAction *action = new QAction(this);
        if(NULL != action) {
            pluginsCmds.append(action);
            connect(action, SIGNAL(triggered()), this, method);
            IQXmlEditPlugIn* plugin = pluginsIterator.value();
            action->setData(pluginsIterator.key());
            action->setText(plugin->name());
            action->setVisible(true);
            parent->addAction(action);
        } else {
            isOk = false ;
        }
    }
    return isOk ;
}

void MainWindow::on_actionDeleteBookmarked_triggered()
{
    ui.editor->onActionDeleteBookmarked();
}

void MainWindow::on_actionCleanBookmarkedContent_triggered()
{
    ui.editor->onActionCleanBookmarkedContent();
}


void MainWindow::setDisplayMode(const qxmledit::EDisplayMode value)
{
    ui.editor->setDisplayMode(value);
    navigationModeIndicator->setExploreMode(value);
}


//---------------------- load trigger

void MainWindow::on_actionXplore_triggered()
{
    if(!MainWindow::checkAbandonChanges()) {
        return ;
    }
    QString filePath = QFileDialog::getOpenFileName(this, tr("Open File"),
                       getRegola()->fileName(), tr("XML files (*.xml);;XML Schema files (*.xsd);;All files (*);;"));
    if(!filePath.isEmpty()) {
        loadFileXplore(filePath);
    }
}

void MainWindow::loadFileXplore(const QString &filePath)
{
    if(!filePath.isEmpty()) {
        doLoadFileXplore(filePath);
        updateRecentFilesMenu(filePath);
    } else {
        Utils::error(tr("File name empty. Unable to load it."));
    }
}


bool MainWindow::loadFile(const QString &filePath)
{
    bool fileLoaded = false;
    if(!filePath.isEmpty()) {
        QFile file(filePath);
        if(file.open(QIODevice::ReadOnly)) {
            QDomDocument document;
            QString errorMsg ;
            int errorLine = 0, errorColumn = 0;
            if(document.setContent(&file, &errorMsg, &errorLine, &errorColumn)) {
                data->sessionManager()->enrollFile(filePath);
                setDocument(document, filePath, true);
                updateRecentFilesMenu(filePath);
                autoLoadValidation();
                fileLoaded = true ;
            } else {
                showLoadFileError(filePath, errorMsg, errorLine, errorColumn);
            }
            file.close();
        } else {
            Utils::error(QString(tr("Unable to load file.\n Error code is '%1'")).arg(file.error()));
        }
    } else {
        Utils::error(tr("File name empty. Unable to load it."));
    }
    return fileLoaded;
}

void MainWindow::showLoadFileError(const QString &filePath, const QString &errorMsg, const int errorLine, const int errorColumn)
{
    // decode error
    QString msg = Utils::decodeXMLError(errorMsg);
    QString error = tr("Unable to parse XML, file ='%1', at line:%3, column:%4, cause: '%2' ").arg(filePath).arg(msg).arg(errorLine).arg(errorColumn);
    Utils::error(error);
}


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

//TODO: error checking
void MainWindow::doLoadFileXplore(const QString &filePath)
{
    ui.editor->doLoadFileXplore(filePath);
    statusBar()->showMessage(tr("Data loaded"), SHORT_TIMEOUT);
}

//TODO: error checking
void MainWindow::onNewMessage(const QString &newMessage)
{
    statusBar()->showMessage(newMessage, SHORT_TIMEOUT);
}

//TODO: error checking
void MainWindow::setDocument(QDomDocument &document, const QString &filePath, const bool isSetState)
{
    ui.editor->setDocument(document, filePath, isSetState);
    statusBar()->showMessage(tr("Data loaded"), SHORT_TIMEOUT);
}

void MainWindow::on_actionHideView_triggered()
{
    ui.editor->onActionHideView(ui.actionHideView->isChecked());
}

void MainWindow::on_actionSearchInFiles_triggered()
{
    searchInFiles(this);
}

void MainWindow::on_actionHelpOnQXmlEdit_triggered()
{
    QString resourceHelp = data->getDocsDir() + "/" + HELP_FILE ;
    QDesktopServices::openUrl(QUrl::fromLocalFile(resourceHelp));
}

bool MainWindow::isValidXsd()
{
    Regola * regola = getRegola();
    if(NULL != regola) {
        return regola->isValidXsd();
    }
    return false;
}

void MainWindow::on_actionViewAsXsd_triggered()
{
    ui.editor->onActionViewAsXsd();
}

Regola *MainWindow::getRegola()
{
    return ui.editor->getRegola();
}

SessionStateWidget *MainWindow::sessionStateWidget()
{
    return _sessionStateWidget ;
}

//void MainWindow::loadSchema(const QString &schemaURL)
//{
//    ui.editor->loadSchema(schemaURL);
//} TODO: controllare se spostato

void MainWindow::schemaLoadComplete(const QString &newLabel)
{
    setSchemaLabel(newLabel);
    statusBar()->update();
}

void MainWindow::on_actionAllowedSchemaElements_triggered()
{
    ui.editor->onActionAllowedSchemaElements();
}

/** Substitute the element text
  */
void MainWindow::on_actionPasteAndSubstituteText_triggered()
{
    ui.editor->onActionPasteAndSubstituteText();
}


void MainWindow::on_actionNewUsingXMLSchema_triggered()
{
    if(!verifyAbandonChanges()) {
        return ;
    }
    bool isOk = false;
    QString schemaURL;
    schemaURL = chooseSchemaFile(this, isOk) ;
    if(isOk) {
        ui.editor->onActionNewUsingXMLSchema(schemaURL);
    }
    removeAttributesFilter();
}

bool MainWindow::verifyAbandonChanges()
{
    Regola *regola = getRegola();
    if(!regola->isEmpty(false) && regola->isModified()) {
        if(QMessageBox::Yes != QMessageBox::question(this, APP_TITLE,
                tr("Abandon changes?"),
                QMessageBox::Yes | QMessageBox::No)) {
            return false ;
        }
    }
    return true ;
}

QWidget *MainWindow::getMainWidget()
{
    return this ;
}

QString MainWindow::getAppTitle()
{
    return APP_TITLE ;
}


void MainWindow::on_actionTransformInComment_triggered()
{
    ui.editor->onActionTransformInComment();
}

void MainWindow::on_actionExtractElementsFromComment_triggered()
{
    ui.editor->onActionExtractElementsFromComment();
}

void MainWindow::autoLoadValidation()
{
    ui.editor->autoLoadValidation();
}

void MainWindow::setSchemaLabel(const QString &newLabel)
{
    labelSchema->setText(newLabel);
}


void MainWindow::on_actionInsertNoNamespaceSchemaReferenceAttributes_triggered()
{
    ui.editor->onActionInsertNoNamespaceSchemaReferenceAttributes();
}

void MainWindow::on_actionInsertSchemaReferenceAttributes_triggered()
{
    ui.editor->onActionInsertSchemaReferenceAttributes();
}

void MainWindow::on_actionExtractFragmentsFromFile_triggered()
{
    if(!verifyAbandonChanges()) {
        return ;
    }
    ExtractResults *results = new  ExtractResults();
    if(NULL == results) {
        Utils::errorOutOfMem(this);
        return ;
    }
    extractFragments(results, this);
    if(!(results->isError() || results->isAborted())) {
        if(results->_optimizeSpeed) {
            delete results ;
            return ;
        }
        if(results->numFragments() == 0) {
            Utils::message(tr("No fragments found"));
            delete results ;
            return ;
        }
        cleanExtractResults();
        _extractResult = results ;
        ui.editor->setNavigationDataAndEnable(1, _extractResult->numFragments());
        showNavigationBox();
    } else {
        delete results ;
    }
}

void MainWindow::showNavigationBox()
{
    ui.editor->showNavigationBox();
}

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

void MainWindow::loadCurrentPage(const int page)
{
    if(NULL != _extractResult) {
        StringOperationResult result;
        _extractResult->loadFragment(page, result);
        if(!result.isError()) {
            ui.editor->loadText(result.result(), false, true);
            // printf("xml is:'%s'\n", result.result().toAscii().data());
        } else {
            Utils::error(this, tr("Error loading data."));
        }
    }
}

void MainWindow::welcomeDialog()
{
    Regola *regola = getRegola() ;
    if(data->shouldShowWelcomeDialog()) {
        WelcomeDialog welcome(lastFilesNames, this);
        welcome.exec();
        bool welcomeDialogEnabled = true ;
        switch(welcome.result()) {
        default:
            // no action;
            break;
        case WelcomeDialog::E_OPENSCHEMA:
            on_actionOpen_triggered();
            if(NULL != regola) {
                ui.editor->onActionViewAsXsd();
            }
            break;
        case WelcomeDialog::E_RECENTFILECHOOSE: {
            QString filePath = welcome.fileChoosen();
            if(!filePath.isEmpty()) {
                loadFile(filePath);
            }
        }
        break;
        case WelcomeDialog::E_SCANFILE:
            on_actionExtractFragmentsFromFile_triggered();
            break;
        case WelcomeDialog::E_CLOSE:
            break;
        case WelcomeDialog::E_MANAGESESSIONS:
            on_actionManageSessions_triggered();
            break;
        case WelcomeDialog::E_VIEWDATA:
            on_actionViewData_triggered();
            break;
        case WelcomeDialog::E_COMPARE:
            on_actionCompareFiles_triggered();
            break;
        }
        welcomeDialogEnabled = welcome.isShowNextTime();
        data->enableWelcomeDialog(welcomeDialogEnabled);
    }
}

void MainWindow::on_actionWelcomeDialog_triggered()
{
    welcomeDialog();
}

void MainWindow::triggersWelcomeDialog()
{
    ui.actionWelcomeDialog->trigger();
}

void MainWindow::error(const QString& message)
{
    Utils::error(this, message);
}

void MainWindow::error(QWidget *widget, const QString& message)
{
    Utils::error(widget, message);
}


void MainWindow::warning(const QString& message)
{
    Utils::warning(this, message);
}

void MainWindow::message(const QString& message)
{
    Utils::message(this, message);
}

bool MainWindow::askYN(const QString & message)
{
    return Utils::askYN(this, message);
}

bool MainWindow::askYN(QWidget *parent, const QString & message)
{
    return Utils::askYN(parent, message);
}

QString MainWindow::msgOutOfMem()
{
    return Utils::msgOutOfMem();
}

void MainWindow::errorNoSel(QWidget * parent)
{
    Utils::errorNoSel(parent);
}

void MainWindow::errorOutOfMem(QWidget *parent)
{
    Utils::errorOutOfMem(parent);
}


QTreeWidget *MainWindow::getMainTreeWidget()
{
    return ui.editor->getMainTreeWidget();
}

XmlEditWidget *MainWindow::getEditor()
{
    return ui.editor ;
}

void MainWindow::on_actionHideAllLeafChildren_triggered()
{
    ui.editor->onActionHideAllLeafChildren();
}

void MainWindow::on_actionHideLeafChildren_triggered()
{
    evaluateSingleItemLeaves(ui.editor->onActionHideLeafChildren());
}

void MainWindow::on_actionShowLeafChildren_triggered()
{
    evaluateSingleItemLeaves(ui.editor->onActionShowLeafChildren());
}

void MainWindow::on_actionShowAllLeafChildren_triggered()
{
    ui.editor->onActionShowAllLeafChildren();
}

void MainWindow::on_actionColumnView_triggered()
{
    Regola *regola = getRegola();
    if(NULL != regola) {
        ColumnarView::showModal(this, regola);
    }
}

Ui::MainWindow *MainWindow::getUI()
{
    return &ui;
}

void MainWindow::on_actionShowCurrentSessionPanel_triggered()
{
    if(data->sessionManager()->isEnabled()) {
        ui.sessionTree->setVisible(ui.actionShowCurrentSessionPanel->isChecked());
    }
}

void MainWindow::onSessionEnablingChanged()
{
    if(isSlave) {
        return ;
    }
    bool isEnabled = false ;
    if(data->sessionManager()->isEnabled()) {
        isEnabled = true ;
    }
    ui.actionShowCurrentSessionPanel->setVisible(isEnabled);
    ui.actionShowCurrentSessionPanel->setEnabled(isEnabled);
    if(!isEnabled) {
        ui.sessionTree->setVisible(false);
        ui.actionShowCurrentSessionPanel->setChecked(false);
    }
    ui.actionNewSession->setEnabled(isEnabled);
}

void MainWindow::onSessionfileLoadRequest(const QString& filePath)
{
    if(!filePath.isEmpty()) {
        if(!MainWindow::checkAbandonChanges()) {
            return ;
        }
        loadFile(filePath);
    }
}

void MainWindow::onSessionFolderOpenRequest(const QString& path)
{
    openFileUsingDialog(path);
}

void MainWindow::on_actionNewSession_triggered()
{
    SessionManager *sessionManager = data->sessionManager();
    if(!sessionManager->isEnabled()) {
        return;
    }
    QString newSessionName = SessionRequester::askForNewSessionName(this, this);
    if(!newSessionName.isEmpty()) {
        if(sessionManager->state() != Session::NoSession) {
            sessionManager->closeSession();
        }
        if(NULL == sessionManager->newSession(newSessionName)) {
            Utils::error(this, tr("Error activating sessions."));
            return ;
        }
        data->setActiveSessionCode(sessionManager->currentSession()->id());
        data->setActiveSessionState(sessionManager->currentSession()->state());
    }
}

void MainWindow::on_actionPauseSession_triggered()
{
    SessionManager *sessionManager = data->sessionManager();
    if(!sessionManager->isEnabled()) {
        return;
    }
    sessionManager->pauseSession();
    data->setActiveSessionState(sessionManager->currentSession()->state());
}

void MainWindow::on_actionResumeSession_triggered()
{
    SessionManager *sessionManager = data->sessionManager();
    if(!sessionManager->isEnabled()) {
        return;
    }
    sessionManager->resumeSession();
    data->setActiveSessionState(sessionManager->state());
}

void MainWindow::on_actionCloseSession_triggered()
{
    SessionManager *sessionManager = data->sessionManager();
    if(!sessionManager->isEnabled()) {
        return;
    }
    sessionManager->closeSession();
    data->setActiveSessionCode(0);
    data->setActiveSessionState(sessionManager->state());
}

void MainWindow::on_actionManageSessions_triggered()
{
    SessionManager *sessionManager = data->sessionManager();
    if(!sessionManager->isEnabled()) {
        return;
    }
    sessionManager->manageSessions(this, this);
}

void MainWindow::on_actionSessionDetails_triggered()
{
    SessionManager *sessionManager = data->sessionManager();
    if(!sessionManager->isEnabled()) {
        return;
    }
    sessionManager->editCurrentSession(this, this);
}

void MainWindow::onShowSessionManagement()
{
    ui.actionManageSessions->trigger();
}

void MainWindow::on_actionUndo_triggered()
{
    ui.editor->undo();
}

void MainWindow::on_actionRedo_triggered()
{
    ui.editor->redo();
}

void MainWindow::onUndoStateUpdated(const bool isUndo, const bool isRedo)
{
    updateUndoState(isUndo, isRedo);
}

void MainWindow::updateUndoState(const bool isUndo, const bool isRedo)
{
    ui.actionUndo->setEnabled(isUndo) ;
    ui.actionRedo->setEnabled(isRedo) ;
}

void MainWindow::setUIDelegate(UIDelegate* newDelegate)
{
    uiDelegate = newDelegate ;
}

void MainWindow::on_actionNewWindow_triggered()
{
    MainWindow *newWindow = new MainWindow(false, application, data);
    if(NULL != newWindow) {
        newWindow->isAutoDelete = true ;
        newWindow->show();
    } else {
        Utils::error(tr("Error opening a new window."));
    }
}

void MainWindow::on_actionViewData_triggered()
{
    DataVisualization::viewData(this, "");
}

void MainWindow::loadVisFile(const QString &fileName)
{
    DataVisualization::viewData(this, fileName);
}

void MainWindow::on_actionBase64Tools_triggered()
{
    data->uiServices()->doBase64Dialog();
}

void MainWindow::on_actionEncodingTools_triggered()
{
    data->uiServices()->doEncodingDialog();
}

void MainWindow::on_actionCompareXSD_triggered()
{
    ui.editor->onActionCompareXSD(getRegola()->fileName());
}

void MainWindow::on_actionExportAttributesSiblingsAsCSVClipboard_triggered()
{
    ui.editor->onActionExportSiblingsAttributesAsCSVClipboard();
}

void MainWindow::removeAttributesFilter()
{
    if(NULL != _attrFilter) {
        delete _attrFilter ;
        _attrFilter = NULL ;
    }
}

void MainWindow::on_actionVisAttrApplyTemporaryProfile_triggered()
{
    AttributeFilter * filter = AttributeFilterDialog::display(this, ui.editor->getSelectedItem());
    if(NULL != filter) {
        filter->setTemporary(true);
        setAttributeProfile(filter);
    }
}

QString MainWindow::askNameForCurrentAttributeFilter()
{
    QString result = "" ;
    bool repeat = true ;
    while(repeat) {
        repeat = false ;
        bool isOk = false;
        result = QInputDialog::getText(this, tr("Save the Temporary Profile"), tr("Profile name:"), QLineEdit::Normal, "", &isOk);
        if(!isOk) {
            return "" ;
        }
        if(result.isEmpty()) {
            repeat = true ;
        }
    }
    return result;
}

void MainWindow::on_actionVisAttrSaveCurrenteProfile_triggered()
{
    if((NULL != _attrFilter) && _attrFilter->isTemporary()) {
        QString name = askNameForCurrentAttributeFilter();
        if(data->attributeFilterManagement()->insertFilterAsProfile(name, _attrFilter)) {
            _attrFilter->setTemporary(false);
            onComputeSelectionState();
            Utils::message(this, tr("Filter saved.")) ;
        } else {
            Utils::error(this, tr("Error saving the filter."));
        }
    }
}


void MainWindow::on_actionVisAttrManageProfiles_triggered()
{
    AttributeFilter *newFilter = data->attributeFilterManagement()->showProfiles(this, this);
    if(NULL != newFilter) {
        setAttributeProfile(newFilter);
    }
}

void MainWindow::on_actionVisAttrRemoveCurrentProfile_triggered()
{
    if(NULL != _attrFilter) {
        setAttributeProfile(NULL);
    }
}

void MainWindow::setAttributeProfile(AttributeFilter *filter)
{
    ui.editor->getPaintInfo()->setAttributeFilter(filter);
    removeAttributesFilter();
    _attrFilter = filter;
    ui.editor->getRegola()->redisplay();
    _attributeFilterWidget->setState(NULL != filter);
    onComputeSelectionState();
}

void MainWindow::on_actionCompareFiles_triggered()
{
    Regola dummy;
    CompareBridge::doCompare(this, false, &dummy, this, lastFilesNames);
}

void MainWindow::on_actionSpring_and_Forces_triggered()
{
    QList<TagNode*> dataList;
    NodesRelationsDialog dialog(true, dataList);
    dialog.exec();
}

void MainWindow::on_actionViewGraphic_triggered()
{
    ui.editor->viewNodes();
}
