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

#include <ktip.h>
#include <kconfigdialog.h>
#include <ktoolbar.h>
#include <kactionmenu.h>
#include <kmessagebox.h>
#include <kservice.h>
#include <kxmlguifactory.h>
#include <knotification.h>
#include <kcombobox.h>
#include <kselectaction.h>
#include <kstringhandler.h>
#include <ktoolbarpopupaction.h>
#include <kaboutdata.h>
#include <kformat.h>
#include <ktoggleaction.h>
#include <kencodingfiledialog.h>
#include <kmessagewidget.h>
#include <kstandardaction.h>
#include <kactioncollection.h>
#include <knotifyconfigwidget.h>

#ifdef KActivities_FOUND
#include <KActivities/ResourceInstance>
#endif

#include <qurlquery.h>
#include <qpushbutton.h>
#include <qtemporaryfile.h>
#include <qsplashscreen.h>
#include <qaction.h>
#include <QDomDocument>
#include <qtabwidget.h>
#include <qlineedit.h>
#include <qsystemtrayicon.h>
#include <qfiledialog.h>
#include <qmenu.h>
#include <qtabbar.h>
#include <qprogressbar.h>
#include <qheaderview.h>
#include <qapplication.h>
#include <qdir.h>
#include <QResizeEvent>
#include <qdesktopservices.h>
#include <qscrollarea.h>
#include <qcompleter.h>
#include <qtextcodec.h>
#include <qcollator.h>

#include "skgtraces.h"
#include "skgservices.h"
#include "skginterfaceplugin.h"
#include "skgtransactionmng.h"
#include "skgzoomselector.h"
#include "skgtreeview.h"
#include "skgwebview.h"
#include <kservicetypetrader.h>

struct doublePointer {
    void* p1;
    void* p2;
};

struct historyPage {
    SKGTabPage::SKGPageHistoryItem current;
    QList<SKGTabPage::SKGPageHistoryItem> next;
    QList<SKGTabPage::SKGPageHistoryItem> previous;
};

class SKGMainPanelPrivate
{
public:
    Ui::skgmainpanel_base ui;
    Ui::skgmainpanel_pref uipref;

    SKGTabWidget* m_tabWidget;
    QSplashScreen* m_splashScreen;

    SKGDocument* m_currentDocument;

    QList<SKGInterfacePlugin*> m_pluginsList;
    QMap<QString, QPointer<QAction> > m_registeredGlogalAction;
    QList<QWidget*> m_hiddenWidgets;
    QList<historyPage> m_historyClosedPages;

    QMenu* m_contextMenu;
    QAction* m_actHideContextItem;
    QAction* m_actShowAllContextItems;
    QAction* m_actDelete;
    QAction* m_addTabAction;
    QAction* m_tipOfDayAction;
    QAction* m_closePageAction;
    QAction* m_closeAllPagesAction;
    QAction* m_closeAllOtherPagesAction;
    QAction* m_switchPinState;
    QAction* m_saveDefaultStateAction;
    QAction* m_resetDefaultStateAction;
    QAction* m_overwriteBookmarkStateAction;
    QAction* m_configureAction;
    QAction* m_enableEditorAction;
    QAction* m_fullScreenAction;
    QAction* m_actLock;
    QAction* m_actUnLock;
    QAction* m_reopenLastClosed;
    QSystemTrayIcon* m_kSystemTrayIcon;
    QLabel* m_kNormalMessage;
    SKGZoomSelector* m_zoomSelector;

    KToolBarPopupAction* m_previousAction;
    KToolBarPopupAction* m_nextAction;
    KToolBarPopupAction* m_buttonMenuAction;
    KToggleAction* m_showMenuBarAction;

    QMenu* m_previousMenu;
    QMenu* m_nextMenu;
    QMenu* m_buttonMenu;
    QWidget* m_mainWidget;
    QVBoxLayout* m_mainLayout;

    doublePointer m_progressObjects;
    bool m_middleClick;
    bool m_saveOnClose;
    QString m_fileName;

#ifdef KActivities_FOUND
    KActivities::ResourceInstance* m_activityResourceInstance;
#endif

    static bool m_currentActionCanceled;
    static SKGMainPanel* m_mainPanel;

    static int progressBarCallBack(int iPos, qint64 iTime, const QString& iName, void* iProgressBar)
    {
        Q_UNUSED(iTime);
        QProgressBar* progressBar = NULL;
        QPushButton* button = NULL;
        doublePointer* pointers = static_cast<doublePointer*>(iProgressBar);
        if (pointers) {
            progressBar = static_cast<QProgressBar*>(pointers->p1);
            button = static_cast<QPushButton*>(pointers->p2);
        }

        bool visible = (iPos > 0 && iPos <= 100);
        if (progressBar) {
            QString commonFormat = "%p%";
            /*if (iPos > 0) {
             *           qint64 estimatedTime = 100 * iTime / iPos;
             *           qint64 remainingTime = estimatedTime - iTime;
             *           commonFormat = commonFormat % " - " % i18nc("To print a remaining time (in seconde) / estimated time (in second)", "%1s / %2s",
             *                          SKGServices::intToString(remainingTime / 1000),
             *                          SKGServices::intToString(estimatedTime / 1000));
            }*/
            progressBar->setFormat(iName.isEmpty() ? commonFormat : commonFormat % '\n' % iName);
            progressBar->setValue(iPos);
            progressBar->setVisible(visible);
            if (iPos == 100) {
                QTimer::singleShot(300, Qt::CoarseTimer, progressBar, SLOT(hide()));
            }
            progressBar->setToolTip(iName);
        }
        if (button) {
            button->setVisible(visible);
            if (iPos == 100) {
                QTimer::singleShot(300, Qt::CoarseTimer, button, SLOT(hide()));
            }
        }


        SKGMainPanelPrivate::m_currentActionCanceled = false;
        if (iPos != 0 && iPos != 100) {
            qApp->processEvents(QEventLoop::AllEvents, 500);
        }
        return (SKGMainPanelPrivate::m_currentActionCanceled ? 1 : 0);
    }

    static bool naturalLessThan(const QString& s1, const QString& s2)
    {
        return (QCollator().compare(s1, s2) < 0);
    }

    static bool adviceLessThan(const SKGAdvice& s1, const SKGAdvice& s2)
    {
        if (s1.getPriority() == s2.getPriority()) {
            return (s1.getShortMessage() > s2.getShortMessage());
        }
        return (s1.getPriority() > s2.getPriority());
    }

    static void setAttribute(QDomElement& iRoot, const QString& iPath, const QString& iValue)
    {
        int pos = iPath.indexOf('.');
        if (pos == -1) {
            iRoot.setAttribute(iPath, iValue);
        } else {
            QString newElementName = iPath.left(pos);
            QString newAttribue = iPath.right(iPath.count() - pos - 1);

            QDomDocument doc("SKGML");
            doc.setContent(iRoot.attribute(newElementName));
            QDomElement root = doc.documentElement();
            if (root.isNull()) {
                root = doc.createElement("parameters");
                doc.appendChild(root);
            }
            setAttribute(root, newAttribue, iValue);

            iRoot.setAttribute(newElementName, doc.toString());
        }
    }

    static bool iconInSystemtray()
    {
        return skgbasegui_settings::icon_in_system_tray();
    }

    void refreshTabPosition()
    {
        m_tabWidget->setTabPosition(static_cast<QTabWidget::TabPosition>(skgbasegui_settings::main_tabs_position()));
    }
};


bool SKGMainPanelPrivate::m_currentActionCanceled = false;
SKGMainPanel* SKGMainPanelPrivate::m_mainPanel = NULL;

SKGMainPanel::SKGMainPanel(QSplashScreen* iSplashScreen, SKGDocument* iDocument, const QStringList& iArguments)
    : KXmlGuiWindow(), d(new SKGMainPanelPrivate)
{
    SKGTRACEINFUNC(1);
    d->m_tabWidget = new SKGTabWidget(this);
    d->m_splashScreen = iSplashScreen;
    d->m_currentDocument = iDocument;
    d->m_actHideContextItem = NULL; d->m_actShowAllContextItems = NULL; d->m_actDelete = NULL; d->m_addTabAction = NULL; d->m_tipOfDayAction = NULL;
    d->m_closePageAction = NULL; d->m_closeAllPagesAction = NULL; d->m_closeAllOtherPagesAction = NULL; d->m_switchPinState = NULL; d->m_saveDefaultStateAction = NULL;
    d->m_resetDefaultStateAction = NULL; d->m_overwriteBookmarkStateAction = NULL; d->m_configureAction = NULL; d->m_enableEditorAction = NULL; d->m_fullScreenAction = NULL; d->m_actLock = NULL;
    d->m_actUnLock = NULL;
    d->m_kSystemTrayIcon = NULL;
    d->m_kNormalMessage = NULL; d->m_zoomSelector = NULL;  d->m_previousAction = NULL; d->m_nextAction = NULL; d->m_buttonMenuAction = NULL;
    d->m_mainWidget = NULL; d->m_mainLayout = NULL; d->m_middleClick = false; d->m_saveOnClose = false;

    setComponentName("skg", KAboutData::applicationData().displayName());
    setObjectName(qApp->applicationDisplayName());

    // Set main panel
    SKGMainPanelPrivate::m_mainPanel = this;
    QScrollArea* w = new QScrollArea(this);
    w->setFrameShape(QFrame::NoFrame);
    w->setFocusPolicy(Qt::NoFocus);
    d->m_mainLayout = new QVBoxLayout(w);
    d->m_mainLayout->setSpacing(0);
    d->m_mainLayout->setContentsMargins(0, 0, 0, 0);
    d->m_mainLayout->addWidget(d->m_tabWidget);

    // setMainWidget(new QLabel("hello"));

    // Initialize settings
    KSharedConfigPtr config = KSharedConfig::openConfig();
    KConfigGroup prefskg = config->group("Main Panel");
    if (prefskg.readEntry("update_modified_contexts", 100) == 100) {
        // First call, we set default values
        prefskg.writeEntry("update_modified_contexts", 2);
        prefskg.writeEntry("update_modified_bookmarks", 2);

        KMessageBox::saveDontShowAgainYesNo("updateContextOnClose", KMessageBox::No);
        KMessageBox::saveDontShowAgainYesNo("updateBookmarkOnClose", KMessageBox::No);
    }

    // Search plugins
    KService::List offers = KServiceTypeTrader::self()->query(QLatin1String("SKG GUI/Plugin"));

    // Create temporary file for tips
    QFile tipsFile(getTipOfDayFileName());
    bool tipsFileOpened = tipsFile.open(QIODevice::WriteOnly);
    bool tipsFound = false;

    QTextStream stream(&tipsFile);

    // Load plugins
    int nb = offers.count();
    SKGTRACEL(1) << nb << " plugins found" << endl;
    if (d->m_splashScreen) {
        d->m_splashScreen->showMessage(i18nc("Splash screen message", "Loading plugins..."), Qt::AlignLeft, QColor(221, 130, 8));    // krazy:exclude=qmethods
    }
    SKGError err;
    QStringList listAuthors;
    QStringList listTasks;
    QStringList listEmails;
    QStringList listOscs;
    for (int i = 0; i < nb; ++i) {
        KService::Ptr service = offers.at(i);
        QString name = service->name();
        QString version = service->property("X-KDE-PluginInfo-Version", QVariant::String).toString();
        QString email = service->property("X-KDE-PluginInfo-Email", QVariant::String).toString();
        QString id = service->property("X-Krunner-ID", QVariant::String).toString();
        QString msg = i18nc("Splash screen message", "Loading plugin %1/%2: %3...", i + 1, nb, name);
        SKGTRACEL(1) << msg << endl;
        if (d->m_splashScreen) {
            d->m_splashScreen->showMessage(msg, Qt::AlignLeft, QColor(221, 130, 8));    // krazy:exclude=qmethods
        }
        KPluginLoader loader(service->library());
        if (version.isEmpty() || SKGServices::stringToDouble(version) < 1) {
            SKGTRACE << "WARNING: plugin [" << name << "] not loaded because of version too old (<1)" << endl;
        } else {
            KPluginFactory* factory2 = loader.factory();
            if (factory2) {
                SKGInterfacePlugin* pluginInterface = factory2->create<SKGInterfacePlugin> (this);
                if (pluginInterface) {
                    if (pluginInterface->isEnabled() && pluginInterface->setupActions(getDocument(), iArguments)) {
                        // Add plugin in about
                        QString author = service->property("X-KDE-PluginInfo-Author", QVariant::String).toString();
                        if (!author.isEmpty()) {
                            QString authorId;
                            QStringList authors = SKGServices::splitCSVLine(author, ',');
                            if (authors.count() == 2) {
                                author = authors.at(0);
                                authorId = authors.at(1);
                            }
                            int pos2 = listAuthors.indexOf(author);
                            if (pos2 == -1) {
                                listAuthors.push_back(author);
                                listTasks.push_back(i18n("Developer of plugin '%1'", name));
                                listEmails.push_back(email);
                                listOscs.push_back(authorId);
                            } else {
                                listTasks[pos2] += i18n(", '%1'", name);
                            }
                        }

                        // Store plugin
                        int nbplugin = d->m_pluginsList.count();
                        int pos3 = nbplugin;
                        for (int j = nbplugin - 1; j >= 0; --j) {
                            if (pluginInterface->getOrder() < d->m_pluginsList.at(j)->getOrder()) {
                                pos3 = j;
                            }
                        }

                        d->m_pluginsList.insert(pos3, pluginInterface);
                        pluginInterface->setObjectName(id);

                        // Build temporary tips file
                        if (tipsFileOpened) {
                            QStringList tipsList = pluginInterface->tips();
                            int nbtips = tipsList.count();
                            for (int t = 0; t < nbtips; ++t) {
                                tipsFound = true;
                                QString tips = tipsList.at(t);
                                tips.remove("<p>");
                                tips.remove("</p>");
                                stream << "<tip category=\""
                                       << id
                                       << "\"><html>" << SKGServices::stringToHtml(tips)
                                       << "</html></tip>"
                                       << endl;
                            }
                        }
                    }
                }
            } else {
                QString msg2 = i18nc("An information message",  "Loading plugin %1 failed because the factory could not be found in %2: %3", id, service->library(), loader.errorString());
                getDocument()->sendMessage(msg2);
                SKGTRACEL(1) << "WARNING:" << msg2 << endl;
            }
        }
    }

    // Add credits
    nb = listAuthors.count();
    for (int i = 0; i < nb; ++i) {
        KAboutData::applicationData().addCredit(i18n("%1", listAuthors.at(i).toLatin1().data()),
                                                i18n("%1", listTasks.at(i).toLatin1().data()),
                                                listEmails.at(i).toLatin1(),
                                                ""
                                                , listOscs.at(i).toLatin1());
    }

    // accept dnd
    setAcceptDrops(true);
    d->ui.setupUi(this);

    // tell the KXmlGuiWindow that this is indeed the main widget
    setCentralWidget(w);

    d->refreshTabPosition();
    d->m_tabWidget->setMovable(true);
    d->m_tabWidget->setTabsClosable(true);
    d->m_tabWidget->setUsesScrollButtons(true);
    d->m_tabWidget->tabBar()->setSelectionBehaviorOnRemove(QTabBar::SelectPreviousTab);
    d->m_tabWidget->setDocumentMode(true);
    d->m_tabWidget->show();

    // System tray
    if (QSystemTrayIcon::isSystemTrayAvailable()) {
        d->m_kSystemTrayIcon = new QSystemTrayIcon(qApp->windowIcon());
        QMenu* systemTrayMenu = new QMenu(this);
        QAction* quit = systemTrayMenu->addAction(QIcon::fromTheme("application-exit"),  i18nc("Verb", "Quit"));
        connect(quit, &QAction::triggered, this, &SKGMainPanel::onQuitAction);

        d->m_kSystemTrayIcon->setContextMenu(systemTrayMenu);
        connect(d->m_kSystemTrayIcon, &QSystemTrayIcon::activated, this, &SKGMainPanel::onTrayActivated);
        if (d->iconInSystemtray()) {
            d->m_kSystemTrayIcon->show();
        }
    }

    // then, setup our actions
    setupActions();

    displayErrorMessage(err);

    // Add a status bar
    statusBar()->show();

    QPalette palette2 = QApplication::palette();
    palette2.setColor(QPalette::Base, Qt::transparent);
    d->ui.kContextList->setPalette(palette2);

    QAction* toggle = d->ui.kDockContext->toggleViewAction();
    QAction* panelAction = actionCollection()->addAction("view_context");
    registerGlobalAction("view_context", panelAction);
    panelAction->setCheckable(true);
    panelAction->setChecked(toggle->isChecked());
    panelAction->setText(toggle->text());
    actionCollection()->setDefaultShortcut(panelAction, Qt::SHIFT + Qt::Key_F9);
    connect(panelAction, &QAction::triggered, toggle, &QAction::trigger);
    connect(toggle, &QAction::toggled, panelAction, &QAction::setChecked);

    QAction* toggle2 = d->ui.kDockMessages->toggleViewAction();
    QAction* panelAction2 = actionCollection()->addAction("view_messages");
    registerGlobalAction("view_messages", panelAction2);
    panelAction2->setCheckable(true);
    panelAction2->setChecked(toggle2->isChecked());
    panelAction2->setText(toggle2->text());
    actionCollection()->setDefaultShortcut(panelAction2, Qt::SHIFT + Qt::Key_F8);
    connect(panelAction2, &QAction::triggered, toggle2, &QAction::trigger);
    connect(toggle2, &QAction::toggled, panelAction2, &QAction::setChecked);

    KSelectAction* contextMenu = new KSelectAction(QIcon::fromTheme("tab-new"), i18nc("Noun", "Pages"), this);
    registerGlobalAction("view_contextmenu" , contextMenu);

    // Add plugin in client in right order
    QList<QListWidgetItem*> pageNotVisible;
    KConfigGroup prefContextVisibility = config->group("Context Visibility");
    int shortCutIndex = 0;
    QString shortCutPrefix = "Ctrl+";
    int nbplugin = d->m_pluginsList.count();
    QList<QAction*> contextActionList;
    for (int j = 0; j < nbplugin; ++j) {
        SKGInterfacePlugin* pluginInterface = d->m_pluginsList.at(j);
        if (pluginInterface) {
            // Creation of the item
            QString title = pluginInterface->title();
            SKGTRACEL(1) << "Add plugin (" << pluginInterface->getOrder() << ") : " << title << endl;
            if (!title.isEmpty()) {
                // Add menu item with shortcuts
                if (pluginInterface->isInPagesChooser()) {
                    bool visible = prefContextVisibility.readEntry(pluginInterface->objectName(), true);
                    QIcon icon = QIcon::fromTheme(pluginInterface->icon());

                    QListWidgetItem* contextItem = new QListWidgetItem(icon, title);
                    contextItem->setStatusTip(pluginInterface->statusTip());
                    contextItem->setToolTip(pluginInterface->toolTip());
                    contextItem->setData(12, j);     // context item ==> plugin
                    int page2 = d->ui.kContextList->count();
                    pluginInterface->setProperty("contextItem", page2);  // plugin ==> context item

                    d->ui.kContextList->addItem(contextItem);

                    QAction* newAction = contextMenu->addAction(icon, title);
                    if (newAction) {
                        newAction->setCheckable(false);
                        newAction->setData(page2);
                        contextItem->setData(15, QVariant::fromValue(static_cast<void*>(newAction)));     // context item ==> action
                        if (!shortCutPrefix.isEmpty()) {
                            ++shortCutIndex;
                            if (shortCutIndex == 10) {
                                shortCutIndex = 0;
                                if (shortCutPrefix == "Ctrl+") {
                                    shortCutPrefix += "Alt+";
                                } else {
                                    shortCutPrefix = "";
                                }
                            }
                            if (!shortCutPrefix.isEmpty()) {
                                actionCollection()->setDefaultShortcut(newAction, QString(shortCutPrefix % SKGServices::intToString(shortCutIndex)));
                            }
                        }
                        connect(newAction, &QAction::triggered, this, &SKGMainPanel::onOpenContext);

                        contextActionList.append(newAction);

                        if (!visible) {
                            pageNotVisible.push_back(contextItem);
                        }

                        // Register action
                        QString id = "page_" % pluginInterface->objectName();
                        registerGlobalAction(id, newAction);
                        registerGlobalAction(id, newAction);
                    }
                }

                // Create dock if needed
                QDockWidget* dockWidget = pluginInterface->getDockWidget();
                if (dockWidget) {
                    addDockWidget(Qt::LeftDockWidgetArea, dockWidget);
                    tabifyDockWidget(d->ui.kDockContext, dockWidget);
                    tabifyDockWidget(dockWidget, d->ui.kDockContext);
                }
            }
        }
    }

    // Lock docs if needed
    KConfigGroup pref = getMainConfigGroup();
    if (pref.readEntry("docks_locked", false)) {
        onLockDocks();
    }

    if (!pref.readEntry("first_launch", false)) {
        this->setWindowState(windowState() | Qt::WindowMaximized);
        pref.writeEntry("first_launch", true);
    }

    // a call to KXmlGuiWindow::setupGUI() populates the GUI
    // with actions, using KXMLGUI.
    // It also applies the saved mainwindow settings, if any, and ask the
    // mainwindow to automatically save settings if changed: window size,
    // toolbar position, icon size, etc.
    setupGUI(Default, "skgmainpanel.rc");
    plugActionList("context_actionlist", contextActionList);

    for (int j = 0; j < nbplugin; ++j) {
        SKGInterfacePlugin* pluginInterface = d->m_pluginsList.at(j);
        if (pluginInterface) {
            QString title = pluginInterface->title();
            SKGTRACEL(1) << "Add plugin client (" << pluginInterface->getOrder() << ") : " << title << endl;
            guiFactory()->addClient(pluginInterface);
        }
    }

    // Hide items in context
    nb = pageNotVisible.count();
    for (int i = 0; i < nb; ++i) {
        setContextVisibility(pageNotVisible.at(i), false);
    }

    // Set status bar
    d->m_kNormalMessage = new QLabel(this);

    QProgressBar* kProgressBar = new QProgressBar(this);
    kProgressBar->setObjectName(QString::fromUtf8("kProgressBar"));
    kProgressBar->setValue(0);
    kProgressBar->setMinimumSize(100, 20);
    QSizePolicy sizePolicyProgessBar(QSizePolicy::Fixed, QSizePolicy::Expanding);
    kProgressBar->setSizePolicy(sizePolicyProgessBar);
    kProgressBar->setToolTip(i18nc("Widget description", "Progress of the current action"));
    QFont f = kProgressBar->font();
    f.setPointSize(6.0);
    kProgressBar->setFont(f);
    kProgressBar->setVisible(false);
    kProgressBar->setAlignment(Qt::AlignTop | Qt::AlignHCenter);
    kProgressBar->setRange(0, 100);

    d->m_zoomSelector = new SKGZoomSelector(this);
    d->m_zoomSelector->setSizePolicy(sizePolicyProgessBar);
    connect(d->m_zoomSelector, &SKGZoomSelector::changed, this, &SKGMainPanel::onZoomChanged);

    statusBar()->addWidget(d->m_kNormalMessage);
    statusBar()->addPermanentWidget(kProgressBar);

    // Set Cancel button
    QPushButton* kCancelButton = new QPushButton();
    kCancelButton->setObjectName(QString::fromUtf8("kCancelButton"));
    kCancelButton->setIcon(QIcon::fromTheme("media-playback-stop"));
    kCancelButton->setToolTip(i18nc("Widget description", "Cancel the current action"));
    kCancelButton->setStatusTip(i18nc("Widget description", "Cancel the current action"));
    kCancelButton->setVisible(false);
    connect(kCancelButton, &QPushButton::clicked, this, &SKGMainPanel::onCancelCurrentAction);

    //
    d->ui.kClearMessageBtn->setIcon(QIcon::fromTheme("edit-clear"));
    connect(d->ui.kClearMessageBtn, &QPushButton::clicked, this, &SKGMainPanel::onClearMessages);

    // Add special button in toolbar
    KToolBar* tb = toolBar();
    if (tb) {
        QLabel* label = new QLabel(this);
        QSizePolicy sizePolicyLabel(QSizePolicy::Expanding, QSizePolicy::Preferred);
        sizePolicyLabel.setHorizontalStretch(0);
        sizePolicyLabel.setVerticalStretch(0);
        sizePolicyLabel.setHeightForWidth(label->sizePolicy().hasHeightForWidth());
        label->setSizePolicy(sizePolicyLabel);
        tb->addWidget(label);
        tb->addAction(d->m_buttonMenuAction);
    }

    statusBar()->addPermanentWidget(kCancelButton);
    statusBar()->addPermanentWidget(d->m_zoomSelector);

    // Set progress bar call back
    if (getDocument()) {
        d->m_progressObjects.p1 = static_cast<void*>(kProgressBar);
        d->m_progressObjects.p2 = static_cast<void*>(kCancelButton);
        getDocument()->setProgressCallback(&SKGMainPanelPrivate::progressBarCallBack, static_cast<void*>(&d->m_progressObjects));
    }

    // Connection
    if (getDocument()) {
        connect(getDocument(), &SKGDocument::transactionSuccessfullyEnded, this, &SKGMainPanel::refresh, Qt::QueuedConnection);
        connect(getDocument(), &SKGDocument::transactionSuccessfullyEnded, this, &SKGMainPanel::notify, Qt::QueuedConnection);
    }
    connect(d->ui.kContextList, &QListWidget::itemPressed, this, &SKGMainPanel::onBeforeOpenContext);
    connect(d->ui.kContextList, &QListWidget::itemPressed, this, &SKGMainPanel::onOpenContext);
    connect(d->m_tabWidget->tabBar(), &QTabBar::tabCloseRequested, this, &SKGMainPanel::closePageByIndex);
    connect(d->m_tabWidget->tabBar(), &QTabBar::tabBarDoubleClicked, this, &SKGMainPanel::addTab);
    connect(d->m_tabWidget->tabBar(), &QTabBar::currentChanged, this, &SKGMainPanel::currentPageChanged);
    connect(this, &SKGMainPanel::currentPageChanged, this, &SKGMainPanel::refresh);

    // Refresh
    refresh();

    // Tip of day
    if (tipsFound) {
        onTipOfDay();
    } else {
        // Remove useless menu
        delete d->m_tipOfDayAction;
        d->m_tipOfDayAction = NULL;
    }

    d->ui.kContextList->installEventFilter(this);

    // Build contextual menu
    d->ui.kContextList->setContextMenuPolicy(Qt::CustomContextMenu);

    d->m_contextMenu = new QMenu(d->ui.kContextList);
    d->m_actHideContextItem = d->m_contextMenu->addAction(QIcon::fromTheme("layer-visible-off"), i18nc("Verb", "Hide"));
    connect(d->m_actHideContextItem, &QAction::triggered, this, &SKGMainPanel::onHideContextItem);

    d->m_actShowAllContextItems = d->m_contextMenu->addAction(QIcon::fromTheme("layer-visible-on"), i18nc("Verb", "Show all"));
    connect(d->m_actShowAllContextItems, &QAction::triggered, this, &SKGMainPanel::onShowAllContextItems);

    d->m_contextMenu->addSeparator();
    d->m_contextMenu->addAction(getGlobalAction("tab_savedefaultstate"));
    d->m_contextMenu->addAction(getGlobalAction("tab_resetdefaultstate"));

    connect(d->ui.kContextList, &QListWidget::customContextMenuRequested, this, &SKGMainPanel::showMenu);

#ifdef KActivities_FOUND
    // Initialize kactivities resource instance
    d->m_activityResourceInstance = new KActivities::ResourceInstance(window()->winId());
    d->m_activityResourceInstance->setParent(this);
#endif

    // Show menu bar and status bar
    menuBar()->show();
    statusBar()->show();

    notify();  // Due to sendMessage not in a transaction
    d->m_splashScreen = NULL;
}

SKGMainPanel::~SKGMainPanel()
{
    SKGTRACEINFUNC(1);
    SKGMainPanelPrivate::m_mainPanel = NULL;
    disconnect(getDocument(), 0, this, 0);

    // close plugins
    int nb = d->m_pluginsList.count();
    for (int i = 0; i < nb; ++i) {
        getPluginByIndex(i)->close();
    }

    if (getDocument()) {
        getDocument()->close();
    }
    delete d;
}

void SKGMainPanel::showMenu(const QPoint& iPos)
{
    if (d->m_contextMenu) {
        d->m_contextMenu->popup(d->ui.kContextList->mapToGlobal(iPos));
    }
}

void SKGMainPanel::setContextVisibility(QListWidgetItem* iItem, bool iVisibility)
{
    if (iItem) {
        // Hide item in context
        iItem->setHidden(!iVisibility);

        // Hide corresponding action
        QAction* act = static_cast< QAction* >(iItem->data(15).value<void*>());
        if (act) {
            act->setVisible(iVisibility);
        }

        // Save state in settings
        SKGInterfacePlugin* plugin = getPluginByIndex(iItem->data(12).toInt());
        if (plugin) {
            KSharedConfigPtr config = KSharedConfig::openConfig();
            KConfigGroup pref = config->group("Context Visibility");
            pref.writeEntry(plugin->objectName(), iVisibility);
        }
    }
}

void SKGMainPanel::setContextVisibility(int iPage, bool iVisibility)
{
    setContextVisibility(d->ui.kContextList->item(iPage), iVisibility);
}

void SKGMainPanel::onHideContextItem()
{
    setContextVisibility(d->ui.kContextList->currentRow(), false);
}

void SKGMainPanel::onShowAllContextItems()
{
    int nb = d->ui.kContextList->count();
    for (int i = 0; i < nb; ++i) {
        setContextVisibility(i, true);
    }
}

SKGDocument* SKGMainPanel::getDocument() const
{
    return d->m_currentDocument;
}

bool SKGMainPanel::processArguments(const QStringList& iArgument)
{
    bool output = false;
    foreach(SKGInterfacePlugin * plugin, d->m_pluginsList) {
        if (plugin) {
            output = plugin->processArguments(iArgument);
            if (output) {
                break;
            }
        }
    }
    return output;
}

void SKGMainPanel::registerGlobalAction(const QString& iIdentifier, QAction* iAction, bool iAddInCollection)
{
    if (iAction == NULL) {
        SKGTRACE << "WARNING: registerGlobalAction(" << iIdentifier << ",NULL)" << endl;
    } else {
        QStringList keys = d->m_registeredGlogalAction.keys();
        foreach(const QString & id, keys) {
            QPointer<QAction> act = d->m_registeredGlogalAction[id];
            if (act && iIdentifier != id && !act->shortcut().isEmpty() && act->shortcut() == iAction->shortcut()) {
                SKGTRACE << "WARNING: The actions [" << iAction->text() << " (" << iIdentifier << ")] and [" << act->text() << " (" << id << ")] has same shortcut [" << iAction->shortcut().toString() << "]" << endl;
            }
        }
        d->m_registeredGlogalAction[iIdentifier] = iAction;
        // This connect has not been migrated on new connect mechanism to avoid crash when leaving the application
        connect(iAction, SIGNAL(destroyed(QObject*)), this, SLOT(unRegisterGlobalAction(QObject*)));
        if (iAddInCollection) {
            QKeySequence shortCut = iAction->shortcut();
            if (!shortCut.isEmpty()) {
                iAction->setShortcut(QKeySequence());
            }
            actionCollection()->addAction(iIdentifier, iAction);
            if (!shortCut.isEmpty()) {
                actionCollection()->setDefaultShortcut(iAction, shortCut);
            }
        }
    }
}

void SKGMainPanel::unRegisterGlobalAction(QObject* iAction)
{
    QAction* act = qobject_cast< QAction* >(iAction);
    if (act) {
        foreach(const QString & id, d->m_registeredGlogalAction.keys(act)) {
            d->m_registeredGlogalAction.remove(id);
        }
    }
}

QAction* SKGMainPanel::getGlobalAction(const QString& iIdentifier, bool iWarnIfNotExist)
{
    QAction* act = d->m_registeredGlogalAction[iIdentifier];
    if (act == NULL && iWarnIfNotExist) {
        SKGTRACE << "WARNING: getGlobalAction(" << iIdentifier << ")=NULL" << endl;
    }
    return act;
}

QMap<QString, QPointer<QAction> > SKGMainPanel::getGlobalActions() const
{
    return d->m_registeredGlogalAction;
}

SKGInterfacePlugin* SKGMainPanel::getPluginByIndex(int iIndex)
{
    SKGInterfacePlugin* output = NULL;
    if (iIndex >= 0 && iIndex < d->m_pluginsList.count()) {
        output = d->m_pluginsList[iIndex];
    }

    return output;
}

SKGInterfacePlugin* SKGMainPanel::getPluginByName(const QString& iName)
{
    SKGInterfacePlugin* output = NULL;
    int nbplugin = d->m_pluginsList.count();
    QString name = iName.toLower();
    for (int j = 0; !output && j < nbplugin; ++j) {
        QString namep = d->m_pluginsList[j]->objectName().toLower();
        if (namep == name || namep.replace(' ', '_') == name) {
            output = d->m_pluginsList[j];
        }
    }

    return output;
}

void SKGMainPanel::setupActions()
{
    SKGTRACEINFUNC(1);

    // Std File
    KStandardAction::quit(this, SLOT(onQuitAction()), actionCollection());
    KStandardAction::configureNotifications(this, SLOT(onConfigureNotifications()), actionCollection());

    // Preferences
    KStandardAction::preferences(this, SLOT(optionsPreferences()), actionCollection());

    // New Tab
    d->m_addTabAction = new QAction(QIcon::fromTheme("tab-new-background"), i18nc("Noun, user action", "New Tab"), this);
    actionCollection()->setDefaultShortcut(d->m_addTabAction, Qt::CTRL + Qt::SHIFT + Qt::Key_W);
    connect(d->m_addTabAction, &QAction::triggered, this, &SKGMainPanel::addTab);
    registerGlobalAction("new_tab", d->m_addTabAction);

    // Add new tab widget
    QToolButton* addTabButton = new QToolButton(this);
    addTabButton->setIcon(d->m_addTabAction->icon());
    addTabButton->setAutoRaise(true);
    addTabButton->raise();
    addTabButton->setDefaultAction(d->m_addTabAction);
    addTabButton->setToolButtonStyle(Qt::ToolButtonIconOnly);
    d->m_tabWidget->setCornerWidget(addTabButton);

    d->m_actLock = new QAction(QIcon::fromTheme("document-encrypt"), i18nc("Verb", "Lock panels"), this);
    connect(d->m_actLock, &QAction::triggered, this, &SKGMainPanel::onLockDocks);
    registerGlobalAction("view_lock", d->m_actLock);

    d->m_actUnLock = new QAction(QIcon::fromTheme("document-decrypt"), i18nc("Verb", "Unlock panels"), this);
    connect(d->m_actUnLock, &QAction::triggered, this, &SKGMainPanel::onUnlockDocks);
    registerGlobalAction("view_unlock", d->m_actUnLock);

    d->m_switchPinState = new QAction(QIcon::fromTheme("document-encrypt"), i18nc("Noun, user action", "Pin this page"), this);
    connect(d->m_switchPinState, &QAction::triggered, [ = ] {this->switchPinPage();});
    registerGlobalAction("tab_switchpin", d->m_switchPinState);

    //
    d->m_closePageAction = actionCollection()->addAction(KStandardAction::Close, "tab_close", this, SLOT(closePage()));
    registerGlobalAction("tab_close", d->m_closePageAction);

    //
    d->m_closeAllPagesAction = new QAction(QIcon::fromTheme("window-close"), i18nc("Noun, user action", "Close All"), this);
    actionCollection()->setDefaultShortcut(d->m_closeAllPagesAction, Qt::ALT + Qt::Key_W);
    connect(d->m_closeAllPagesAction, &QAction::triggered, this, &SKGMainPanel::closeAllPages);
    registerGlobalAction("tab_closeall", d->m_closeAllPagesAction);

    //
    d->m_closeAllOtherPagesAction = new QAction(QIcon::fromTheme("window-close"), i18nc("Noun, user action", "Close All Other"), this);
    actionCollection()->setDefaultShortcut(d->m_closeAllOtherPagesAction, Qt::CTRL + Qt::ALT + Qt::Key_W);
    connect(d->m_closeAllOtherPagesAction, &QAction::triggered, [ = ] {this->closeAllOtherPages();});
    registerGlobalAction("tab_closeallother", d->m_closeAllOtherPagesAction);

    //
    d->m_saveDefaultStateAction = new QAction(QIcon::fromTheme("document-save"), i18nc("Noun, user action", "Save page state"), this);
    actionCollection()->setDefaultShortcut(d->m_saveDefaultStateAction, Qt::CTRL + Qt::ALT + Qt::Key_S);
    connect(d->m_saveDefaultStateAction, &QAction::triggered, this, &SKGMainPanel::saveDefaultState);
    registerGlobalAction("tab_savedefaultstate", d->m_saveDefaultStateAction);

    //
    d->m_resetDefaultStateAction = new QAction(QIcon::fromTheme("edit-clear"), i18nc("Noun, user action", "Reset page state"), this);
    actionCollection()->setDefaultShortcut(d->m_resetDefaultStateAction, Qt::CTRL + Qt::ALT + Qt::Key_R);
    connect(d->m_resetDefaultStateAction, &QAction::triggered, this, &SKGMainPanel::resetDefaultState);
    registerGlobalAction("tab_resetdefaultstate", d->m_resetDefaultStateAction);

    //
    d->m_reopenLastClosed = new QAction(QIcon::fromTheme("tab-new"), i18nc("Noun, user action", "Reopen last page closed"), this);
    actionCollection()->setDefaultShortcut(d->m_reopenLastClosed, Qt::CTRL + Qt::ALT + Qt::Key_T);
    connect(d->m_reopenLastClosed, &QAction::triggered, this, &SKGMainPanel::onReopenLastClosed);
    registerGlobalAction("tab_reopenlastclosed", d->m_reopenLastClosed);

    //
    QStringList overlay;
    overlay.push_back("bookmarks");
    d->m_overwriteBookmarkStateAction = new QAction(SKGServices::fromTheme("document-save", overlay), i18nc("Noun, user action", "Overwrite bookmark state"), this);
    connect(d->m_overwriteBookmarkStateAction, &QAction::triggered, this, &SKGMainPanel::overwriteBookmarkState);
    actionCollection()->setDefaultShortcut(d->m_overwriteBookmarkStateAction, Qt::CTRL + Qt::ALT + Qt::Key_B);
    registerGlobalAction("tab_overwritebookmark", d->m_overwriteBookmarkStateAction);

    //
    d->m_configureAction = new QAction(QIcon::fromTheme("configure"), i18nc("Noun, user action", "Configure..."), this);
    connect(d->m_configureAction, &QAction::triggered, [ = ] {this->optionsPreferences();});
    registerGlobalAction("tab_configure", d->m_configureAction);

    d->m_tipOfDayAction = KStandardAction::tipOfDay(this, SLOT(onTipOfDay()), actionCollection());

    // Menu
    d->m_buttonMenuAction = new KToolBarPopupAction(QIcon::fromTheme("configure"), "", this);
    d->m_buttonMenuAction->setToolTip(i18nc("Noun, user action", "Menu"));
    d->m_buttonMenu = static_cast<QMenu*>(d->m_buttonMenuAction->menu());
    connect(d->m_buttonMenu , &QMenu::aboutToShow, this, &SKGMainPanel::onShowButtonMenu);
    d->m_buttonMenuAction->setDelayed(false);
    registerGlobalAction("view_menu", d->m_buttonMenuAction);

    d->m_showMenuBarAction = KStandardAction::showMenubar(this, SLOT(onShowMenuBar()), actionCollection());
    KConfigGroup pref = getMainConfigGroup();
    d->m_showMenuBarAction->setChecked(pref.readEntry("menubar_shown", true));
    QTimer::singleShot(200, Qt::CoarseTimer, this, SLOT(onShowMenuBar()));
    registerGlobalAction("options_show_menubar", d->m_showMenuBarAction);

    //
    d->m_previousAction = new KToolBarPopupAction(QIcon::fromTheme("go-previous"), i18nc("Noun, user action", "Previous"), this);
    connect(d->m_previousAction, &QAction::triggered, this, &SKGMainPanel::onPrevious);
    actionCollection()->setDefaultShortcut(d->m_previousAction, Qt::ALT + Qt::Key_Left);
    d->m_previousAction->setPriority(QAction::LowPriority);
    d->m_previousMenu = static_cast<QMenu*>(d->m_previousAction->menu());
    connect(d->m_previousMenu , &QMenu::aboutToShow, this, &SKGMainPanel::onShowPreviousMenu);
    d->m_previousAction->setStickyMenu(false);
    d->m_previousAction->setData(0);
    registerGlobalAction("go_previous", d->m_previousAction);

    //
    d->m_nextAction = new KToolBarPopupAction(QIcon::fromTheme("go-next"), i18nc("Noun, user action", "Next"), this);
    connect(d->m_nextAction, &QAction::triggered, this, &SKGMainPanel::onNext);
    actionCollection()->setDefaultShortcut(d->m_nextAction, Qt::ALT + Qt::Key_Right);
    d->m_nextAction->setPriority(QAction::LowPriority);
    d->m_nextMenu = static_cast<QMenu*>(d->m_nextAction->menu());
    connect(d->m_nextMenu , &QMenu::aboutToShow, this, &SKGMainPanel::onShowNextMenu);
    d->m_nextAction->setStickyMenu(false);
    d->m_nextAction->setData(0);
    registerGlobalAction("go_next", d->m_nextAction);

    //
    d->m_fullScreenAction = actionCollection()->addAction(KStandardAction::FullScreen, "fullscreen", this, SLOT(onFullScreen()));
    registerGlobalAction("fullscreen", d->m_fullScreenAction);

    //
    d->m_enableEditorAction = new QAction(QIcon::fromTheme("appointment-new"), i18nc("Noun, user action", "Enable editor"), this);
    actionCollection()->setDefaultShortcut(d->m_enableEditorAction, Qt::CTRL + Qt::Key_Insert);
    connect(d->m_enableEditorAction, &QAction::triggered, this, &SKGMainPanel::enableEditor);
    registerGlobalAction("enable_editor", d->m_enableEditorAction);

    // Contextual menu
    d->m_tabWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
    d->m_tabWidget->insertAction(0, getGlobalAction("new_tab"));
    d->m_tabWidget->insertAction(0, getGlobalAction("tab_reopenlastclosed"));
    d->m_tabWidget->insertAction(0, getGlobalAction("tab_close"));
    d->m_tabWidget->insertAction(0, getGlobalAction("tab_closeall"));
    d->m_tabWidget->insertAction(0, getGlobalAction("tab_closeallother"));
    {
        QAction* sep = new QAction(this);
        sep->setSeparator(true);
        d->m_tabWidget->insertAction(0, sep);
    }
    d->m_tabWidget->insertAction(0, getGlobalAction("tab_switchpin"));
    d->m_tabWidget->insertAction(0, getGlobalAction("tab_resetdefaultstate"));
    d->m_tabWidget->insertAction(0, getGlobalAction("tab_savedefaultstate"));
    d->m_tabWidget->insertAction(0, getGlobalAction("tab_overwritebookmark"));
    d->m_tabWidget->insertAction(0, getGlobalAction("tab_configure"));
    {
        QAction* sep = new QAction(this);
        sep->setSeparator(true);
        d->m_tabWidget->insertAction(0, sep);
    }
    d->m_tabWidget->insertAction(0, getGlobalAction("fullscreen"));
}

void SKGMainPanel::enableEditor()
{
    SKGTabPage* cPage = currentPage();
    if (cPage) {
        cPage->activateEditor();
    }
}

void SKGMainPanel::onPrevious()
{
    SKGError err;
    SKGTRACEINFUNCRC(10, err);

    SKGTabPage* cPage = currentPage();
    if (cPage) {
        // Get index in history of page to refresh
        int indexPrevious = static_cast<QAction*>(sender())->data().toInt();

        // Get lists
        QList<SKGTabPage::SKGPageHistoryItem> listPrevious = cPage->getPreviousPages();
        if (indexPrevious < listPrevious.count()) {
            QList<SKGTabPage::SKGPageHistoryItem> listNext = cPage->getNextPages();
            SKGTabPage::SKGPageHistoryItem current = currentPageHistoryItem();

            // Get item to refresh
            SKGTabPage::SKGPageHistoryItem item = listPrevious.at(indexPrevious);

            // Open page
            cPage = openPage(getPluginByName(item.plugin), currentPageIndex(), item.state, item.name, item.bookmarkID);
            if (cPage) {
                cPage->setBookmarkID(item.bookmarkID);

                // Update lists
                listNext.insert(0, current);
                listPrevious.removeAt(indexPrevious);
                for (int i = 0; i < indexPrevious; ++i) {
                    SKGTabPage::SKGPageHistoryItem itemPrevious = listPrevious.at(0);  // Because the list is modified
                    listNext.insert(0, itemPrevious);
                    listPrevious.removeAt(0);
                }

                // Set lists
                cPage->setPreviousPages(listPrevious);
                cPage->setNextPages(listNext);
            }

            refresh();
        }
    }
}

void SKGMainPanel::onShowPreviousMenu()
{
    if (d->m_previousMenu) {
        d->m_previousMenu->clear();

        SKGTabPage* cPage = currentPage();
        if (cPage) {
            QList<SKGTabPage::SKGPageHistoryItem> list = cPage->getPreviousPages();
            int nb = list.count();
            for (int i = 0; i < nb; ++i) {
                QAction* act = d->m_previousMenu->addAction(QIcon::fromTheme(list.at(i).icon), list.at(i).name);
                if (act) {
                    act->setData(i);
                    connect(act, &QAction::triggered, this, &SKGMainPanel::onPrevious);
                }
            }
        }
    }
}

void SKGMainPanel::onShowMenuBar()
{
    bool test = d->m_showMenuBarAction->isChecked();
    menuBar()->setVisible(test);
    d->m_buttonMenuAction->setVisible(!test);

    KConfigGroup pref = getMainConfigGroup();
    pref.writeEntry("menubar_shown", test);
}

void SKGMainPanel::onFullScreen()
{
    if (!d->m_fullScreenAction->isChecked()) {
        // No Full screen
        setWindowState(windowState() & ~Qt::WindowFullScreen);   // reset

        // Show widget
        int nb = d->m_hiddenWidgets.count();
        for (int i = nb - 1; i >= 0; --i) {  // Must be done in this order
            d->m_hiddenWidgets.at(i)->show();
        }
        d->m_hiddenWidgets.clear();
    } else {
        // Full screen
        // Create list of widget to hide
        d->m_hiddenWidgets.push_back(menuBar());
        d->m_hiddenWidgets.push_back(statusBar());
        QList<KToolBar*> toolbars = toolBars();
        foreach(KToolBar * toolbar, toolbars) {
            d->m_hiddenWidgets.push_back(toolbar);
        }

        QObjectList cs = children();
        foreach(QObject * c, cs) {
            QDockWidget* doc = qobject_cast<QDockWidget*>(c);
            if (doc) {
                d->m_hiddenWidgets.push_back(doc);
            }
        }

        // Hide widget, keep in list hidden widget
        int nb = d->m_hiddenWidgets.count();
        for (int i = nb - 1; i >= 0; --i) {
            QWidget* w = d->m_hiddenWidgets.at(i);
            if (w && w->isVisible()) {
                w->hide();
            } else {
                d->m_hiddenWidgets.removeAt(i);
            }
        }

        setWindowState(windowState() | Qt::WindowFullScreen);   // set

        displayMessage(i18nc("Information message", "You can exit full screen mode with %1 or with the contextual menu", d->m_fullScreenAction->shortcut().toString()));
    }
}

void SKGMainPanel::onNext()
{
    SKGError err;
    SKGTRACEINFUNCRC(10, err);

    SKGTabPage* cPage = currentPage();
    if (cPage) {
        // Get index in history of page to refresh
        int posNext = static_cast<QAction*>(sender())->data().toInt();

        // Get lists
        QList<SKGTabPage::SKGPageHistoryItem> listPrevious = cPage->getPreviousPages();
        QList<SKGTabPage::SKGPageHistoryItem> listNext = cPage->getNextPages();
        SKGTabPage::SKGPageHistoryItem current = currentPageHistoryItem();

        // Get item to refresh
        SKGTabPage::SKGPageHistoryItem item = listNext.at(posNext);

        // Open page
        cPage = openPage(getPluginByName(item.plugin), currentPageIndex(), item.state, item.name, item.bookmarkID);
        if (cPage) {
            cPage->setBookmarkID(item.bookmarkID);

            // Update lists
            listPrevious.insert(0, current);
            listNext.removeAt(posNext);
            for (int i = 0; i < posNext; ++i) {
                SKGTabPage::SKGPageHistoryItem itemNext = listNext.at(0);  // Because the list is modified
                listPrevious.insert(0, itemNext);
                listNext.removeAt(0);
            }

            // Set lists
            cPage->setPreviousPages(listPrevious);
            cPage->setNextPages(listNext);
        }

        refresh();
    }
}

void SKGMainPanel::onReopenLastClosed()
{
    SKGError err;
    SKGTRACEINFUNCRC(10, err);

    SKGTabPage::SKGPageHistoryItem current = currentPageHistoryItem();

    // Get item to refresh
    historyPage item = d->m_historyClosedPages.takeLast();

    // Open page
    SKGTabPage* cPage = openPage(getPluginByName(item.current.plugin), -1, item.current.state, item.current.name, item.current.bookmarkID);
    if (cPage) {
        cPage->setBookmarkID(item.current.bookmarkID);
        cPage->setNextPages(item.next);
        cPage->setPreviousPages(item.previous);
    }

    refresh();
}

void SKGMainPanel::onLockDocks()
{
    QObjectList cs = children();
    foreach(QObject * c, cs) {
        QDockWidget* doc = qobject_cast<QDockWidget*>(c);
        if (doc) {
            doc->setFeatures(QDockWidget::NoDockWidgetFeatures);
        }
    }

    KConfigGroup pref = getMainConfigGroup();
    pref.writeEntry("docks_locked", true);

    refresh();
}

void SKGMainPanel::onUnlockDocks()
{
    QObjectList cs = children();
    foreach(QObject * c, cs) {
        QDockWidget* doc = qobject_cast<QDockWidget*>(c);
        if (doc) {
            doc->setFeatures(QDockWidget::AllDockWidgetFeatures);
        }
    }

    KConfigGroup pref = getMainConfigGroup();
    pref.writeEntry("docks_locked", false);

    refresh();
}

void SKGMainPanel::onConfigureNotifications()
{
    KNotifyConfigWidget::configure(this);
}

void SKGMainPanel::onShowNextMenu()
{
    if (d->m_nextMenu) {
        d->m_nextMenu->clear();

        SKGTabPage* cPage = currentPage();
        if (cPage) {
            QList<SKGTabPage::SKGPageHistoryItem> list = cPage->getNextPages();
            int nb = list.count();
            for (int i = 0; i < nb; ++i) {
                QAction* act = d->m_nextMenu->addAction(QIcon::fromTheme(list.at(i).icon), list.at(i).name);
                if (act) {
                    act->setData(i);
                    connect(act, &QAction::triggered, this, &SKGMainPanel::onNext);
                }
            }
        }
    }
}

void SKGMainPanel::onShowButtonMenu()
{
    if (d->m_buttonMenu) {
        d->m_buttonMenu->clear();

        QMenuBar* mb = menuBar();
        if (mb) {
            d->m_buttonMenu->addActions(mb->actions());
        }
    }
}

bool SKGMainPanel::queryClose()
{
    SKGTRACEINFUNC(1);
    // Bug 2777697: To be sure that all page modifications are closed
    closeAllPages();
    // Bug 2777697:

    bool output = queryFileClose();

    if (output && d->m_kSystemTrayIcon) {
        d->m_kSystemTrayIcon->hide();
        delete d->m_kSystemTrayIcon;
        d->m_kSystemTrayIcon = NULL;
    }

    // To be sure that the application is not closed in fullscreen mode
    if (output) {
        if (d->m_fullScreenAction->isChecked()) {
            d->m_fullScreenAction->trigger();
        }
    }

    return output;
}

void SKGMainPanel::setSaveOnClose(bool iSaveOnClose)
{
    d->m_saveOnClose = iSaveOnClose;
}

bool SKGMainPanel::queryFileClose()
{
    SKGTRACEINFUNC(1);
    bool output = true;
    if (getDocument()->getCurrentTransaction()) {
        displayMessage(i18nc("skgtestimportskg", "The application cannot be closed when an operation is running."), SKGDocument::Error);
        output = false;
    } else if (getDocument()->isFileModified()) {
        QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
        int code = KMessageBox::Yes;
        QString fileName = getDocument()->getCurrentFileName();
        QAction* save = getGlobalAction(fileName.isEmpty() ? "file_save_as" : "file_save");
        if (save) {
            if (!d->m_saveOnClose) code = KMessageBox::questionYesNoCancel(this, i18nc("Question", "The document has been modified.\nDo you want to save it before closing?"),
                                              QString(),
                                              KGuiItem(fileName.isEmpty() ?  i18nc("Question", "Save as") : i18nc("Question", "Save"),
                                                       QIcon::fromTheme(fileName.isEmpty() ?  "document-save-as" : "document-save")),
                                              KGuiItem(i18nc("Question", "Do not save")));
            if (code == KMessageBox::Yes) {
                save->trigger();
            }
            output = (code == KMessageBox::No || code == KMessageBox::Yes);
        } else {
            code = KMessageBox::questionYesNo(this, i18nc("Question", "Current modifications will not be saved.\nDo you want to continue?"));
            output = (code == KMessageBox::Yes);
        }
        QApplication::restoreOverrideCursor();
    }

    return output;
}

SKGObjectBase::SKGListSKGObjectBase SKGMainPanel::getSelectedObjects()
{
    SKGObjectBase::SKGListSKGObjectBase selection;

    SKGTabPage* cPage = currentPage();
    if (cPage) {
        selection = cPage->getSelectedObjects();
    }
    return selection;
}

int SKGMainPanel::getNbSelectedObjects()
{
    int nb = 0;

    SKGTabPage* cPage = currentPage();
    if (cPage) {
        nb = cPage->getNbSelectedObjects();
    }
    return nb;
}

SKGAdviceList SKGMainPanel::getAdvice() const
{
    SKGTRACEINFUNC(1);
    // Get list of ignored advice
    QString currentMonth = QDate::currentDate().toString("yyyy-MM");
    QStringList ignoredAdvice = getDocument()->getParameters("advice", "t_value='I' OR t_value='I_" % currentMonth % '\'');

    // Build the list of all advice by requesting all plugins
    SKGAdviceList globalAdviceList;
    int index = 0;
    while (index >= 0) {
        SKGInterfacePlugin* plugin = SKGMainPanel::getMainPanel()->getPluginByIndex(index);
        if (plugin) {
            foreach(const SKGAdvice & ad, plugin->advice(ignoredAdvice)) {
                if (!ignoredAdvice.contains(ad.getUUID()) && !ignoredAdvice.contains(SKGServices::splitCSVLine(ad.getUUID(), '|').at(0))) {
                    globalAdviceList.push_back(ad);
                }
            }
        } else {
            index = -2;
        }
        ++index;
    }
    qSort(globalAdviceList.begin(), globalAdviceList.end(), SKGMainPanelPrivate::adviceLessThan);

    return globalAdviceList;
}

KConfigGroup SKGMainPanel::getMainConfigGroup()
{
    KSharedConfigPtr config = KSharedConfig::openConfig();
    return config->group("Main Panel");
}

void SKGMainPanel::optionsPreferences(const QString& iPluginName)
{
    SKGTRACEINFUNC(1);
    // Compute page
    QString pluginName = iPluginName;
    if (pluginName.isEmpty() && this->currentPage()) {
        pluginName = this->currentPage()->objectName();
    }

    // Synchronize setting with confirmation panel
    {
        KMessageBox::ButtonCode confirm;
        int ask = KMessageBox::shouldBeShownYesNo("updateBookmarkOnClose", confirm);

        KConfigGroup pref = getMainConfigGroup();
        if (ask) {
            pref.writeEntry("update_modified_bookmarks", 0);
        } else if (confirm == KMessageBox::Yes) {
            pref.writeEntry("update_modified_bookmarks", 1);
        } else {
            pref.writeEntry("update_modified_bookmarks", 2);
        }
    }
    {
        KMessageBox::ButtonCode confirm;
        int ask = KMessageBox::shouldBeShownYesNo("updateContextOnClose", confirm);

        KConfigGroup pref = getMainConfigGroup();
        if (ask) {
            pref.writeEntry("update_modified_contexts", 0);
        } else if (confirm == KMessageBox::Yes) {
            pref.writeEntry("update_modified_contexts", 1);
        } else {
            pref.writeEntry("update_modified_contexts", 2);
        }
    }
    skgbasegui_settings::self()->load();


    if (KConfigDialog::showDialog("settings")) {
        return;
    }

    KConfigDialog* dialog = new KConfigDialog(this, "settings", skgbasegui_settings::self());

    // Add main
    QWidget* w = new QWidget();
    d->uipref.setupUi(w);
    d->uipref.kcfg_date_format->addItem(i18nc("Date format", "Short date (%1, %2)",
                                        QLocale().toString(QDate::currentDate(), QLocale::ShortFormat),
                                        QLocale().toString(QDate::currentDate().addDays(-10), QLocale::ShortFormat)));
    d->uipref.kcfg_date_format->addItem(i18nc("Date format", "Long date (%1, %2)",
                                        QLocale().toString(QDate::currentDate(), QLocale::LongFormat),
                                        QLocale().toString(QDate::currentDate().addDays(-10), QLocale::LongFormat)));
    d->uipref.kcfg_date_format->addItem(i18nc("Date format", "Fancy short date (%1, %2)",
                                        KFormat().formatRelativeDate(QDate::currentDate(), QLocale::ShortFormat),
                                        KFormat().formatRelativeDate(QDate::currentDate().addDays(-10), QLocale::ShortFormat)));
    d->uipref.kcfg_date_format->addItem(i18nc("Date format", "Fancy long date (%1, %2)",
                                        KFormat().formatRelativeDate(QDate::currentDate(), QLocale::LongFormat),
                                        KFormat().formatRelativeDate(QDate::currentDate().addDays(-10), QLocale::LongFormat)));
    d->uipref.kcfg_date_format->addItem(i18nc("Date format", "ISO date (%1, %2)",
                                        QDate::currentDate().toString(Qt::ISODate),
                                        QDate::currentDate().addDays(-10).toString(Qt::ISODate)));
    dialog->addPage(w, skgbasegui_settings::self(), i18nc("Noun", "General"), "preferences-other");

    // Add plugin in client in right order
    int nbplugin = d->m_pluginsList.count();
    for (int j = 0; j < nbplugin; ++j) {
        SKGInterfacePlugin* pluginInterface = getPluginByIndex(j);
        if (pluginInterface) {
            QWidget* w2 = pluginInterface->getPreferenceWidget();
            if (w2) {
                KPageWidgetItem* p = dialog->addPage(w2, pluginInterface->getPreferenceSkeleton(), pluginInterface->title(), pluginInterface->icon());
                if (p && pluginName == pluginInterface->objectName()) {
                    dialog->setCurrentPage(p);
                }
            }
        }
    }

    connect(dialog, &KConfigDialog::settingsChanged, this, &SKGMainPanel::onSettingsChanged);

    dialog->setAttribute(Qt::WA_DeleteOnClose);
    dialog->show();

    // Refresh
    refresh();
}

void SKGMainPanel::onSettingsChanged()
{
    SKGError err;
    SKGTRACEINFUNCRC(1, err);
    {
        int nb = d->m_pluginsList.count();
        SKGBEGINPROGRESSTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Save settings"), err, nb);

        // Refresh plugins
        for (int i = 0; !err && i < nb; ++i) {
            err = getPluginByIndex(i)->savePreferences();
            IFOKDO(err, getDocument()->stepForward(i + 1))
        }

        // Setting for icon
        if (d->m_kSystemTrayIcon) {
            if (d->iconInSystemtray()) {
                d->m_kSystemTrayIcon->show();
            } else {
                d->m_kSystemTrayIcon->hide();
            }
        }

        // Setting for tab position
        d->refreshTabPosition();

        // Setting for bookmarks modification
        {
            int option = skgbasegui_settings::update_modified_bookmarks();
            if (option == 0) {
                // ASK: remove following setting
                KMessageBox::enableMessage("updateBookmarkOnClose");
            } else if (option == 1) {
                // ALWAYS: set following setting
                KMessageBox::saveDontShowAgainYesNo("updateBookmarkOnClose", KMessageBox::Yes);
            } else {
                // NEVER: set following setting
                KMessageBox::saveDontShowAgainYesNo("updateBookmarkOnClose", KMessageBox::No);
            }
        }
        {
            int option = skgbasegui_settings::update_modified_contexts();
            if (option == 0) {
                // ASK: remove following setting
                KMessageBox::enableMessage("updateContextOnClose");
            } else if (option == 1) {
                // ALWAYS: set following setting
                KMessageBox::saveDontShowAgainYesNo("updateContextOnClose", KMessageBox::Yes);
            } else {
                // NEVER: set following setting
                KMessageBox::saveDontShowAgainYesNo("updateContextOnClose", KMessageBox::No);
            }
        }
        skgbasegui_settings::self()->load();
    }

    emit settingsChanged();

    // Display error
    displayErrorMessage(err);
}

void SKGMainPanel::refresh()
{
    SKGTRACEINFUNC(1);

    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));

    // Show/hide main widget
    bool atLeastOnePageOpened = (d->m_tabWidget->count() > 0);
    d->m_tabWidget->setVisible(atLeastOnePageOpened);
    if (d->m_mainWidget) {
        d->m_mainWidget->setVisible(!atLeastOnePageOpened);
    }

    // Refresh plugins
    int nb = d->m_pluginsList.count();
    for (int i = 0; i < nb; ++i) {
        getPluginByIndex(i)->refresh();
    }

    // Enable addTabeAction
    SKGTabPage* toSave = currentPage();
    if (toSave && toSave->isPin()) {
        d->m_switchPinState->setText(i18nc("Noun, user action", "Unpin this page"));
    } else {
        d->m_switchPinState->setText(i18nc("Noun, user action", "Pin this page"));
    }
    if (d->m_addTabAction) {
        d->m_addTabAction->setEnabled(atLeastOnePageOpened);
    }
    if (d->m_closePageAction) {
        d->m_closePageAction->setEnabled(atLeastOnePageOpened && toSave && !toSave->isPin());
    }
    if (d->m_switchPinState) {
        d->m_switchPinState->setEnabled(atLeastOnePageOpened);
    }
    if (d->m_closeAllPagesAction) {
        d->m_closeAllPagesAction->setEnabled(atLeastOnePageOpened);
    }
    if (d->m_closeAllOtherPagesAction) {
        d->m_closeAllOtherPagesAction->setEnabled(d->m_tabWidget->count() > 1);
    }
    if (d->m_reopenLastClosed) {
        d->m_reopenLastClosed->setEnabled(d->m_historyClosedPages.count());
    }

    if (d->m_saveDefaultStateAction) {
        d->m_saveDefaultStateAction->setEnabled(toSave && !toSave->getDefaultStateAttribute().isEmpty());
    }
    if (d->m_resetDefaultStateAction) {
        d->m_resetDefaultStateAction->setEnabled(toSave && !toSave->getDefaultStateAttribute().isEmpty());
    }
    if (d->m_overwriteBookmarkStateAction) {
        d->m_overwriteBookmarkStateAction->setEnabled(toSave && !toSave->getBookmarkID().isEmpty());
    }
    if (d->m_enableEditorAction) {
        d->m_enableEditorAction->setEnabled(toSave && toSave->isEditor());
    }
    if (d->m_zoomSelector) {
        d->m_zoomSelector->setVisible(toSave && toSave->isZoomable());
        if (toSave) {
            d->m_zoomSelector->setValue(toSave->zoomPosition());
            QWidget* zoomWidget = toSave->zoomableWidget();
            SKGTreeView* treeView = qobject_cast<SKGTreeView*>(zoomWidget);
            if (treeView) {
                disconnect(treeView, &SKGTreeView::zoomChanged, 0 , 0);
                connect(treeView, &SKGTreeView::zoomChanged, [ = ](int val) {
                    d->m_zoomSelector->setValue(val);
                });
            } else {
                SKGWebView* webView = qobject_cast<SKGWebView*>(zoomWidget);
                if (webView) {
                    disconnect(webView, &SKGWebView::zoomChanged, 0 , 0);
                    connect(webView, &SKGWebView::zoomChanged, [ = ](int val) {
                        d->m_zoomSelector->setValue(val);
                    });
                }
            }
        }
    }

    if (d->m_actLock) {
        d->m_actLock->setVisible(d->ui.kDockContext->features() == QDockWidget::AllDockWidgetFeatures);
    }
    if (d->m_actUnLock) {
        d->m_actUnLock->setVisible(d->ui.kDockContext->features() == QDockWidget::NoDockWidgetFeatures);
    }

    if (d->m_previousAction) {
        QList<SKGTabPage::SKGPageHistoryItem> list;
        if (toSave) {
            list = toSave->getPreviousPages();
        }
        d->m_previousAction->setEnabled(list.count());
    }
    if (d->m_nextAction) {
        QList<SKGTabPage::SKGPageHistoryItem> list;
        if (toSave) {
            list = toSave->getNextPages();
        }
        d->m_nextAction->setEnabled(list.count());
    }

    // Set current selection of context
    d->ui.kContextList->clearSelection();
    if (toSave) {
        // Get plugin of current page
        SKGInterfacePlugin* plugin = getPluginByName(toSave->objectName());
        int index = (plugin ? plugin->property("contextItem").toInt() : -1);
        if (index != -1) {
            d->ui.kContextList->setCurrentItem(d->ui.kContextList->item(index));
        }
    }

    // Set window title
    QString modified;
    if (getDocument()->isFileModified()) {
        modified += i18nc("Noun, indicate that current document is modified", " [modified]");
    }
    if (getDocument()->isReadOnly()) {
        modified += i18nc("Noun, indicate that current document is loaded in read only", " [read only]");
    }
    QString fileName = getDocument()->getCurrentFileName();
    if (fileName.isEmpty()) {
        fileName = i18nc("Noun, default name for a new document", "Untitled");
    } else {
        if (fileName != d->m_fileName) {
            d->m_fileName = fileName;
#ifdef KActivities_FOUND
            if (!d->m_fileName.isEmpty()) {
                d->m_activityResourceInstance->setUri(d->m_fileName);
            }
#endif
        }
    }
    setWindowTitle(i18nc("Title of the main window", "%1%2 - %3", fileName, modified, qApp->applicationDisplayName()));

    QApplication::restoreOverrideCursor();
}

SKGTabPage::SKGPageHistoryItem SKGMainPanel::currentPageHistoryItem() const
{
    SKGTabPage::SKGPageHistoryItem cpage;
    int currentIndex = currentPageIndex();
    SKGTabPage* cPage = currentPage();
    if (currentIndex >= 0 && cPage) {
        cpage.plugin = cPage->objectName();
        SKGInterfacePlugin* plugin = SKGMainPanel::getMainPanel()->getPluginByName(cpage.plugin);
        if (plugin) {
            cpage.name = d->m_tabWidget->tabText(currentIndex);
            cpage.icon = plugin->icon();
        }
        cpage.state = cPage->getState();
        cpage.bookmarkID = cPage->getBookmarkID();
    }

    return cpage;
}

SKGTabPage* SKGMainPanel::openPage(SKGInterfacePlugin* plugin, int index, const QString& parameters, const QString& title, const QString& iID, bool iSetCurrent)
{
    SKGTRACEINFUNC(1);
    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
    bool previous = d->m_tabWidget->blockSignals(true);
    // If the current page is pin, then open new page
    SKGTabPage* cPage = currentPage();
    if (cPage && cPage->isPin()) {
        index = -1;
        iSetCurrent = true;
    }

    SKGTabPage* w = NULL;

    QList<SKGTabPage::SKGPageHistoryItem> previousPages;
    if (index != -1) {
        int currentIndex = currentPageIndex();
        if (currentIndex >= 0 && cPage) {
            previousPages = cPage->getPreviousPages();
            previousPages.insert(0, currentPageHistoryItem());

            d->m_tabWidget->removeTab(currentIndex);
            closePage(cPage);

            // Repair the history of closed page
            if (!d->m_historyClosedPages.isEmpty()) {
                d->m_historyClosedPages.removeLast();
            }
        }
    }

    if (plugin) {
        w = plugin->getWidget();
        if (w) {
            // Title
            QString title2 = (title.isEmpty() ? plugin->title() : title);
            w->setObjectName(plugin->objectName());
            if (!iID.isEmpty()) {
                w->setBookmarkID(iID);
            }

            QString param = parameters;
            if (param.isEmpty()) {
                QString def = w->getDefaultStateAttribute();
                if (!def.isEmpty()) {
                    param = getDocument()->getParameter(def);
                }
            }
            SKGTRACEL(10) << "state=[" << param << "]" << endl;
            w->setState(param);
            connect(w, &SKGTabPage::selectionChanged, this, &SKGMainPanel::refresh);

            if (index == -1) {
                SKGTRACEINFUNC(20);
                d->m_tabWidget->addTab(w, QIcon::fromTheme(plugin->icon()), title2);
                if (iSetCurrent) {
                    d->m_tabWidget->setCurrentWidget(w);
                }
            } else {
                SKGTRACEINFUNC(20);
                d->m_tabWidget->insertTab(index, w, QIcon::fromTheme(plugin->icon()), title2);
                if (iSetCurrent) {
                    d->m_tabWidget->setCurrentWidget(w);
                }

                w->setPreviousPages(previousPages);
                QList<SKGTabPage::SKGPageHistoryItem> empty;
                w->setNextPages(empty);
            }
            SKGTRACEL(1) << "opening plugin [" << plugin->objectName() << ']' << endl;
            Q_EMIT pageOpened();
        }
    } else {
        getDocument()->sendMessage(i18nc("An information message",  "Impossible to open the page because the plugin was not found"), SKGDocument::Error);
        notify();  // Due to sendMessage not in a transaction
    }

    // Show/hide main widget
    bool atLeastOnePageOpened = (d->m_tabWidget->count() > 0);
    d->m_tabWidget->setVisible(atLeastOnePageOpened);
    if (d->m_mainWidget) {
        d->m_mainWidget->setVisible(!atLeastOnePageOpened);
    }

    d->m_tabWidget->blockSignals(previous);
    if (iSetCurrent) {
        Q_EMIT currentPageChanged();
    }
    QApplication::restoreOverrideCursor();
    return w;
}

int SKGMainPanel::currentPageIndex() const
{
    return d->m_tabWidget->currentIndex();
}

SKGTabPage* SKGMainPanel::currentPage() const
{
    return qobject_cast< SKGTabPage* >(d->m_tabWidget->currentWidget());
}

QSplashScreen* SKGMainPanel::splashScreen() const
{
    return d->m_splashScreen;
}

int SKGMainPanel::countPages() const
{
    return d->m_tabWidget->count();
}

SKGTabPage* SKGMainPanel::page(int iIndex) const
{
    return static_cast<SKGTabPage*>(d->m_tabWidget->widget(iIndex));
}

void SKGMainPanel::setCurrentPage(int iIndex)
{
    d->m_tabWidget->setCurrentIndex(iIndex);
}

void SKGMainPanel::onBeforeOpenContext()
{
    d->m_middleClick = QApplication::mouseButtons() & Qt::MidButton;
}

bool SKGMainPanel::openPage(const QUrl& iUrl, bool iNewPage)
{
    QUrl url(iUrl);
    if (url.scheme() == "skg") {
        // Get plugin
        SKGInterfacePlugin* plugin = getPluginByName(url.host());
        if (plugin) {
            // Open special page
            SKGTabPage* w = plugin->getWidget();
            if (w) {
                // Create xml
                QString path = url.path().remove('/');
                QDomDocument doc("SKGML");
                doc.setContent(getDocument()->getParameter(path.isEmpty() ? w->getDefaultStateAttribute() : path));
                QDomElement root = doc.documentElement();
                if (root.isNull()) {
                    root = doc.createElement("parameters");
                    doc.appendChild(root);
                }
                auto params = QUrlQuery(url).queryItems();
                foreach(auto p, params) {
                    QString value = QUrl::fromPercentEncoding(p.second.toLatin1());
                    SKGMainPanelPrivate::setAttribute(root, p.first, value);
                }

                // Open page
                openPage(plugin, iNewPage ? -1 : currentPageIndex(), doc.toString());
                return true;
            }
        } else {
            // Trigger action
            QAction* act = SKGMainPanel::getMainPanel()->getGlobalAction(url.host());
            if (act) {
                act->trigger();
                return true;
            }
        }
    } else {
        QDesktopServices::openUrl(iUrl);
        return true;
    }
    displayErrorMessage(SKGError(ERR_ABORT, i18nc("Error message", "Unknown plugin or action [%1] in url [%2]", url.host(), iUrl.toString())));

    return false;
}

bool SKGMainPanel::openPage(const QString& iUrl, bool iNewPage)
{
    // Get the url
    QString urlString(iUrl);
    if (urlString.isEmpty()) {
        QAction* act = qobject_cast< QAction* >(sender());
        if (act) {
            urlString = act->data().toString();
        }
    }

    return openPage(QUrl(urlString), iNewPage);
}

SKGTabPage* SKGMainPanel::openPage(int iPage, bool iNewPage)
{
    SKGTRACEINFUNC(1);
    SKGTRACEL(1) << "iPage=" << iPage << endl;
    int index = d->ui.kContextList->item(iPage)->data(12).toInt();
    return openPage(getPluginByIndex(index), iNewPage ? -1 : currentPageIndex());
}

void SKGMainPanel::onOpenContext()
{
    SKGTRACEINFUNC(1);
    int cpage = -1;
    QAction* s = qobject_cast<QAction*>(this->sender());
    if (s) {
        cpage = s->data().toInt();
    } else {
        cpage = d->ui.kContextList->currentRow();
    }
    if (cpage != -1) {
        openPage(cpage, QApplication::keyboardModifiers() &Qt::ControlModifier || d->m_middleClick || QGuiApplication::mouseButtons() & Qt::MidButton);
    }
    d->m_middleClick = false;
}

void SKGMainPanel::switchPinPage(QWidget* iWidget)
{
    SKGTabPage* toSwitch = qobject_cast< SKGTabPage* >(iWidget);
    if (toSwitch == NULL) {
        toSwitch = currentPage();
    }

    if (toSwitch) {
        toSwitch->setPin(!toSwitch->isPin());
        Q_EMIT currentPageChanged();
    }
}

void SKGMainPanel::closePageByIndex(int iIndex)
{
    QWidget* w = NULL;
    if (iIndex >= 0) {
        w = d->m_tabWidget->widget(iIndex);
    } else {
        w = d->m_tabWidget->currentWidget();
    }

    closePage(w);
}

void SKGMainPanel::closePage(QWidget* iWidget, bool iForce)
{
    SKGTRACEINFUNC(1);
    if (getDocument()->getCurrentTransaction()) {
        QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
        displayMessage(i18nc("Information message", "A page cannot be closed when an operation is running."), SKGDocument::Information);
        QApplication::restoreOverrideCursor();
    } else {
        SKGTabPage* toRemove = qobject_cast< SKGTabPage* >(iWidget);
        if (toRemove == NULL) {
            toRemove = currentPage();
        }
        if (toRemove && toRemove->close(iForce)) {
            historyPage item;
            item.current = currentPageHistoryItem();
            item.next = toRemove->getNextPages();
            item.previous = toRemove->getPreviousPages();
            d->m_historyClosedPages.push_back(item);
            delete toRemove;

            emit pageClosed();
        }
    }

    // Show/hide main widget
    bool atLeastOnePageOpened = (d->m_tabWidget->count() > 0);
    d->m_tabWidget->setVisible(atLeastOnePageOpened);
    if (d->m_mainWidget) {
        d->m_mainWidget->setVisible(!atLeastOnePageOpened);
    }
}

void SKGMainPanel::closeAllPages(bool iForce)
{
    SKGTRACEINFUNC(1);
    bool previous = d->m_tabWidget->blockSignals(true);
    int nb = d->m_tabWidget->count();
    for (int i = nb - 1; i >= 0; --i) {
        SKGTabPage* w = qobject_cast< SKGTabPage* >(d->m_tabWidget->widget(i));
        if (w && (iForce || !w->isPin())) {
            closePage(w, iForce);
        }
    }
    d->m_tabWidget->blockSignals(previous);
    KMessageBox::enableMessage("closepinnedpage");
    Q_EMIT currentPageChanged();
}

void SKGMainPanel::closeAllOtherPages(QWidget* iWidget)
{
    SKGTRACEINFUNC(1);
    bool previous = d->m_tabWidget->blockSignals(true);
    QWidget* toKeep = iWidget;
    if (toKeep == NULL) {
        toKeep = currentPage();
    }

    int nb = d->m_tabWidget->count();
    for (int i = nb - 1; i >= 0; --i) {
        SKGTabPage* w = qobject_cast< SKGTabPage* >(d->m_tabWidget->widget(i));
        if (w && w != toKeep && !w->isPin()) {
            closePage(w);
        }
    }
    d->m_tabWidget->blockSignals(previous);
    Q_EMIT currentPageChanged();
}

void SKGMainPanel::saveDefaultState()
{
    SKGTRACEINFUNC(1);
    SKGError err;
    SKGTabPage* toSave = currentPage();
    if (toSave) {
        // Get bookmarks uuid
        QString uuid = toSave->getBookmarkID();

        // Reset bookmarks uuid to overwrite page state
        toSave->setBookmarkID("");

        // Overwrite
        toSave->overwrite(false);

        // Set original bookmarks uuid
        toSave->setBookmarkID(uuid);
    }
}

void SKGMainPanel::overwriteBookmarkState()
{
    SKGTRACEINFUNC(1);
    SKGError err;
    SKGTabPage* toSave = currentPage();
    if (toSave) {
        // Get bookmarks uuid
        QString uuid = toSave->getBookmarkID();
        if (!uuid.isEmpty()) {
            // Overwrite
            toSave->overwrite(false);
        }
    }
}

void SKGMainPanel::resetDefaultState()
{
    SKGTRACEINFUNC(1);
    SKGError err;
    SKGTabPage* toSave = currentPage();
    if (toSave) {
        QString name = toSave->getDefaultStateAttribute();
        if (!name.isEmpty()) {
            SKGBEGINLIGHTTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Reset default state"), err);
            IFOKDO(err, getDocument()->setParameter(name, "<!DOCTYPE SKGML>"))

            // Refresh panel
            IFOK(err) toSave->setState("");
        }
    }

    // status bar
    IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Default state has been reset")))
    displayErrorMessage(err);
}

void SKGMainPanel::addTab()
{
    SKGTRACEINFUNC(1);
    SKGTabPage* cPage = currentPage();
    if (cPage) {
        openPage(getPluginByName(cPage->objectName()));
    }
}

bool SKGMainPanel::eventFilter(QObject* iObject, QEvent* iEvent)
{
    if (iObject && iEvent && iEvent->type() == QEvent::Resize) {
        QResizeEvent* rEvent = static_cast<QResizeEvent*>(iEvent);
        if (rEvent) {
            QSize newSize = rEvent->size();

            // Compute icon size
            int s = qMax(qMin(newSize.width() / 5, 64), 16);
            d->ui.kContextList->setIconSize(QSize(s, s));
        }
    }
    return false;
}

QString SKGMainPanel::getTipOfDayFileName()
{
    return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) % "/" % QCoreApplication::applicationName() % ".tips";
}

void SKGMainPanel::onTipOfDay()
{
    SKGTRACEINFUNC(1);
    KTipDialog::showTip(this, QFileInfo(getTipOfDayFileName()).fileName(), (sender() != NULL));
}

void SKGMainPanel::onTrayActivated(QSystemTrayIcon::ActivationReason reason)
{
    SKGTRACEINFUNC(1);
    if (reason == QSystemTrayIcon::Trigger) {
        setVisible(!isVisible());
    }
}

void SKGMainPanel::notify(int iTransaction)
{
    SKGTRACEINFUNC(1);
    SKGTRACEL(1) << "iTransaction=" << iTransaction << endl;

    // Notify
    SKGObjectBase transaction(getDocument(), "doctransaction", iTransaction);
    if (iTransaction == 0 || transaction.getAttribute("t_mode") != "R") {
        QStringList msg;
        QList<SKGDocument::MessageType> type;
        getDocument()->getMessages(iTransaction, msg, type, false);
        int nbMessages = msg.count();
        if (nbMessages) {
            // Build list of types
            SKGDocument::MessageType maxType = SKGDocument::Positive;
            QList<SKGDocument::MessageType> listGroups;
            for (int i = 0; i < nbMessages; ++i) {
                if (listGroups.count() == 0 || type.at(i) != listGroups.at(listGroups.count() - 1)) {
                    listGroups.push_back(type.at(i));
                }
                if (static_cast<int>(type.at(i)) >= static_cast<int>(maxType)) {
                    maxType = type.at(i);
                }
            }

            // Is the number of type acceptable?
            bool modeGrouped = false;
            if (listGroups.count() > 5 || nbMessages > 20) {
                // Too many group ==> simplification
                listGroups.clear();
                listGroups.push_back(maxType);
                modeGrouped = true;
            }

            // Build message
            QString message;
            int indexGroup = 0;
            for (int i = 0; i < nbMessages; ++i) {
                QString m = msg.at(i);
                SKGDocument::MessageType t = type.at(i);
                if (modeGrouped) {
                    if (t == SKGDocument::Warning) {
                        m = i18nc("Warning header", "Warning: %1", m);
                    } else if (t == SKGDocument::Error) {
                        m = i18nc("Error header", "Error: %1", m);
                    } else if (t == SKGDocument::Information) {
                        m = i18nc("Information header", "Information: %1", m);
                    } else if (t == SKGDocument::Positive) {
                        m = i18nc("Done header", "Done: %1", m);
                    }
                }
                if (modeGrouped || t == listGroups.at(indexGroup)) {
                    // Same group
                    if (!message.isEmpty()) {
                        message += "<br>";
                    }
                    message += m;
                } else {
                    // Different group
                    displayMessage(message, listGroups.at(indexGroup));

                    // Reset message
                    message = m;

                    indexGroup++;
                }
            }

            if (nbMessages < 21 || !SKGServices::getEnvVariable("SKGTEST").isEmpty()) {
                // Display a simple notification
                /*KNotification* notify = new KNotification(KAboutData::applicationData().componentName() % "_info_event" , this);
                notify->setText(message);
                notify->sendEvent();*/

                displayMessage(message, listGroups.at(indexGroup));
            } else {
                // Too many message, display a warning panel
                KMessageBox::information(SKGMainPanel::getMainPanel(), message, i18nc("Noun",  "Notification"));
            }
        }
    }
}

void SKGMainPanel::changeEvent(QEvent* e)
{
    KXmlGuiWindow::changeEvent(e);
}

QLabel* SKGMainPanel::statusNormalMessage() const
{
    return d->m_kNormalMessage;
}

KMessageWidget* SKGMainPanel::displayMessage(const QString& iMessage, SKGDocument::MessageType iType)
{
    // Create message widget
    KMessageWidget* msg = NULL;
    if (!iMessage.isEmpty()) {
        msg = new KMessageWidget(this);
        msg->setText(iMessage);
        msg->setMessageType(static_cast<KMessageWidget::MessageType>(iType));
        QTimer::singleShot(iType == SKGDocument::Positive ? 5000 : iType == SKGDocument::Information ? 10000 : 20000, Qt::CoarseTimer, msg, SLOT(deleteLater()));
        d->m_mainLayout->insertWidget(qMax(d->m_mainLayout->indexOf(d->m_mainWidget) - 1, 0), msg);

        // Store message
        KMessageWidget* msg2 = new KMessageWidget(this);
        msg2->setText(iMessage);
        msg2->setMessageType(static_cast<KMessageWidget::MessageType>(iType));
        QVBoxLayout* l = qobject_cast< QVBoxLayout* >(d->ui.kMessagesLayout->layout());
        if (l) {
            l->insertWidget(0, msg2);
        }

        // Emit message
        //  [Event/error]
        //  [Event/neutral]
        //  [Event/positive]
        KNotification* notification = new KNotification(iType == SKGDocument::Error ? "error" : (iType == SKGDocument::Positive ? "positive" : "neutral") , this);
        notification->setText(iMessage);
        notification->sendEvent();

        // Alert
        if (iType == SKGDocument::Error || iType == SKGDocument::Warning) {
            qApp->alert(this);
        }
    }

    return msg;
}

KMessageWidget* SKGMainPanel::displayErrorMessage(const QString& iMessage)
{
    QString msg = iMessage;
    if (msg.isEmpty()) {
        QAction* act = qobject_cast< QAction* >(sender());
        if (act) {
            msg = act->data().toString();
        }
    }
    return displayMessage(msg, SKGDocument::Error);
}

KMessageWidget* SKGMainPanel::displayErrorMessage(const SKGError& iError, QAction* iAction)
{
    SKGTRACEINFUNC(1);
    KMessageWidget* msg = NULL;
    SKGMainPanel* parent = SKGMainPanel::getMainPanel();
    if (parent) {
        if (iError) {
            // Get the message
            msg = parent->displayErrorMessage(iError.getFullMessage());

            // Add history action in case of
            if (iError.getHistoricalSize()) {
                QAction* history = new QAction(i18nc("Noun", "History"), msg);
                history->setIcon(QIcon::fromTheme("dialog-information"));
                history->setData(iError.getFullMessageWithHistorical());
                msg->addAction(history);
                connect(history, &QAction::triggered, parent, [ = ] { parent->displayErrorMessage();});
                connect(history, &QAction::triggered, msg, &KMessageWidget::deleteLater, Qt::QueuedConnection);
            }

            // Add the additional action
            if (iAction) {
                iAction->setParent(msg);
                msg->addAction(iAction);
                connect(iAction, &QAction::triggered, msg, &KMessageWidget::deleteLater, Qt::QueuedConnection);
            }
        } else {
            // Status bar
            QLabel* label = parent->statusNormalMessage();
            QString message = iError.getMessage();
            if (label && !message.isEmpty()) {
                label->setText(message);
            }
        }
    }
    return msg;
}

void SKGMainPanel::onCancelCurrentAction()
{
    SKGMainPanelPrivate::m_currentActionCanceled = true;
}

void SKGMainPanel::onQuitAction()
{
    // Bug 2777697: To be sure that all page modifications are closed
    closeAllPages(true);
    // Bug 2777697:

    qApp->closeAllWindows();
}

QString SKGMainPanel::getSaveFileName(const QString& startDir, const QString& filter, QWidget* iParent, const QString& caption, QString* iCodec)
{
    QString fileName;
    if (iCodec) {
        QString lastCodecUsed = QTextCodec::codecForLocale()->name();
        KEncodingFileDialog::Result result = KEncodingFileDialog::getSaveUrlAndEncoding(lastCodecUsed, QUrl(startDir), filter , iParent, caption);
        if (result.URLs.count()) {
            fileName = result.URLs.at(0).toString();
        }
        *iCodec = result.encoding;
    } else {
        fileName = QFileDialog::getSaveFileName(iParent, caption, startDir, filter);
    }
    if (fileName.isEmpty()) {
        return "";
    }
    QFile f(fileName);
    if (f.exists() && KMessageBox::warningContinueCancel(iParent,
            i18nc("Question", "File <b>%1</b> already exists. Do you really want to overwrite it?", fileName),
            i18nc("Question", "Warning"),
            KGuiItem(i18nc("Verb",  "Save"), QIcon::fromTheme("document-save"))) != KMessageBox::Continue) {
        return "";
    }

    return fileName;
}

void SKGMainPanel::fillWithDistinctValue(
    QWidget* iWidget,
    SKGDocument* iDoc,
    const QString& iTable,
    const QString& iAttribut,
    const QString& iWhereClause,
    bool iAddoperators)
{
    SKGTRACEINFUNC(10);

    if (iWidget && iDoc) {
        // Get list
        QStringList list;
        iDoc->getDistinctValues(iTable, iAttribut, iWhereClause, list);
        if (list.count() && !list.at(0).isEmpty()) {
            list.insert(0, "");
        }

        // Sorting list
        // Correction bug 202341 vvv
        qSort(list.begin(), list.end(), SKGMainPanelPrivate::naturalLessThan);
        // Correction bug 202341 ^^^

        // Add operator
        if (iAddoperators) {
            list.push_back('=' % i18nc("Key word to modify a string into a field", "capitalize"));
            list.push_back('=' % i18nc("Key word to modify a string into a field", "capwords"));
            list.push_back('=' % i18nc("Key word to modify a string into a field", "lower"));
            list.push_back('=' % i18nc("Key word to modify a string into a field", "trim"));
            list.push_back('=' % i18nc("Key word to modify a string into a field", "upper"));
        }

        auto comp = new QCompleter(list);
        if (comp) {
            comp->setCaseSensitivity(Qt::CaseInsensitive);
            comp->setFilterMode(Qt::MatchContains);

            // Fill completion
            KComboBox* kcmb = qobject_cast<KComboBox*> (iWidget);
            if (kcmb) {
                // Fill combo
                kcmb->clear();
                kcmb->addItems(list);

                kcmb->setCompleter(comp);
            } else {
                QLineEdit* kline = qobject_cast<QLineEdit*> (iWidget);
                if (kline) {
                    kline->setClearButtonEnabled(true);
                    kline->setCompleter(comp);
                }
            }
        }
    }
}

SKGMainPanel* SKGMainPanel::getMainPanel()
{
    return SKGMainPanelPrivate::m_mainPanel;
}

void SKGMainPanel::onZoomChanged()
{
    SKGTabPage* toSave = currentPage();
    if (toSave) {
        toSave->setZoomPosition(d->m_zoomSelector->value());
        d->m_zoomSelector->setValue(toSave->zoomPosition());  // In case of a limit is reached
    }
}

void SKGMainPanel::setMainWidget(QWidget* iWidget)
{
    if (d->m_mainWidget == NULL && d->m_mainLayout && iWidget) {
        d->m_mainWidget = iWidget;
        d->m_mainLayout->addWidget(d->m_mainWidget);

        // Show/hide main widget
        d->m_tabWidget->setVisible(d->m_tabWidget->count());
        if (d->m_mainWidget) {
            d->m_mainWidget->setVisible(!d->m_tabWidget->isVisible());
        }
    }
}

SKGTabWidget* SKGMainPanel::getTabWidget() const
{
    return d->m_tabWidget;
}

void SKGMainPanel::onClearMessages()
{
    QLayout* l = d->ui.kMessagesLayout->layout();
    if (l) {
        // Remove all item of the layout
        while (l->count() > 1) {
            QLayoutItem* child = l->takeAt(0);
            if (child) {
                QWidget* w = child->widget();
                delete w;
                delete child;
            }
        }
    }
}

