File indexing completed on 2024-05-05 17:19:02

0001 /***************************************************************************
0002  * SPDX-FileCopyrightText: 2022 S. MANKOWSKI stephane@mankowski.fr
0003  * SPDX-FileCopyrightText: 2022 G. DE BURE support@mankowski.fr
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  ***************************************************************************/
0006 /** @file
0007 * This file defines classes skgmainpanel.
0008 *
0009 * @author Stephane MANKOWSKI / Guillaume DE BURE
0010 */
0011 #include "skgmainpanel.h"
0012 
0013 #include <kaboutdata.h>
0014 #include <kactioncollection.h>
0015 #include <kactionmenu.h>
0016 #include <kcombobox.h>
0017 #include <kconfigdialog.h>
0018 #include <kencodingfiledialog.h>
0019 #include <kformat.h>
0020 #include <kmessagebox.h>
0021 #include <kmessagewidget.h>
0022 #include <knotification.h>
0023 #include <knotifyconfigwidget.h>
0024 #include <kselectaction.h>
0025 #include <kservice.h>
0026 #include <kservicetypetrader.h>
0027 #include <kstandardaction.h>
0028 #include <kstatusnotifieritem.h>
0029 #include <kstringhandler.h>
0030 #include <ktoggleaction.h>
0031 #include <ktoolbar.h>
0032 #include <ktoolbarpopupaction.h>
0033 #include <kxmlguifactory.h>
0034 #include <qvariant.h>
0035 
0036 #ifdef KActivities_FOUND
0037 #include <kactivities/resourceinstance.h>
0038 #endif
0039 
0040 #ifdef SKG_DBUS
0041 #include <QDBusConnection>
0042 #include <QDBusMessage>
0043 #endif
0044 
0045 #include <qaction.h>
0046 #include <qapplication.h>
0047 #include <qcollator.h>
0048 #include <qcompleter.h>
0049 #include <qdesktopservices.h>
0050 #include <qdir.h>
0051 #include <qdom.h>
0052 #include <qevent.h>
0053 #include <qfiledialog.h>
0054 #include <qheaderview.h>
0055 #include <qlineedit.h>
0056 #include <qmenu.h>
0057 #include <qprocess.h>
0058 #include <qprogressbar.h>
0059 #include <qpushbutton.h>
0060 #include <qscrollarea.h>
0061 #include <qsplashscreen.h>
0062 #include <qtabbar.h>
0063 #include <qtabwidget.h>
0064 #include <qtextcodec.h>
0065 #include <qurlquery.h>
0066 #include <QRandomGenerator>
0067 
0068 #include <algorithm>    // std::sort
0069 
0070 #include "skgdefine.h"
0071 #include "skginterfaceplugin.h"
0072 #include "skgservices.h"
0073 #include "skgtraces.h"
0074 #include "skgtransactionmng.h"
0075 #include "skgtreeview.h"
0076 #include "skgwebview.h"
0077 #include "skgzoomselector.h"
0078 
0079 struct doublePointer {
0080     void* p1;
0081     void* p2;
0082 };
0083 
0084 struct historyPage {
0085     SKGTabPage::SKGPageHistoryItem current;
0086     SKGTabPage::SKGPageHistoryItemList next;
0087     SKGTabPage::SKGPageHistoryItemList previous;
0088 };
0089 
0090 struct actionDetails {
0091     QPointer<QAction> action;
0092     QStringList tables;
0093     int min{};
0094     int max{};
0095     int ranking{};
0096     bool focus{};
0097 };
0098 class SKGMainPanelPrivate
0099 {
0100 public:
0101     Ui::skgmainpanel_base ui{};
0102     Ui::skgmainpanel_pref uipref{};
0103 
0104     SKGTabWidget* m_tabWidget;
0105     QSplashScreen* m_splashScreen;
0106 
0107     SKGDocument* m_currentDocument;
0108 
0109     QList<SKGInterfacePlugin*> m_pluginsList;
0110     QMap<QString, actionDetails > m_registeredGlogalAction;
0111     QList<historyPage> m_historyClosedPages;
0112 
0113     QMenu* m_contextMenu;
0114     QAction* m_actHideContextItem;
0115     QAction* m_actShowAllContextItems;
0116     QAction* m_actDelete;
0117     QAction* m_closePageAction;
0118     QAction* m_closeAllOtherPagesAction;
0119     QAction* m_switchPinState;
0120     QAction* m_saveDefaultStateAction;
0121     QAction* m_resetDefaultStateAction;
0122     QAction* m_overwriteBookmarkStateAction;
0123     QAction* m_configureAction;
0124     QAction* m_enableEditorAction;
0125     QAction* m_fullScreenAction;
0126     QAction* m_actLock;
0127     QAction* m_actUnLock;
0128     QAction* m_reopenLastClosed;
0129     KStatusNotifierItem* m_kSystemTrayIcon;
0130     QLabel* m_kNormalMessage;
0131     SKGZoomSelector* m_zoomSelector;
0132 
0133     KToolBarPopupAction* m_previousAction;
0134     KToolBarPopupAction* m_nextAction;
0135     KToolBarPopupAction* m_buttonMenuAction;
0136     KToggleAction* m_showMenuBarAction;
0137 
0138     QMenu* m_previousMenu;
0139     QMenu* m_nextMenu;
0140     QMenu* m_buttonMenu;
0141     QWidget* m_mainWidget;
0142     QVBoxLayout* m_mainLayout{};
0143 
0144     doublePointer m_progressObjects{};
0145     bool m_middleClick;
0146     bool m_saveOnClose;
0147     QString m_fileName;
0148     SKGWidget* m_widgetHavingSelection;
0149     QStringList m_tipsOfTheDay;
0150 
0151 #ifdef KActivities_FOUND
0152     KActivities::ResourceInstance* m_activityResourceInstance;
0153 #endif
0154 
0155     static bool m_currentActionCanceled;
0156     static SKGMainPanel* m_mainPanel;
0157 
0158     SKGMainPanelPrivate()
0159     {
0160         m_actHideContextItem = nullptr; m_actShowAllContextItems = nullptr; m_actDelete = nullptr;
0161         m_closePageAction = nullptr; m_closeAllOtherPagesAction = nullptr; m_switchPinState = nullptr; m_saveDefaultStateAction = nullptr;
0162         m_resetDefaultStateAction = nullptr; m_overwriteBookmarkStateAction = nullptr; m_configureAction = nullptr; m_enableEditorAction = nullptr; m_fullScreenAction = nullptr; m_actLock = nullptr;
0163         m_actUnLock = nullptr;
0164         m_kSystemTrayIcon = nullptr;
0165         m_kNormalMessage = nullptr; m_zoomSelector = nullptr;  m_previousAction = nullptr; m_nextAction = nullptr; m_buttonMenuAction = nullptr;
0166         m_mainWidget = nullptr; m_mainLayout = nullptr; m_middleClick = false; m_saveOnClose = false;
0167         m_widgetHavingSelection = nullptr;
0168         m_tabWidget = nullptr; m_splashScreen = nullptr; m_currentDocument = nullptr;
0169         m_contextMenu = nullptr;
0170         m_reopenLastClosed = nullptr;
0171         m_showMenuBarAction = nullptr;
0172         m_previousMenu = nullptr;
0173         m_nextMenu = nullptr;
0174         m_buttonMenu = nullptr;
0175         m_progressObjects.p1 = nullptr;
0176         m_progressObjects.p2 = nullptr;
0177 #ifdef KActivities_FOUND
0178         m_activityResourceInstance = nullptr;
0179 #endif
0180     }
0181 
0182     static int progressBarCallBack(int iPos, qint64 iTime, const QString& iName, void* iProgressBar)
0183     {
0184         Q_UNUSED(iTime)
0185         QProgressBar* progressBar = nullptr;
0186         QPushButton* button = nullptr;
0187         auto* pointers = static_cast<doublePointer*>(iProgressBar);
0188         if (pointers != nullptr) {
0189             progressBar = static_cast<QProgressBar*>(pointers->p1);
0190             button = static_cast<QPushButton*>(pointers->p2);
0191         }
0192 
0193         bool visible = (iPos > 0 && iPos <= 100);
0194         if (progressBar != nullptr) {
0195             QString commonFormat = QStringLiteral("%p%");
0196             /*if (iPos > 0) {
0197              *           qint64 estimatedTime = 100 * iTime / iPos;
0198              *           qint64 remainingTime = estimatedTime - iTime;
0199              *           commonFormat = commonFormat % " - " % i18nc("To print a remaining time (in seconde) / estimated time (in second)", "%1s / %2s",
0200              *                          SKGServices::intToString(remainingTime / 1000),
0201              *                          SKGServices::intToString(estimatedTime / 1000));
0202             }*/
0203             progressBar->setFormat(iName.isEmpty() ? commonFormat : commonFormat % '\n' % iName);
0204             progressBar->setValue(iPos);
0205             progressBar->setVisible(visible);
0206             if (iPos == 100) {
0207                 QTimer::singleShot(300, Qt::CoarseTimer, progressBar, &QProgressBar::hide);
0208             }
0209             progressBar->setToolTip(iName);
0210 #ifdef SKG_DBUS
0211             QDBusMessage message = QDBusMessage::createSignal(QLatin1String("/org/") + KAboutData::applicationData().componentName() + QLatin1String("/UnityLauncher"),
0212                                    QStringLiteral("com.canonical.Unity.LauncherEntry"),
0213                                    QStringLiteral("Update"));
0214             message << "application://org.kde.skrooge.desktop";
0215 
0216             QVariantMap setProperty;
0217             if (iPos == 100) {
0218                 setProperty.insert(QStringLiteral("progress"), 0.0);
0219                 setProperty.insert(QStringLiteral("progress-visible"), false);
0220             } else {
0221                 setProperty.insert(QStringLiteral("progress"), static_cast<double>(iPos / 100.0));
0222                 setProperty.insert(QStringLiteral("progress-visible"), true);
0223             }
0224             message << setProperty;
0225             QDBusConnection::sessionBus().send(message);
0226 #endif
0227         }
0228         if (button != nullptr) {
0229             button->setVisible(visible);
0230             if (iPos == 100) {
0231                 QTimer::singleShot(300, Qt::CoarseTimer, button, &QPushButton::hide);
0232             }
0233         }
0234 
0235 
0236         SKGMainPanelPrivate::m_currentActionCanceled = false;
0237         if (iPos != 0 && iPos != 100) {
0238             qApp->processEvents(QEventLoop::AllEvents, 500);
0239         }
0240         return (SKGMainPanelPrivate::m_currentActionCanceled ? 1 : 0);
0241     }
0242 
0243     static bool adviceLessThan(const SKGAdvice& s1, const SKGAdvice& s2)
0244     {
0245         if (s1.getPriority() == s2.getPriority()) {
0246             return (s1.getShortMessage() > s2.getShortMessage());
0247         }
0248         return (s1.getPriority() > s2.getPriority());
0249     }
0250 
0251     static void setAttribute(QDomElement& iRoot, const QString& iPath, const QString& iValue)
0252     {
0253         int pos = iPath.indexOf('.');
0254         if (pos == -1) {
0255             iRoot.setAttribute(iPath, iValue);
0256         } else {
0257             QString newElementName = iPath.left(pos);
0258             QString newAttribue = iPath.right(iPath.count() - pos - 1);
0259 
0260             QDomDocument doc(QStringLiteral("SKGML"));
0261             doc.setContent(iRoot.attribute(newElementName));
0262             QDomElement root = doc.documentElement();
0263             if (root.isNull()) {
0264                 root = doc.createElement(QStringLiteral("parameters"));
0265                 doc.appendChild(root);
0266             }
0267             setAttribute(root, newAttribue, iValue);
0268 
0269             iRoot.setAttribute(newElementName, doc.toString());
0270         }
0271     }
0272 
0273     void refreshTabPosition()
0274     {
0275         m_tabWidget->setTabPosition(static_cast<QTabWidget::TabPosition>(skgbasegui_settings::main_tabs_position()));
0276     }
0277 
0278     void rebuildSystemTray()
0279     {
0280         if (skgbasegui_settings::icon_in_system_tray()) {
0281             if (m_kSystemTrayIcon == nullptr) {
0282                 m_kSystemTrayIcon = new KStatusNotifierItem(m_mainPanel);
0283                 m_kSystemTrayIcon->setStandardActionsEnabled(true);
0284                 m_kSystemTrayIcon->setAssociatedWidget(m_mainPanel);
0285                 m_kSystemTrayIcon->setIconByPixmap(QApplication::windowIcon());
0286             }
0287         } else {
0288             if (m_kSystemTrayIcon != nullptr) {
0289                 delete m_kSystemTrayIcon;
0290                 m_kSystemTrayIcon = nullptr;
0291             }
0292         }
0293     }
0294 };
0295 
0296 
0297 bool SKGMainPanelPrivate::m_currentActionCanceled = false;
0298 SKGMainPanel* SKGMainPanelPrivate::m_mainPanel = nullptr;
0299 
0300 SKGMainPanel::SKGMainPanel(QSplashScreen* iSplashScreen, SKGDocument* iDocument)
0301     :  d(new SKGMainPanelPrivate)
0302 {
0303     SKGTRACEINFUNC(1)
0304     d->m_tabWidget = new SKGTabWidget(this);
0305     d->m_splashScreen = iSplashScreen;
0306     d->m_currentDocument = iDocument;
0307 
0308     setComponentName(QStringLiteral("skg"), KAboutData::applicationData().displayName());
0309     setObjectName(qApp->applicationDisplayName());
0310 
0311     // Set main panel
0312     SKGMainPanelPrivate::m_mainPanel = this;
0313     auto w = new QScrollArea(this);
0314     w->setFrameShape(QFrame::NoFrame);
0315     w->setFocusPolicy(Qt::NoFocus);
0316     d->m_mainLayout = new QVBoxLayout(w);
0317     d->m_mainLayout->setSpacing(0);
0318     d->m_mainLayout->setContentsMargins(0, 0, 0, 0);
0319     d->m_mainLayout->addWidget(d->m_tabWidget);
0320     d->ui.setupUi(this);
0321 
0322     // setMainWidget(new QLabel("hello"));
0323 
0324     // Initialize settings
0325     KSharedConfigPtr config = KSharedConfig::openConfig();
0326     KConfigGroup prefskg = config->group("Main Panel");
0327     int option = prefskg.readEntry("update_modified_contexts", 100);
0328     if (option == 100) {
0329         // First call, we set default values
0330         prefskg.writeEntry("update_modified_contexts", 2, KConfigBase::Normal);
0331         // NEVER: set following setting
0332 #ifdef SKG_KF_5102
0333         KMessageBox::saveDontShowAgainTwoActions(QStringLiteral("updateContextOnClose"), KMessageBox::No);
0334 #else
0335         KMessageBox::saveDontShowAgainYesNo(QStringLiteral("updateContextOnClose"), KMessageBox::No);
0336 #endif
0337         SKGTRACEL(1) << "update_modified_contexts set to NEVER" << SKGENDL;
0338         SKGTRACEL(1) << "updateContextOnClose set to No" << SKGENDL;
0339     }
0340 
0341     option = prefskg.readEntry("update_modified_bookmarks", 100);
0342     if (option == 100) {
0343         // First call, we set default values
0344         prefskg.writeEntry("update_modified_bookmarks", 2, KConfigBase::Normal);
0345         // NEVER: set following setting
0346 #ifdef SKG_KF_5102
0347         KMessageBox::saveDontShowAgainTwoActions(QStringLiteral("updateBookmarkOnClose"), KMessageBox::No);
0348 #else
0349         KMessageBox::saveDontShowAgainYesNo(QStringLiteral("updateBookmarkOnClose"), KMessageBox::No);
0350 #endif
0351         SKGTRACEL(1) << "update_modified_bookmarks set to NEVER" << SKGENDL;
0352         SKGTRACEL(1) << "updateBookmarkOnClose set to No" << SKGENDL;
0353     }
0354 
0355 
0356     const auto plugins = KPluginMetaData::findPlugins(QStringLiteral("skg_gui"));
0357 
0358     // Load plugins
0359     int nb = plugins.count();
0360     SKGTRACEL(1) << nb << " plugins found" << SKGENDL;
0361     if (d->m_splashScreen != nullptr) {
0362         d->m_splashScreen->showMessage(i18nc("Splash screen message", "Loading plugins…"), Qt::AlignLeft, QColor(221, 130, 8));    // krazy:exclude=qmethods
0363     }
0364     SKGError err;
0365     QStringList listAuthors;
0366     QStringList listTasks;
0367     QStringList listEmails;
0368     QStringList listOscs;
0369     for (int i = 0; i < nb; ++i) {
0370         const auto plugin = plugins.at(i);
0371         const auto name = plugin.name();
0372         const auto filename = plugin.fileName();
0373         QString id = plugin.value(QStringLiteral("X-Krunner-ID"));
0374         QString msg = i18nc("Splash screen message", "Loading plugin %1/%2: %3…", i + 1, nb, name);
0375         SKGTRACEL(1) << msg << " from " << filename << SKGENDL;
0376         if (d->m_splashScreen != nullptr) {
0377             d->m_splashScreen->showMessage(msg, Qt::AlignLeft, QColor(221, 130, 8));    // krazy:exclude=qmethods
0378         }
0379 
0380         const auto factory = KPluginFactory::loadFactory(plugin);
0381         const auto pluginInterface = factory.plugin ? factory.plugin->create<SKGInterfacePlugin>(this) : nullptr;
0382         if (pluginInterface != nullptr) {
0383             if (pluginInterface->isEnabled() && pluginInterface->setupActions(getDocument())) {
0384                 // Add plugin in about
0385                 QStringList listOfAuthors;
0386                 QStringList listOfEmails;
0387                 QStringList listOfPlugins;
0388                 QString author = plugin.authors().at(0).name();
0389                 QString email = plugin.authors().at(0).emailAddress();
0390                 if (!author.isEmpty()) {
0391                     listOfAuthors.push_back(author);
0392                     listOfEmails.push_back(email);
0393                     listOfPlugins.push_back(name);
0394                 }
0395 
0396                 const auto subPlugins = pluginInterface->subPlugins();
0397                 auto subPluginsNotFound = subPlugins;
0398                 for (const QString& subPlugin : subPlugins) {
0399                     const auto subOffers = KPluginMetaData::findPlugins(subPlugin);
0400                     int nbSubOffers = subOffers.count();
0401                     for (int j = 0; j < nbSubOffers; ++j) {
0402                         const auto subService = subOffers.at(j);
0403                         subPluginsNotFound.removeAll(subPlugin);
0404                         QString author2 = subService.authors().at(0).name();
0405                         QString email2 = subService.authors().at(0).emailAddress();
0406                         if (!author2.isEmpty()) {
0407                             listOfAuthors.push_back(author2);
0408                             listOfEmails.push_back(email2);
0409                             listOfPlugins.push_back(subService.name());
0410                         }
0411                     }
0412                 }
0413 
0414                 for (const QString& subPlugin : subPluginsNotFound) {
0415                     KService::List subOffers = KServiceTypeTrader::self()->query(subPlugin);
0416                     int nbSubOffers = subOffers.count();
0417                     for (int j = 0; j < nbSubOffers; ++j) {
0418                         KService::Ptr subService = subOffers.at(j);
0419 #ifdef SKG_KF_5102
0420                         QString author2 = subService->property(QStringLiteral("X-KDE-PluginInfo-Author"), QMetaType::QString).toString();
0421                         QString email2 = subService->property(QStringLiteral("X-KDE-PluginInfo-Email"), QMetaType::QString).toString();
0422 #else
0423                         QString author2 = subService->property(QStringLiteral("X-KDE-PluginInfo-Author"), QVariant::String).toString();
0424                         QString email2 = subService->property(QStringLiteral("X-KDE-PluginInfo-Email"), QVariant::String).toString();
0425 #endif
0426                         if (!author2.isEmpty()) {
0427                             listOfAuthors.push_back(author2);
0428                             listOfEmails.push_back(email2);
0429                             listOfPlugins.push_back(subService->name());
0430                         }
0431                     }
0432                 }
0433 
0434                 int nbAuthors = listOfAuthors.count();
0435                 for (int j = 0; j < nbAuthors; ++j) {
0436                     QString authorId;
0437                     QString author = listOfAuthors.at(j);
0438                     QStringList authors = SKGServices::splitCSVLine(author, ',');
0439                     if (authors.count() == 2) {
0440                         author = authors.at(0);
0441                         authorId = authors.at(1);
0442                     }
0443                     int pos2 = listAuthors.indexOf(author);
0444                     if (pos2 == -1) {
0445                         listAuthors.push_back(author);
0446                         listTasks.push_back(i18n("Developer of plugin '%1'", listOfPlugins.at(j)));
0447                         listEmails.push_back(listOfEmails.at(j));
0448                         listOscs.push_back(authorId);
0449                     } else {
0450                         listTasks[pos2] += i18n(", '%1'", listOfPlugins.at(j));
0451                     }
0452                 }
0453 
0454                 // Store plugin
0455                 int nbplugin = d->m_pluginsList.count();
0456                 int pos3 = nbplugin;
0457                 for (int j = nbplugin - 1; j >= 0; --j) {
0458                     if (pluginInterface->getOrder() < d->m_pluginsList.at(j)->getOrder()) {
0459                         pos3 = j;
0460                     }
0461                 }
0462 
0463                 d->m_pluginsList.insert(pos3, pluginInterface);
0464                 pluginInterface->setObjectName(id);
0465 
0466                 // Add tips
0467                 d->m_tipsOfTheDay += pluginInterface->tips();
0468             } else {
0469                 SKGTRACEL(1) << "Disabled" << SKGENDL;
0470             }
0471         } else {
0472             SKGTRACEL(1) << "Loading error: " << factory.errorString << SKGENDL;
0473         }
0474     }
0475 
0476     // Add credits
0477     KAboutData about = KAboutData::applicationData();
0478     nb = listAuthors.count();
0479     for (int i = 0; i < nb; ++i) {
0480         about.addCredit(listAuthors.at(i), listTasks.at(i), listEmails.at(i), QLatin1String(""), listOscs.at(i));
0481     }
0482     KAboutData::setApplicationData(about);
0483 
0484     // accept dnd
0485     setAcceptDrops(true);
0486 
0487     // tell the KXmlGuiWindow that this is indeed the main widget
0488     setCentralWidget(w);
0489 
0490     d->refreshTabPosition();
0491     d->m_tabWidget->setMovable(true);
0492     d->m_tabWidget->setTabsClosable(true);
0493     d->m_tabWidget->setUsesScrollButtons(true);
0494     d->m_tabWidget->tabBar()->setSelectionBehaviorOnRemove(QTabBar::SelectPreviousTab);
0495     d->m_tabWidget->setDocumentMode(true);
0496     d->m_tabWidget->show();
0497 
0498     // System tray
0499     d->rebuildSystemTray();
0500 
0501     // then, setup our actions
0502     setupActions();
0503 
0504     displayErrorMessage(err);
0505 
0506     // Add a status bar
0507     statusBar()->show();
0508 
0509     QPalette palette2 = QApplication::palette();
0510     palette2.setColor(QPalette::Base, Qt::transparent);
0511     d->ui.kContextList->setPalette(palette2);
0512 
0513     QAction* toggle = d->ui.kDockContext->toggleViewAction();
0514     QAction* panelAction = actionCollection()->addAction(QStringLiteral("view_context"));
0515     registerGlobalAction(QStringLiteral("view_context"), panelAction);
0516     panelAction->setCheckable(true);
0517     panelAction->setChecked(toggle->isChecked());
0518     panelAction->setText(toggle->text());
0519     actionCollection()->setDefaultShortcut(panelAction, Qt::SHIFT + Qt::Key_F9);
0520     connect(panelAction, &QAction::triggered, toggle, &QAction::trigger);
0521     connect(toggle, &QAction::toggled, panelAction, &QAction::setChecked);
0522 
0523     QAction* toggle2 = d->ui.kDockMessages->toggleViewAction();
0524     QAction* panelAction2 = actionCollection()->addAction(QStringLiteral("view_messages"));
0525     registerGlobalAction(QStringLiteral("view_messages"), panelAction2);
0526     panelAction2->setCheckable(true);
0527     panelAction2->setChecked(toggle2->isChecked());
0528     panelAction2->setText(toggle2->text());
0529     actionCollection()->setDefaultShortcut(panelAction2, Qt::SHIFT + Qt::Key_F8);
0530     connect(panelAction2, &QAction::triggered, toggle2, &QAction::trigger);
0531     connect(toggle2, &QAction::toggled, panelAction2, &QAction::setChecked);
0532 
0533     auto contextMenu = new KSelectAction(SKGServices::fromTheme(QStringLiteral("tab-new")), i18nc("Noun", "Pages"), this);
0534     registerGlobalAction(QStringLiteral("view_contextmenu"), contextMenu);
0535 
0536     // Add plugin in client in right order
0537     QList<QListWidgetItem*> pageNotVisible;
0538     KConfigGroup prefContextVisibility = config->group("Context Visibility");
0539     int shortCutIndex = 0;
0540     QString shortCutPrefix = QStringLiteral("Ctrl+");
0541     int nbplugin = d->m_pluginsList.count();
0542     QList<QAction*> contextActionList;
0543     for (int j = 0; j < nbplugin; ++j) {
0544         SKGInterfacePlugin* pluginInterface = d->m_pluginsList.at(j);
0545         if (pluginInterface != nullptr) {
0546             // Creation of the item
0547             QString title = pluginInterface->title();
0548             SKGTRACEL(1) << "Add plugin (" << pluginInterface->getOrder() << ") : " << title << SKGENDL;
0549             if (!title.isEmpty()) {
0550                 // Add menu item with shortcuts
0551                 if (pluginInterface->isInPagesChooser()) {
0552                     bool visible = prefContextVisibility.readEntry(pluginInterface->objectName(), true);
0553                     QIcon icon = SKGServices::fromTheme(pluginInterface->icon());
0554 
0555                     auto contextItem = new QListWidgetItem(icon, title);
0556                     contextItem->setStatusTip(pluginInterface->statusTip());
0557                     contextItem->setToolTip(pluginInterface->toolTip());
0558                     contextItem->setData(12, j);     // context item ==> plugin
0559                     int page2 = d->ui.kContextList->count();
0560                     pluginInterface->setProperty("contextItem", page2);  // plugin ==> context item
0561 
0562                     d->ui.kContextList->addItem(contextItem);
0563 
0564                     QAction* newAction = contextMenu->addAction(icon, title);
0565                     if (newAction != nullptr) {
0566                         newAction->setCheckable(false);
0567                         newAction->setData(page2);
0568                         contextItem->setData(15, QVariant::fromValue(static_cast<void*>(newAction)));     // context item ==> action
0569                         if (!shortCutPrefix.isEmpty()) {
0570                             ++shortCutIndex;
0571                             if (shortCutIndex == 10) {
0572                                 shortCutIndex = 0;
0573                                 if (shortCutPrefix == QStringLiteral("Ctrl+")) {
0574                                     shortCutPrefix += QStringLiteral("Alt+");
0575                                 } else {
0576                                     shortCutPrefix = QLatin1String("");
0577                                 }
0578                             }
0579                             if (!shortCutPrefix.isEmpty()) {
0580                                 actionCollection()->setDefaultShortcut(newAction, QString(shortCutPrefix % SKGServices::intToString(shortCutIndex)));
0581                             }
0582                         }
0583                         connect(newAction, &QAction::triggered, this, &SKGMainPanel::onOpenContext);
0584 
0585                         contextActionList.append(newAction);
0586 
0587                         if (!visible) {
0588                             pageNotVisible.push_back(contextItem);
0589                         }
0590 
0591                         // Register action
0592                         QString id = "page_" % pluginInterface->objectName();
0593                         registerGlobalAction(id, newAction);
0594                         registerGlobalAction(id, newAction);
0595                     }
0596                 }
0597 
0598                 // Create dock if needed
0599                 QDockWidget* dockWidget = pluginInterface->getDockWidget();
0600                 if (dockWidget != nullptr) {
0601                     addDockWidget(Qt::LeftDockWidgetArea, dockWidget);
0602                     tabifyDockWidget(d->ui.kDockContext, dockWidget);
0603                     tabifyDockWidget(dockWidget, d->ui.kDockContext);
0604                 }
0605             }
0606         }
0607     }
0608 
0609     // Lock docs if needed
0610     KConfigGroup pref = getMainConfigGroup();
0611     if (pref.readEntry("docks_locked", false)) {
0612         onLockDocks();
0613     }
0614 
0615     if (!pref.readEntry("first_launch", false)) {
0616         this->setWindowState(windowState() | Qt::WindowMaximized);
0617         pref.writeEntry("first_launch", true);
0618     }
0619 
0620     // a call to KXmlGuiWindow::setupGUI() populates the GUI
0621     // with actions, using KXMLGUI.
0622     // It also applies the saved mainwindow settings, if any, and ask the
0623     // mainwindow to automatically save settings if changed: window size,
0624     // toolbar position, icon size, etc.
0625     setupGUI(Default, QStringLiteral("skgmainpanel.rc"));
0626     plugActionList(QStringLiteral("context_actionlist"), contextActionList);
0627 
0628     for (int j = 0; j < nbplugin; ++j) {
0629         SKGInterfacePlugin* pluginInterface = d->m_pluginsList.at(j);
0630         if (pluginInterface != nullptr) {
0631             QString title = pluginInterface->title();
0632             SKGTRACEL(1) << "Add plugin client (" << pluginInterface->getOrder() << ") : " << title << SKGENDL;
0633             guiFactory()->addClient(pluginInterface);
0634         }
0635     }
0636 
0637     // Hide items in context
0638     nb = pageNotVisible.count();
0639     for (int i = 0; i < nb; ++i) {
0640         setContextVisibility(pageNotVisible.at(i), false);
0641     }
0642 
0643     // Set status bar
0644     d->m_kNormalMessage = new QLabel(this);
0645     d->m_kNormalMessage->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred));
0646 
0647     auto kProgressBar = new QProgressBar(this);
0648     kProgressBar->setObjectName(QStringLiteral("kProgressBar"));
0649     kProgressBar->setValue(0);
0650     kProgressBar->setMinimumSize(100, 20);
0651     QSizePolicy sizePolicyProgessBar(QSizePolicy::Fixed, QSizePolicy::Expanding);
0652     kProgressBar->setSizePolicy(sizePolicyProgessBar);
0653     kProgressBar->setToolTip(i18nc("Widget description", "Progress of the current action"));
0654     QFont f = kProgressBar->font();
0655     f.setPointSize(6.0);
0656     kProgressBar->setFont(f);
0657     kProgressBar->setVisible(false);
0658     kProgressBar->setAlignment(Qt::AlignTop | Qt::AlignHCenter);
0659     kProgressBar->setRange(0, 100);
0660 
0661     d->m_zoomSelector = new SKGZoomSelector(this);
0662     d->m_zoomSelector->setSizePolicy(sizePolicyProgessBar);
0663     connect(d->m_zoomSelector, &SKGZoomSelector::changed, this, &SKGMainPanel::onZoomChanged);
0664 
0665     statusBar()->addWidget(d->m_kNormalMessage, 1);
0666     statusBar()->addPermanentWidget(kProgressBar);
0667 
0668     // Set Cancel button
0669     auto kCancelButton = new QPushButton();
0670     kCancelButton->setObjectName(QStringLiteral("kCancelButton"));
0671     kCancelButton->setIcon(SKGServices::fromTheme(QStringLiteral("media-playback-stop")));
0672     kCancelButton->setToolTip(i18nc("Widget description", "Cancel the current action"));
0673     kCancelButton->setStatusTip(i18nc("Widget description", "Cancel the current action"));
0674     kCancelButton->setVisible(false);
0675     connect(kCancelButton, &QPushButton::clicked, this, &SKGMainPanel::onCancelCurrentAction);
0676 
0677     //
0678     d->ui.kClearMessageBtn->setIcon(SKGServices::fromTheme(QStringLiteral("edit-clear")));
0679     connect(d->ui.kClearMessageBtn, &QPushButton::clicked, this, &SKGMainPanel::onClearMessages);
0680 
0681     // Add special button in toolbar
0682     KToolBar* tb = toolBar();
0683     if (tb != nullptr) {
0684         auto label = new QLabel(this);
0685         QSizePolicy sizePolicyLabel(QSizePolicy::Expanding, QSizePolicy::Preferred);
0686         sizePolicyLabel.setHorizontalStretch(0);
0687         sizePolicyLabel.setVerticalStretch(0);
0688         sizePolicyLabel.setHeightForWidth(label->sizePolicy().hasHeightForWidth());
0689         label->setSizePolicy(sizePolicyLabel);
0690         tb->addWidget(label);
0691         tb->addAction(d->m_buttonMenuAction);
0692     }
0693 
0694     statusBar()->addPermanentWidget(kCancelButton);
0695     statusBar()->addPermanentWidget(d->m_zoomSelector);
0696 
0697     // Set progress bar call back
0698     if (getDocument() != nullptr) {
0699         d->m_progressObjects.p1 = static_cast<void*>(kProgressBar);
0700         d->m_progressObjects.p2 = static_cast<void*>(kCancelButton);
0701         getDocument()->setProgressCallback(&SKGMainPanelPrivate::progressBarCallBack, static_cast<void*>(&d->m_progressObjects));
0702     }
0703 
0704     // Connection
0705     if (getDocument() != nullptr) {
0706         connect(getDocument(), &SKGDocument::transactionSuccessfullyEnded, this, &SKGMainPanel::refresh, Qt::QueuedConnection);
0707         connect(getDocument(), &SKGDocument::transactionSuccessfullyEnded, this, &SKGMainPanel::notify, Qt::QueuedConnection);
0708     }
0709     connect(d->ui.kContextList, &QListWidget::itemPressed, this, &SKGMainPanel::onBeforeOpenContext);
0710     connect(d->ui.kContextList, &QListWidget::itemPressed, this, &SKGMainPanel::onOpenContext);
0711     connect(d->m_tabWidget->tabBar(), &QTabBar::tabCloseRequested, this, &SKGMainPanel::closePageByIndex);
0712     connect(d->m_tabWidget->tabBar(), &QTabBar::tabBarClicked, this, [ = ](int index) {
0713         if ((QApplication::mouseButtons() & Qt::MidButton) != 0u) {
0714             closePageByIndex(index);
0715         }
0716     });
0717     connect(d->m_tabWidget->tabBar(), &QTabBar::tabBarDoubleClicked, this, &SKGMainPanel::addTab);
0718     connect(d->m_tabWidget->tabBar(), &QTabBar::currentChanged, this, &SKGMainPanel::currentPageChanged);
0719     connect(this, &SKGMainPanel::currentPageChanged, this, &SKGMainPanel::refresh);
0720     connect(this, &SKGMainPanel::currentPageChanged, this, &SKGMainPanel::selectionChanged);
0721 
0722     // Refresh
0723     refresh();
0724 
0725     d->ui.kContextList->installEventFilter(this);
0726 
0727     // Build contextual menu
0728     d->ui.kContextList->setContextMenuPolicy(Qt::CustomContextMenu);
0729 
0730     d->m_contextMenu = new QMenu(d->ui.kContextList);
0731     d->m_actHideContextItem = d->m_contextMenu->addAction(SKGServices::fromTheme(QStringLiteral("layer-visible-off")), i18nc("Verb", "Hide"));
0732     connect(d->m_actHideContextItem, &QAction::triggered, this, &SKGMainPanel::onHideContextItem);
0733 
0734     d->m_actShowAllContextItems = d->m_contextMenu->addAction(SKGServices::fromTheme(QStringLiteral("layer-visible-on")), i18nc("Verb", "Show all"));
0735     connect(d->m_actShowAllContextItems, &QAction::triggered, this, &SKGMainPanel::onShowAllContextItems);
0736 
0737     d->m_contextMenu->addSeparator();
0738     d->m_contextMenu->addAction(getGlobalAction(QStringLiteral("tab_savedefaultstate")));
0739     d->m_contextMenu->addAction(getGlobalAction(QStringLiteral("tab_resetdefaultstate")));
0740 
0741     connect(d->ui.kContextList, &QListWidget::customContextMenuRequested, this, &SKGMainPanel::showMenu);
0742 
0743 #ifdef KActivities_FOUND
0744     // Initialize kactivities resource instance
0745     d->m_activityResourceInstance = new KActivities::ResourceInstance(window()->winId());
0746     d->m_activityResourceInstance->setParent(this);
0747 #endif
0748 
0749     // Show menu bar and status bar
0750     menuBar()->show();
0751     statusBar()->show();
0752 
0753     notify();  // Due to sendMessage not in a transaction
0754     d->m_splashScreen = nullptr;
0755 }
0756 
0757 SKGMainPanel::~SKGMainPanel()
0758 {
0759     SKGTRACEINFUNC(1)
0760     SKGMainPanelPrivate::m_mainPanel = nullptr;
0761     disconnect(getDocument(), nullptr, this, nullptr);
0762 
0763     // close plugins
0764     int nb = d->m_pluginsList.count();
0765     for (int i = 0; i < nb; ++i) {
0766         auto plugin = getPluginByIndex(i);
0767         plugin->close();
0768     }
0769 
0770     if (getDocument() != nullptr) {
0771         getDocument()->close();
0772     }
0773     delete d;
0774 }
0775 
0776 void SKGMainPanel::showMenu(const QPoint iPos)
0777 {
0778     if (d->m_contextMenu != nullptr) {
0779         d->m_contextMenu->popup(d->ui.kContextList->mapToGlobal(iPos));
0780     }
0781 }
0782 
0783 void SKGMainPanel::setContextVisibility(QListWidgetItem* iItem, bool iVisibility)
0784 {
0785     if (iItem != nullptr) {
0786         // Hide item in context
0787         iItem->setHidden(!iVisibility);
0788 
0789         // Hide corresponding action
0790         QAction* act = static_cast< QAction* >(iItem->data(15).value<void*>());
0791         if (act != nullptr) {
0792             act->setVisible(iVisibility);
0793         }
0794 
0795         // Save state in settings
0796         SKGInterfacePlugin* plugin = getPluginByIndex(iItem->data(12).toInt());
0797         if (plugin != nullptr) {
0798             KSharedConfigPtr config = KSharedConfig::openConfig();
0799             KConfigGroup pref = config->group("Context Visibility");
0800             pref.writeEntry(plugin->objectName(), iVisibility);
0801         }
0802     }
0803 }
0804 
0805 void SKGMainPanel::setContextVisibility(int iPage, bool iVisibility)
0806 {
0807     setContextVisibility(d->ui.kContextList->item(iPage), iVisibility);
0808 }
0809 
0810 void SKGMainPanel::onHideContextItem()
0811 {
0812     setContextVisibility(d->ui.kContextList->currentRow(), false);
0813 }
0814 
0815 void SKGMainPanel::onShowAllContextItems()
0816 {
0817     int nb = d->ui.kContextList->count();
0818     for (int i = 0; i < nb; ++i) {
0819         setContextVisibility(i, true);
0820     }
0821 }
0822 
0823 SKGDocument* SKGMainPanel::getDocument() const
0824 {
0825     return d->m_currentDocument;
0826 }
0827 
0828 QStringList SKGMainPanel::processArguments(const QStringList& iArgument)
0829 {
0830     QStringList output = iArgument;
0831     for (auto plugin : qAsConst(d->m_pluginsList)) {
0832         if (plugin != nullptr) {
0833             output = plugin->processArguments(output);
0834         }
0835     }
0836     return output;
0837 }
0838 
0839 void SKGMainPanel::registerGlobalAction(const QString& iIdentifier, QAction* iAction, bool iAddInCollection,
0840                                         const QStringList& iListOfTable, int iMinSelection, int iMaxSelection,
0841                                         int iRanking, bool iSelectionMustHaveFocus)
0842 {
0843     if (iAction == nullptr) {
0844         SKGTRACE << "WARNING: registerGlobalAction(" << iIdentifier << ",nullptr)" << SKGENDL;
0845     } else {
0846         if (d->m_registeredGlogalAction.contains(iIdentifier) &&
0847             d->m_registeredGlogalAction.value(iIdentifier).action != iAction) {
0848             SKGTRACE << "WARNING: Another action already registered for identifier " << iIdentifier << SKGENDL;
0849         }
0850         QStringList keys = d->m_registeredGlogalAction.keys();
0851         for (const auto& id : qAsConst(keys)) {
0852             QPointer<QAction> act = d->m_registeredGlogalAction.value(id).action;
0853             if ((act != nullptr) && iIdentifier != id && act != iAction && !act->shortcut().isEmpty() && act->shortcut() == iAction->shortcut()) {
0854                 SKGTRACE << "WARNING: The actions [" << iAction->text() << " (" << iIdentifier << ")] and [" << act->text() << " (" << id << ")] has same shortcut [" << iAction->shortcut().toString() << "]" << SKGENDL;
0855             }
0856         }
0857         actionDetails actDetails;
0858         actDetails.action = iAction;
0859         actDetails.tables = iListOfTable;
0860         actDetails.min = iMinSelection;
0861         actDetails.max = iMaxSelection;
0862         actDetails.focus = iSelectionMustHaveFocus;
0863         actDetails.ranking = (iRanking == -1 ? 10 * (d->m_registeredGlogalAction.count() + 1) : iRanking);
0864         d->m_registeredGlogalAction[iIdentifier] = actDetails;
0865 
0866         // This connect has not been migrated on new connect mechanism to avoid crash when leaving the application
0867         connect(iAction, SIGNAL(destroyed(QObject*)), this, SLOT(unRegisterGlobalAction(QObject*)));  // clazy:exclude=old-style-connect
0868         if (iAddInCollection) {
0869             QKeySequence shortCut = iAction->shortcut();
0870             if (!shortCut.isEmpty()) {
0871                 iAction->setShortcut(QKeySequence());
0872             }
0873             actionCollection()->addAction(iIdentifier, iAction);
0874             if (!shortCut.isEmpty()) {
0875                 actionCollection()->setDefaultShortcut(iAction, shortCut);
0876             }
0877         }
0878     }
0879 }
0880 
0881 void SKGMainPanel::unRegisterGlobalAction(QObject* iAction)
0882 {
0883     auto* act = qobject_cast< QAction* >(iAction);
0884     if (act != nullptr) {
0885         const auto keys = d->m_registeredGlogalAction.keys();
0886         for (const auto& id : keys) {
0887             if (d->m_registeredGlogalAction.value(id).action == QPointer<QAction>(act)) {
0888                 d->m_registeredGlogalAction.remove(id);
0889             }
0890         }
0891     }
0892 }
0893 
0894 QPointer<QAction> SKGMainPanel::getGlobalAction(const QString& iIdentifier, bool iWarnIfNotExist)
0895 {
0896     QAction* act = d->m_registeredGlogalAction.value(iIdentifier).action;
0897     if (act == nullptr && iWarnIfNotExist) {
0898         SKGTRACE << "WARNING: getGlobalAction(" << iIdentifier << ")=nullptr" << SKGENDL;
0899     }
0900     return act;
0901 }
0902 
0903 QList< QPointer< QAction > > SKGMainPanel::getActionsForContextualMenu(const QString& iTable)
0904 {
0905     // Filter action
0906     QVector<actionDetails> tmp;
0907     for (const auto& actDetails : qAsConst(d->m_registeredGlogalAction)) {
0908         if (actDetails.ranking > 0 && actDetails.min > 0) {
0909             if (actDetails.tables.isEmpty() || actDetails.tables.contains(iTable)) {
0910                 tmp.push_back(actDetails);
0911             } else if (actDetails.tables.count() == 1 && actDetails.tables.at(0).startsWith(QLatin1String("query:"))) {
0912                 // Dynamic mode
0913                 QStringList tmpListTable;
0914                 getDocument()->getDistinctValues(QStringLiteral("sqlite_master"), QStringLiteral("name"), actDetails.tables.at(0).right(actDetails.tables.at(0).count() - 6), tmpListTable);
0915                 if (tmpListTable.contains(iTable)) {
0916                     tmp.push_back(actDetails);
0917                 }
0918             }
0919         }
0920     }
0921 
0922     // Sort
0923     std::sort(tmp.begin(), tmp.end(), [&](const actionDetails & a, const actionDetails & b) {
0924         return a.ranking < b.ranking;
0925     });
0926 
0927     // Generate output
0928     int previousGroup = -1;
0929     QList< QPointer< QAction > > output;
0930     output.reserve(tmp.count());
0931     for (const auto& actDetails : qAsConst(tmp)) {
0932         int currentGroup = (actDetails.ranking) / 100;
0933         if (currentGroup != previousGroup) {
0934             output.push_back(nullptr);
0935             previousGroup = currentGroup;
0936         }
0937         output.push_back(actDetails.action);
0938     }
0939     return output;
0940 }
0941 
0942 QMap<QString, QPointer<QAction> > SKGMainPanel::getGlobalActions() const
0943 {
0944     QMap<QString, QPointer<QAction> > map;
0945     const auto keys = d->m_registeredGlogalAction.keys();
0946     for (const auto& id : keys) {
0947         map[id] = d->m_registeredGlogalAction[id].action;
0948     }
0949     return map;
0950 }
0951 
0952 SKGInterfacePlugin* SKGMainPanel::getPluginByIndex(int iIndex)
0953 {
0954     SKGInterfacePlugin* output = nullptr;
0955     if (iIndex >= 0 && iIndex < d->m_pluginsList.count()) {
0956         output = d->m_pluginsList.value(iIndex);
0957     }
0958 
0959     return output;
0960 }
0961 
0962 SKGInterfacePlugin* SKGMainPanel::getPluginByName(const QString& iName)
0963 {
0964     SKGInterfacePlugin* output = nullptr;
0965     int nbplugin = d->m_pluginsList.count();
0966     QString name = iName.toLower();
0967     for (int j = 0; (output == nullptr) && j < nbplugin; ++j) {
0968         QString namep = d->m_pluginsList.at(j)->objectName().toLower();
0969         if (namep == name || namep.replace(' ', '_') == name) {
0970             output = d->m_pluginsList.at(j);
0971         }
0972     }
0973 
0974     return output;
0975 }
0976 
0977 void SKGMainPanel::setupActions()
0978 {
0979     SKGTRACEINFUNC(1)
0980 
0981     // Std File
0982     KStandardAction::quit(this, SLOT(onQuitAction()), actionCollection());
0983     KStandardAction::configureNotifications(this, SLOT(onConfigureNotifications()), actionCollection());
0984 
0985     // Preferences
0986     KStandardAction::preferences(this, SLOT(optionsPreferences()), actionCollection());
0987 
0988     // New Tab
0989     auto actAddTab = new QAction(SKGServices::fromTheme(QStringLiteral("tab-new-background")), i18nc("Noun, user action", "New Tab"), this);
0990     actionCollection()->setDefaultShortcut(actAddTab, Qt::CTRL + Qt::SHIFT + Qt::Key_W);
0991     connect(actAddTab, &QAction::triggered, this, &SKGMainPanel::addTab);
0992     registerGlobalAction(QStringLiteral("new_tab"), actAddTab, true, QStringList(), -1);
0993 
0994     // Add new tab widget
0995     auto addTabButton = new QToolButton(this);
0996     addTabButton->setIcon(actAddTab->icon());
0997     addTabButton->setAutoRaise(true);
0998     addTabButton->raise();
0999     addTabButton->setDefaultAction(actAddTab);
1000     addTabButton->setToolButtonStyle(Qt::ToolButtonIconOnly);
1001     addTabButton->setFocusPolicy(Qt::NoFocus);
1002     d->m_tabWidget->setCornerWidget(addTabButton);
1003 
1004     d->m_actLock = new QAction(SKGServices::fromTheme(QStringLiteral("document-encrypt")), i18nc("Verb", "Lock panels"), this);
1005     connect(d->m_actLock, &QAction::triggered, this, &SKGMainPanel::onLockDocks);
1006     registerGlobalAction(QStringLiteral("view_lock"), d->m_actLock);
1007 
1008     d->m_actUnLock = new QAction(SKGServices::fromTheme(QStringLiteral("document-decrypt")), i18nc("Verb", "Unlock panels"), this);
1009     connect(d->m_actUnLock, &QAction::triggered, this, &SKGMainPanel::onUnlockDocks);
1010     registerGlobalAction(QStringLiteral("view_unlock"), d->m_actUnLock);
1011 
1012     d->m_switchPinState = new QAction(SKGServices::fromTheme(QStringLiteral("document-encrypt")), i18nc("Noun, user action", "Pin this page"), this);
1013     connect(d->m_switchPinState, &QAction::triggered, this, [ = ] {this->switchPinPage(nullptr);});
1014     registerGlobalAction(QStringLiteral("tab_switchpin"), d->m_switchPinState);
1015 
1016     //
1017     d->m_closePageAction = actionCollection()->addAction(KStandardAction::Close, QStringLiteral("tab_close"), this, SLOT(closeCurrentPage()));
1018     registerGlobalAction(QStringLiteral("tab_close"), d->m_closePageAction);
1019 
1020     //
1021     auto actCloseAllPages = new QAction(SKGServices::fromTheme(QStringLiteral("window-close")), i18nc("Noun, user action", "Close All"), this);
1022     actionCollection()->setDefaultShortcut(actCloseAllPages, Qt::ALT + Qt::Key_W);
1023     connect(actCloseAllPages, &QAction::triggered, this, &SKGMainPanel::closeAllPages);
1024     registerGlobalAction(QStringLiteral("tab_closeall"), actCloseAllPages, true, QStringList(), -1);
1025 
1026     //
1027     d->m_closeAllOtherPagesAction = new QAction(SKGServices::fromTheme(QStringLiteral("window-close")), i18nc("Noun, user action", "Close All Other"), this);
1028     actionCollection()->setDefaultShortcut(d->m_closeAllOtherPagesAction, Qt::CTRL + Qt::ALT + Qt::Key_W);
1029     connect(d->m_closeAllOtherPagesAction, &QAction::triggered, this, [ = ] {this->closeAllOtherPages(nullptr);});
1030     registerGlobalAction(QStringLiteral("tab_closeallother"), d->m_closeAllOtherPagesAction);
1031 
1032     //
1033     d->m_saveDefaultStateAction = new QAction(SKGServices::fromTheme(QStringLiteral("document-save")), i18nc("Noun, user action", "Save page state"), this);
1034     actionCollection()->setDefaultShortcut(d->m_saveDefaultStateAction, Qt::CTRL + Qt::ALT + Qt::Key_S);
1035     connect(d->m_saveDefaultStateAction, &QAction::triggered, this, &SKGMainPanel::saveDefaultState);
1036     registerGlobalAction(QStringLiteral("tab_savedefaultstate"), d->m_saveDefaultStateAction);
1037 
1038     //
1039     d->m_resetDefaultStateAction = new QAction(SKGServices::fromTheme(QStringLiteral("edit-clear")), i18nc("Noun, user action", "Reset page state"), this);
1040     actionCollection()->setDefaultShortcut(d->m_resetDefaultStateAction, Qt::CTRL + Qt::ALT + Qt::Key_R);
1041     connect(d->m_resetDefaultStateAction, &QAction::triggered, this, &SKGMainPanel::resetDefaultState);
1042     registerGlobalAction(QStringLiteral("tab_resetdefaultstate"), d->m_resetDefaultStateAction);
1043 
1044     //
1045     d->m_reopenLastClosed = new QAction(SKGServices::fromTheme(QStringLiteral("tab-new")), i18nc("Noun, user action", "Reopen last page closed"), this);
1046     actionCollection()->setDefaultShortcut(d->m_reopenLastClosed, Qt::CTRL + Qt::ALT + Qt::Key_T);
1047     connect(d->m_reopenLastClosed, &QAction::triggered, this, &SKGMainPanel::onReopenLastClosed);
1048     registerGlobalAction(QStringLiteral("tab_reopenlastclosed"), d->m_reopenLastClosed);
1049 
1050     //
1051     QStringList overlay;
1052     overlay.push_back(QStringLiteral("bookmarks"));
1053     d->m_overwriteBookmarkStateAction = new QAction(SKGServices::fromTheme(QStringLiteral("document-save"), overlay), i18nc("Noun, user action", "Overwrite bookmark state"), this);
1054     connect(d->m_overwriteBookmarkStateAction, &QAction::triggered, this, &SKGMainPanel::overwriteBookmarkState);
1055     actionCollection()->setDefaultShortcut(d->m_overwriteBookmarkStateAction, Qt::CTRL + Qt::ALT + Qt::Key_B);
1056     registerGlobalAction(QStringLiteral("tab_overwritebookmark"), d->m_overwriteBookmarkStateAction);
1057 
1058     //
1059     d->m_configureAction = new QAction(SKGServices::fromTheme(QStringLiteral("configure")), i18nc("Noun, user action", "Configure…"), this);
1060     connect(d->m_configureAction, &QAction::triggered, this, [ = ] {this->optionsPreferences();});
1061     registerGlobalAction(QStringLiteral("tab_configure"), d->m_configureAction);
1062 
1063     // Menu
1064     d->m_buttonMenuAction = new KToolBarPopupAction(SKGServices::fromTheme(QStringLiteral("configure")), QLatin1String(""), this);
1065     d->m_buttonMenuAction->setToolTip(i18nc("Noun, user action", "Menu"));
1066     d->m_buttonMenu = d->m_buttonMenuAction->menu();
1067     connect(d->m_buttonMenu, &QMenu::aboutToShow, this, &SKGMainPanel::onShowButtonMenu);
1068     d->m_buttonMenuAction->setDelayed(false);
1069     registerGlobalAction(QStringLiteral("view_menu"), d->m_buttonMenuAction);
1070 
1071     d->m_showMenuBarAction = KStandardAction::showMenubar(this, SLOT(onShowMenuBar()), actionCollection());
1072     KConfigGroup pref = getMainConfigGroup();
1073     d->m_showMenuBarAction->setChecked(pref.readEntry("menubar_shown", true));
1074     QTimer::singleShot(200, Qt::CoarseTimer, this, &SKGMainPanel::onShowMenuBar);
1075     registerGlobalAction(QStringLiteral("options_show_menubar"), d->m_showMenuBarAction);
1076 
1077     //
1078     d->m_previousAction = new KToolBarPopupAction(SKGServices::fromTheme(QStringLiteral("go-previous")), i18nc("Noun, user action", "Previous"), this);
1079     connect(d->m_previousAction, &QAction::triggered, this, &SKGMainPanel::onPrevious);
1080     actionCollection()->setDefaultShortcut(d->m_previousAction, Qt::ALT + Qt::Key_Left);
1081     d->m_previousAction->setPriority(QAction::LowPriority);
1082     d->m_previousMenu = d->m_previousAction->menu();
1083     connect(d->m_previousMenu, &QMenu::aboutToShow, this, &SKGMainPanel::onShowPreviousMenu);
1084     d->m_previousAction->setStickyMenu(false);
1085     d->m_previousAction->setData(0);
1086     registerGlobalAction(QStringLiteral("go_previous"), d->m_previousAction);
1087 
1088     //
1089     d->m_nextAction = new KToolBarPopupAction(SKGServices::fromTheme(QStringLiteral("go-next")), i18nc("Noun, user action", "Next"), this);
1090     connect(d->m_nextAction, &QAction::triggered, this, &SKGMainPanel::onNext);
1091     actionCollection()->setDefaultShortcut(d->m_nextAction, Qt::ALT + Qt::Key_Right);
1092     d->m_nextAction->setPriority(QAction::LowPriority);
1093     d->m_nextMenu = d->m_nextAction->menu();
1094     connect(d->m_nextMenu, &QMenu::aboutToShow, this, &SKGMainPanel::onShowNextMenu);
1095     d->m_nextAction->setStickyMenu(false);
1096     d->m_nextAction->setData(0);
1097     registerGlobalAction(QStringLiteral("go_next"), d->m_nextAction);
1098 
1099     //
1100     d->m_fullScreenAction = actionCollection()->addAction(KStandardAction::FullScreen, QStringLiteral("fullscreen"), this, SLOT(onFullScreen()));
1101     registerGlobalAction(QStringLiteral("fullscreen"), d->m_fullScreenAction);
1102 
1103     //
1104     d->m_enableEditorAction = new QAction(SKGServices::fromTheme(QStringLiteral("appointment-new")), i18nc("Noun, user action", "Enable editor"), this);
1105     actionCollection()->setDefaultShortcut(d->m_enableEditorAction, Qt::CTRL + Qt::Key_Insert);
1106     connect(d->m_enableEditorAction, &QAction::triggered, this, &SKGMainPanel::enableEditor);
1107     registerGlobalAction(QStringLiteral("enable_editor"), d->m_enableEditorAction);
1108 
1109     //
1110     auto migrateSQLCipher = new QAction(SKGServices::fromTheme(QStringLiteral("run-build")), i18nc("Noun, user action", "Migrate to SQLCipher format"), this);
1111     connect(migrateSQLCipher, &QAction::triggered, this, &SKGMainPanel::onMigrateToSQLCipher);
1112     registerGlobalAction(QStringLiteral("migrate_sqlcipher"), migrateSQLCipher);
1113 
1114     // Contextual menu
1115     d->m_tabWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
1116     d->m_tabWidget->insertAction(nullptr, getGlobalAction(QStringLiteral("new_tab")));
1117     d->m_tabWidget->insertAction(nullptr, getGlobalAction(QStringLiteral("tab_reopenlastclosed")));
1118     d->m_tabWidget->insertAction(nullptr, getGlobalAction(QStringLiteral("tab_close")));
1119     d->m_tabWidget->insertAction(nullptr, getGlobalAction(QStringLiteral("tab_closeall")));
1120     d->m_tabWidget->insertAction(nullptr, getGlobalAction(QStringLiteral("tab_closeallother")));
1121     {
1122         auto sep = new QAction(this);
1123         sep->setSeparator(true);
1124         d->m_tabWidget->insertAction(nullptr, sep);
1125     }
1126     d->m_tabWidget->insertAction(nullptr, getGlobalAction(QStringLiteral("tab_switchpin")));
1127     d->m_tabWidget->insertAction(nullptr, getGlobalAction(QStringLiteral("tab_resetdefaultstate")));
1128     d->m_tabWidget->insertAction(nullptr, getGlobalAction(QStringLiteral("tab_savedefaultstate")));
1129     d->m_tabWidget->insertAction(nullptr, getGlobalAction(QStringLiteral("tab_overwritebookmark")));
1130     d->m_tabWidget->insertAction(nullptr, getGlobalAction(QStringLiteral("tab_configure")));
1131     {
1132         auto sep = new QAction(this);
1133         sep->setSeparator(true);
1134         d->m_tabWidget->insertAction(nullptr, sep);
1135     }
1136     d->m_tabWidget->insertAction(nullptr, getGlobalAction(QStringLiteral("fullscreen")));
1137 }
1138 
1139 void SKGMainPanel::enableEditor()
1140 {
1141     SKGTabPage* cPage = currentPage();
1142     if (cPage != nullptr) {
1143         cPage->activateEditor();
1144     }
1145 }
1146 
1147 void SKGMainPanel::onPrevious()
1148 {
1149     SKGError err;
1150     SKGTRACEINFUNCRC(10, err)
1151 
1152     SKGTabPage* cPage = currentPage();
1153     if (cPage != nullptr) {
1154         // Get index in history of page to refresh
1155         int indexPrevious = qobject_cast<QAction*>(sender())->data().toInt();
1156 
1157         // Get lists
1158         SKGTabPage::SKGPageHistoryItemList listPrevious = cPage->getPreviousPages();
1159         if (indexPrevious < listPrevious.count()) {
1160             SKGTabPage::SKGPageHistoryItemList listNext = cPage->getNextPages();
1161             SKGTabPage::SKGPageHistoryItem current = currentPageHistoryItem();
1162 
1163             // Get item to refresh
1164             SKGTabPage::SKGPageHistoryItem item = listPrevious.at(indexPrevious);
1165 
1166             // Open page
1167             cPage = openPage(getPluginByName(item.plugin), currentPageIndex(), item.state, item.name, item.bookmarkID);
1168             if (cPage != nullptr) {
1169                 cPage->setBookmarkID(item.bookmarkID);
1170 
1171                 // Update lists
1172                 listNext.insert(0, current);
1173                 listPrevious.removeAt(indexPrevious);
1174                 for (int i = 0; i < indexPrevious; ++i) {
1175                     SKGTabPage::SKGPageHistoryItem itemPrevious = listPrevious.at(0);  // Because the list is modified
1176                     listNext.insert(0, itemPrevious);
1177                     listPrevious.removeAt(0);
1178                 }
1179 
1180                 // Set lists
1181                 cPage->setPreviousPages(listPrevious);
1182                 cPage->setNextPages(listNext);
1183             }
1184 
1185             refresh();
1186         }
1187     }
1188 }
1189 
1190 void SKGMainPanel::onShowPreviousMenu()
1191 {
1192     if (d->m_previousMenu != nullptr) {
1193         d->m_previousMenu->clear();
1194 
1195         SKGTabPage* cPage = currentPage();
1196         if (cPage != nullptr) {
1197             SKGTabPage::SKGPageHistoryItemList list = cPage->getPreviousPages();
1198             int nb = list.count();
1199             for (int i = 0; i < nb; ++i) {
1200                 QAction* act = d->m_previousMenu->addAction(SKGServices::fromTheme(list.at(i).icon), list.at(i).name);
1201                 if (act != nullptr) {
1202                     act->setData(i);
1203                     connect(act, &QAction::triggered, this, &SKGMainPanel::onPrevious);
1204                 }
1205             }
1206         }
1207     }
1208 }
1209 
1210 void SKGMainPanel::onShowMenuBar()
1211 {
1212     bool test = d->m_showMenuBarAction->isChecked();
1213     menuBar()->setVisible(test);
1214     d->m_buttonMenuAction->setVisible(!test);
1215 
1216     KConfigGroup pref = getMainConfigGroup();
1217     pref.writeEntry("menubar_shown", test);
1218 }
1219 
1220 void SKGMainPanel::onFullScreen()
1221 {
1222     auto* p = d->m_tabWidget;
1223     if (p != nullptr) {
1224         if (!d->m_fullScreenAction->isChecked()) {
1225             // No Full screen
1226             p->setWindowState(p->windowState() & ~Qt::WindowFullScreen);   // reset
1227             d->m_mainLayout->addWidget(d->m_tabWidget);
1228         } else {
1229             bool atLeastOnePageOpened = (d->m_tabWidget->count() > 0);
1230             if (atLeastOnePageOpened) {
1231                 // Activate Full screen mode
1232                 p->setParent(nullptr);
1233                 p->setWindowFlags(p->windowFlags() | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint);
1234                 p->setWindowState(p->windowState() | Qt::WindowFullScreen);   // set
1235                 p->show();
1236 
1237                 displayMessage(i18nc("Information message", "You can exit full screen mode with %1 or with the contextual menu", d->m_fullScreenAction->shortcut().toString()));
1238             } else {
1239                 d->m_fullScreenAction->setChecked(false);
1240                 displayMessage(i18nc("Information message", "At least one page must be opened to enable full screen mode"), SKGDocument::Error);
1241             }
1242         }
1243     }
1244 }
1245 
1246 void SKGMainPanel::onNext()
1247 {
1248     SKGError err;
1249     SKGTRACEINFUNCRC(10, err)
1250 
1251     SKGTabPage* cPage = currentPage();
1252     if (cPage != nullptr) {
1253         // Get index in history of page to refresh
1254         int posNext = qobject_cast<QAction*>(sender())->data().toInt();
1255 
1256         // Get lists
1257         SKGTabPage::SKGPageHistoryItemList listPrevious = cPage->getPreviousPages();
1258         SKGTabPage::SKGPageHistoryItemList listNext = cPage->getNextPages();
1259         SKGTabPage::SKGPageHistoryItem current = currentPageHistoryItem();
1260 
1261         // Get item to refresh
1262         SKGTabPage::SKGPageHistoryItem item = listNext.at(posNext);
1263 
1264         // Open page
1265         cPage = openPage(getPluginByName(item.plugin), currentPageIndex(), item.state, item.name, item.bookmarkID);
1266         if (cPage != nullptr) {
1267             cPage->setBookmarkID(item.bookmarkID);
1268 
1269             // Update lists
1270             listPrevious.insert(0, current);
1271             listNext.removeAt(posNext);
1272             for (int i = 0; i < posNext; ++i) {
1273                 SKGTabPage::SKGPageHistoryItem itemNext = listNext.at(0);  // Because the list is modified
1274                 listPrevious.insert(0, itemNext);
1275                 listNext.removeAt(0);
1276             }
1277 
1278             // Set lists
1279             cPage->setPreviousPages(listPrevious);
1280             cPage->setNextPages(listNext);
1281         }
1282 
1283         refresh();
1284     }
1285 }
1286 
1287 void SKGMainPanel::onReopenLastClosed()
1288 {
1289     SKGError err;
1290     SKGTRACEINFUNCRC(10, err)
1291 
1292     SKGTabPage::SKGPageHistoryItem current = currentPageHistoryItem();
1293 
1294     // Get item to refresh
1295     historyPage item = d->m_historyClosedPages.takeLast();
1296 
1297     // Open page
1298     SKGTabPage* cPage = openPage(getPluginByName(item.current.plugin), -1, item.current.state, item.current.name, item.current.bookmarkID);
1299     if (cPage != nullptr) {
1300         cPage->setBookmarkID(item.current.bookmarkID);
1301         cPage->setNextPages(item.next);
1302         cPage->setPreviousPages(item.previous);
1303     }
1304 
1305     refresh();
1306 }
1307 
1308 void SKGMainPanel::onLockDocks()
1309 {
1310     QObjectList cs = children();
1311     for (auto c : qAsConst(cs)) {
1312         auto* doc = qobject_cast<QDockWidget*>(c);
1313         if (doc != nullptr) {
1314             doc->setFeatures(QDockWidget::NoDockWidgetFeatures);
1315         }
1316     }
1317 
1318     KConfigGroup pref = getMainConfigGroup();
1319     pref.writeEntry("docks_locked", true);
1320 
1321     refresh();
1322 }
1323 
1324 void SKGMainPanel::onUnlockDocks()
1325 {
1326     QObjectList cs = children();
1327     for (auto c : qAsConst(cs)) {
1328         auto* doc = qobject_cast<QDockWidget*>(c);
1329         if (doc != nullptr) {
1330             doc->setFeatures(QDockWidget::AllDockWidgetFeatures);
1331         }
1332     }
1333 
1334     KConfigGroup pref = getMainConfigGroup();
1335     pref.writeEntry("docks_locked", false);
1336 
1337     refresh();
1338 }
1339 
1340 void SKGMainPanel::onConfigureNotifications()
1341 {
1342     KNotifyConfigWidget::configure(this);
1343 }
1344 
1345 void SKGMainPanel::onShowNextMenu()
1346 {
1347     if (d->m_nextMenu != nullptr) {
1348         d->m_nextMenu->clear();
1349 
1350         SKGTabPage* cPage = currentPage();
1351         if (cPage != nullptr) {
1352             SKGTabPage::SKGPageHistoryItemList list = cPage->getNextPages();
1353             int nb = list.count();
1354             for (int i = 0; i < nb; ++i) {
1355                 QAction* act = d->m_nextMenu->addAction(SKGServices::fromTheme(list.at(i).icon), list.at(i).name);
1356                 if (act != nullptr) {
1357                     act->setData(i);
1358                     connect(act, &QAction::triggered, this, &SKGMainPanel::onNext);
1359                 }
1360             }
1361         }
1362     }
1363 }
1364 
1365 void SKGMainPanel::onShowButtonMenu()
1366 {
1367     if (d->m_buttonMenu != nullptr) {
1368         d->m_buttonMenu->clear();
1369 
1370         QMenuBar* mb = menuBar();
1371         if (mb != nullptr) {
1372             d->m_buttonMenu->addActions(mb->actions());
1373         }
1374     }
1375 }
1376 
1377 bool SKGMainPanel::queryClose()
1378 {
1379     SKGTRACEINFUNC(1)
1380     // Bug 2777697: To be sure that all page modifications are closed
1381     closeAllPages();
1382     // Bug 2777697:
1383 
1384     bool output = queryFileClose();
1385 
1386     // To be sure that the application is not closed in fullscreen mode
1387     if (output) {
1388         if (d->m_fullScreenAction->isChecked()) {
1389             d->m_fullScreenAction->trigger();
1390         }
1391     }
1392 
1393     return output;
1394 }
1395 
1396 void SKGMainPanel::setSaveOnClose(bool iSaveOnClose)
1397 {
1398     d->m_saveOnClose = iSaveOnClose;
1399 }
1400 
1401 bool SKGMainPanel::queryFileClose()
1402 {
1403     SKGTRACEINFUNC(1)
1404     bool output = true;
1405     if (getDocument()->getCurrentTransaction() != 0) {
1406         displayMessage(i18nc("skgtestimportskg", "The application cannot be closed when a transaction is running."), SKGDocument::Error);
1407         output = false;
1408     } else if (getDocument()->isFileModified()) {
1409         QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
1410 #ifdef SKG_KF_5102
1411         int code = KMessageBox::PrimaryAction;
1412 #else
1413         int code = KMessageBox::Yes;
1414 #endif
1415         QString fileName = getDocument()->getCurrentFileName();
1416         QAction* save = getGlobalAction(fileName.isEmpty() ? QStringLiteral("file_save_as") : QStringLiteral("file_save"));
1417         if (save != nullptr) {
1418             if (!d->m_saveOnClose) {
1419 #ifdef SKG_KF_5102
1420                 code = KMessageBox::questionTwoActionsCancel(this, i18nc("Question", "The document has been modified.\nDo you want to save it before closing?"),
1421                         QString(),
1422                         KGuiItem(fileName.isEmpty() ?  i18nc("Question", "Save as") : i18nc("Question", "Save"),
1423                                  SKGServices::fromTheme(fileName.isEmpty() ?  QStringLiteral("document-save-as") : QStringLiteral("document-save"))),
1424                         KGuiItem(i18nc("Question", "Do not save")));
1425             }
1426             if (code == KMessageBox::PrimaryAction) {
1427 #else
1428                 code = KMessageBox::questionYesNoCancel(this, i18nc("Question", "The document has been modified.\nDo you want to save it before closing?"),
1429                                                         QString(),
1430                                                         KGuiItem(fileName.isEmpty() ?  i18nc("Question", "Save as") : i18nc("Question", "Save"),
1431                                                                 SKGServices::fromTheme(fileName.isEmpty() ?  QStringLiteral("document-save-as") : QStringLiteral("document-save"))),
1432                                                         KGuiItem(i18nc("Question", "Do not save")));
1433             }
1434             if (code == KMessageBox::Yes) {
1435 #endif
1436                 save->trigger();
1437             }
1438 #ifdef SKG_KF_5102
1439             output = (code == KMessageBox::SecondaryAction || code == KMessageBox::PrimaryAction);
1440         } else {
1441             code = KMessageBox::questionTwoActions(this, i18nc("Question", "Current modifications will not be saved.\nDo you want to continue?"),
1442                                                    i18nc("Question",  "Close confirmation"),
1443                                                    KStandardGuiItem::apply(), KStandardGuiItem::cancel()
1444                                                   );
1445             output = (code == KMessageBox::PrimaryAction);
1446 #else
1447             output = (code == KMessageBox::No || code == KMessageBox::Yes);
1448         } else {
1449             code = KMessageBox::questionYesNo(this, i18nc("Question", "Current modifications will not be saved.\nDo you want to continue?"));
1450             output = (code == KMessageBox::Yes);
1451 #endif
1452         }
1453         QApplication::restoreOverrideCursor();
1454     }
1455 
1456     return output;
1457 }
1458 
1459 SKGObjectBase SKGMainPanel::getFirstSelectedObject() const
1460 {
1461     SKGObjectBase selection;
1462     SKGWidget* cPage = (d->m_widgetHavingSelection != nullptr ? d->m_widgetHavingSelection : currentPage());
1463     if (cPage != nullptr) {
1464         selection = cPage->getFirstSelectedObject();
1465     }
1466     return selection;
1467 }
1468 
1469 SKGObjectBase::SKGListSKGObjectBase SKGMainPanel::getSelectedObjects() const
1470 {
1471     SKGObjectBase::SKGListSKGObjectBase selection;
1472     SKGWidget* cPage = (d->m_widgetHavingSelection != nullptr ? d->m_widgetHavingSelection : currentPage());
1473     if (cPage != nullptr) {
1474         selection = cPage->getSelectedObjects();
1475     }
1476     return selection;
1477 }
1478 
1479 int SKGMainPanel::getNbSelectedObjects() const
1480 {
1481     int nb = 0;
1482     SKGWidget* cPage = (d->m_widgetHavingSelection != nullptr ? d->m_widgetHavingSelection : currentPage());
1483     if (cPage != nullptr) {
1484         nb = cPage->getNbSelectedObjects();
1485     }
1486     return nb;
1487 }
1488 
1489 bool SKGMainPanel::hasSelectionWithFocus()
1490 {
1491     bool output = false;
1492     SKGWidget* cPage = (d->m_widgetHavingSelection != nullptr ? d->m_widgetHavingSelection : currentPage());
1493     if (cPage != nullptr) {
1494         output = cPage->hasSelectionWithFocus();
1495     }
1496     return output;
1497 }
1498 
1499 SKGAdviceList SKGMainPanel::getAdvice() const
1500 {
1501     SKGTRACEINFUNC(1)
1502     // Get list of ignored advice
1503     QString currentMonth = QDate::currentDate().toString(QStringLiteral("yyyy-MM"));
1504     QStringList ignoredAdvice = getDocument()->getParameters(QStringLiteral("advice"), "t_value='I' OR t_value='I_" % currentMonth % '\'');
1505 
1506     // Build the list of all advice by requesting all plugins
1507     SKGAdviceList globalAdviceList;
1508     int index = 0;
1509     while (index >= 0) {
1510         SKGInterfacePlugin* plugin = SKGMainPanel::getMainPanel()->getPluginByIndex(index);
1511         if (plugin != nullptr) {
1512             const auto list = plugin->advice(ignoredAdvice);
1513             for (const auto& ad : list) {
1514                 if (!ignoredAdvice.contains(ad.getUUID()) && !ignoredAdvice.contains(SKGServices::splitCSVLine(ad.getUUID(), '|').at(0))) {
1515                     globalAdviceList.push_back(ad);
1516                 }
1517             }
1518         } else {
1519             index = -2;
1520         }
1521         ++index;
1522     }
1523     std::sort(globalAdviceList.begin(), globalAdviceList.end(), SKGMainPanelPrivate::adviceLessThan);
1524 
1525     return globalAdviceList;
1526 }
1527 
1528 KConfigGroup SKGMainPanel::getMainConfigGroup()
1529 {
1530     KSharedConfigPtr config = KSharedConfig::openConfig();
1531     return config->group("Main Panel");
1532 }
1533 
1534 void SKGMainPanel::optionsPreferences(const QString& iPluginName)
1535 {
1536     SKGTRACEINFUNC(1)
1537     // Compute page
1538     QString pluginName = iPluginName;
1539     if (pluginName.isEmpty()) {
1540         auto* act = qobject_cast<QAction*>(sender());
1541         if (act != nullptr) {
1542             pluginName = act->property("page").toString();
1543         }
1544     }
1545     if (pluginName.isEmpty() && (this->currentPage() != nullptr)) {
1546         pluginName = this->currentPage()->objectName();
1547     }
1548     SKGTRACEL(1) << "Open setting page: " << pluginName << SKGENDL;
1549     // Synchronize setting with confirmation panel
1550     if (skgbasegui_settings::update_modified_bookmarks() == 0) {
1551         KMessageBox::ButtonCode confirm;
1552 #ifdef SKG_KF_5102
1553         bool ask = KMessageBox::shouldBeShownTwoActions(QStringLiteral("updateBookmarkOnClose"), confirm);
1554 #else
1555         bool ask = KMessageBox::shouldBeShownYesNo(QStringLiteral("updateBookmarkOnClose"), confirm);
1556 #endif
1557         KConfigGroup pref = getMainConfigGroup();
1558         if (ask) {
1559             pref.writeEntry("update_modified_bookmarks", 0, KConfigBase::Normal);
1560             SKGTRACEL(1) << "update_modified_bookmarks set to ASK" << SKGENDL;
1561         } else if (confirm == KMessageBox::Yes) {
1562             pref.writeEntry("update_modified_bookmarks", 1, KConfigBase::Normal);
1563             SKGTRACEL(1) << "update_modified_bookmarks set to ALWAYS" << SKGENDL;
1564         } else {
1565             pref.writeEntry("update_modified_bookmarks", 2, KConfigBase::Normal);
1566             SKGTRACEL(1) << "update_modified_bookmarks set to NEVER" << SKGENDL;
1567         }
1568     }
1569 
1570     if (skgbasegui_settings::update_modified_contexts() == 0) {
1571         KMessageBox::ButtonCode confirm;
1572 #ifdef SKG_KF_5102
1573         bool ask = KMessageBox::shouldBeShownTwoActions(QStringLiteral("updateContextOnClose"), confirm);
1574 #else
1575         bool ask = KMessageBox::shouldBeShownYesNo(QStringLiteral("updateContextOnClose"), confirm);
1576 #endif
1577         KConfigGroup pref = getMainConfigGroup();
1578         if (ask) {
1579             pref.writeEntry("update_modified_contexts", 0, KConfigBase::Normal);
1580             SKGTRACEL(1) << "update_modified_contexts set to ASK" << SKGENDL;
1581         } else if (confirm == KMessageBox::Yes) {
1582             pref.writeEntry("update_modified_contexts", 1, KConfigBase::Normal);
1583             SKGTRACEL(1) << "update_modified_contexts set to ALWAYS" << SKGENDL;
1584         } else {
1585             pref.writeEntry("update_modified_contexts", 2, KConfigBase::Normal);
1586             SKGTRACEL(1) << "update_modified_contexts set to NEVER" << SKGENDL;
1587         }
1588     }
1589     skgbasegui_settings::self()->load();
1590 
1591 
1592     if (KConfigDialog::showDialog(QStringLiteral("settings"))) {
1593         return;
1594     }
1595 
1596     auto dialog = new KConfigDialog(this, QStringLiteral("settings"), skgbasegui_settings::self());
1597 
1598     // Add main
1599     auto w = new QWidget();
1600     d->uipref.setupUi(w);
1601     d->uipref.kcfg_date_format->addItem(i18nc("Date format", "Short date (%1, %2)",
1602                                         QLocale().toString(QDate::currentDate(), QLocale::ShortFormat),
1603                                         QLocale().toString(QDate::currentDate().addDays(-10), QLocale::ShortFormat)));
1604     d->uipref.kcfg_date_format->addItem(i18nc("Date format", "Long date (%1, %2)",
1605                                         QLocale().toString(QDate::currentDate(), QLocale::LongFormat),
1606                                         QLocale().toString(QDate::currentDate().addDays(-10), QLocale::LongFormat)));
1607     d->uipref.kcfg_date_format->addItem(i18nc("Date format", "Fancy short date (%1, %2)",
1608                                         KFormat().formatRelativeDate(QDate::currentDate(), QLocale::ShortFormat),
1609                                         KFormat().formatRelativeDate(QDate::currentDate().addDays(-10), QLocale::ShortFormat)));
1610     d->uipref.kcfg_date_format->addItem(i18nc("Date format", "Fancy long date (%1, %2)",
1611                                         KFormat().formatRelativeDate(QDate::currentDate(), QLocale::LongFormat),
1612                                         KFormat().formatRelativeDate(QDate::currentDate().addDays(-10), QLocale::LongFormat)));
1613     d->uipref.kcfg_date_format->addItem(i18nc("Date format", "ISO date (%1, %2)",
1614                                         QDate::currentDate().toString(Qt::ISODate),
1615                                         QDate::currentDate().addDays(-10).toString(Qt::ISODate)));
1616     dialog->addPage(w, skgbasegui_settings::self(), i18nc("Noun", "General"), QStringLiteral("preferences-other"));
1617 
1618     // Add plugin in client in right order
1619     int nbplugin = d->m_pluginsList.count();
1620     for (int j = 0; j < nbplugin; ++j) {
1621         SKGInterfacePlugin* pluginInterface = getPluginByIndex(j);
1622         if (pluginInterface != nullptr) {
1623             QWidget* w2 = pluginInterface->getPreferenceWidget();
1624             if (w2 != nullptr) {
1625                 auto icon = SKGServices::fromTheme(pluginInterface->icon());
1626                 KPageWidgetItem* p = dialog->addPage(w2, pluginInterface->getPreferenceSkeleton(), pluginInterface->title(), icon.name());
1627                 if ((p != nullptr) && pluginName == pluginInterface->objectName()) {
1628                     dialog->setCurrentPage(p);
1629                 }
1630             }
1631         }
1632     }
1633 
1634     connect(dialog, &KConfigDialog::settingsChanged, this, &SKGMainPanel::onSettingsChanged);
1635 
1636     dialog->setAttribute(Qt::WA_DeleteOnClose);
1637     dialog->show();
1638 
1639     // Refresh
1640     refresh();
1641 }
1642 
1643 void SKGMainPanel::onSettingsChanged()
1644 {
1645     SKGError err;
1646     SKGTRACEINFUNCRC(1, err) {
1647         int nb = d->m_pluginsList.count();
1648         SKGBEGINPROGRESSTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Save settings"), err, nb)
1649 
1650         // Refresh plugins
1651         for (int i = 0; !err && i < nb; ++i) {
1652             err = getPluginByIndex(i)->savePreferences();
1653             IFOKDO(err, getDocument()->stepForward(i + 1))
1654         }
1655 
1656         // Setting for tab position
1657         d->refreshTabPosition();
1658 
1659         // Setting for bookmarks modification
1660         {
1661             int option = skgbasegui_settings::update_modified_bookmarks();
1662             if (option == 0) {
1663                 // ASK: remove following setting
1664                 KMessageBox::enableMessage(QStringLiteral("updateBookmarkOnClose"));
1665                 SKGTRACEL(1) << "updateBookmarkOnClose set to ASK" << SKGENDL;
1666             } else if (option == 1) {
1667                 // ALWAYS: set following setting
1668 #ifdef SKG_KF_5102
1669                 KMessageBox::saveDontShowAgainTwoActions(QStringLiteral("updateBookmarkOnClose"), KMessageBox::Yes);
1670 #else
1671                 KMessageBox::saveDontShowAgainYesNo(QStringLiteral("updateBookmarkOnClose"), KMessageBox::No);
1672 #endif
1673                 SKGTRACEL(1) << "updateBookmarkOnClose set to Yes" << SKGENDL;
1674             } else {
1675                 // NEVER: set following setting
1676 #ifdef SKG_KF_5102
1677                 KMessageBox::saveDontShowAgainTwoActions(QStringLiteral("updateBookmarkOnClose"), KMessageBox::No);
1678 #else
1679                 KMessageBox::saveDontShowAgainYesNo(QStringLiteral("updateBookmarkOnClose"), KMessageBox::No);
1680 #endif
1681                 SKGTRACEL(1) << "updateBookmarkOnClose set to No" << SKGENDL;
1682             }
1683         }
1684         {
1685             int option = skgbasegui_settings::update_modified_contexts();
1686             if (option == 0) {
1687                 // ASK: remove following setting
1688                 KMessageBox::enableMessage(QStringLiteral("updateContextOnClose"));
1689                 SKGTRACEL(1) << "updateContextOnClose set to ASK" << SKGENDL;
1690             } else if (option == 1) {
1691                 // ALWAYS: set following setting
1692 #ifdef SKG_KF_5102
1693                 KMessageBox::saveDontShowAgainTwoActions(QStringLiteral("updateContextOnClose"), KMessageBox::Yes);
1694 #else
1695                 KMessageBox::saveDontShowAgainYesNo(QStringLiteral("updateBookmarkOnClose"), KMessageBox::Yes);
1696 
1697 #endif
1698                 SKGTRACEL(1) << "updateContextOnClose set to Yes" << SKGENDL;
1699             } else {
1700                 // NEVER: set following setting
1701 #ifdef SKG_KF_5102
1702                 KMessageBox::saveDontShowAgainTwoActions(QStringLiteral("updateContextOnClose"), KMessageBox::No);
1703 #else
1704                 KMessageBox::saveDontShowAgainYesNo(QStringLiteral("updateContextOnClose"), KMessageBox::No);
1705 #endif
1706                 SKGTRACEL(1) << "updateContextOnClose set to No" << SKGENDL;
1707             }
1708         }
1709         skgbasegui_settings::self()->load();
1710     }
1711 
1712     // Rebuild system tray
1713     d->rebuildSystemTray();
1714 
1715     emit settingsChanged();
1716 
1717     // Display error
1718     displayErrorMessage(err);
1719 }
1720 
1721 void SKGMainPanel::refresh()
1722 {
1723     SKGTRACEINFUNC(1)
1724 
1725     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1726 
1727     // Show/hide main widget
1728     bool atLeastOnePageOpened = (d->m_tabWidget->count() > 0);
1729     d->m_tabWidget->setVisible(atLeastOnePageOpened);
1730     if (d->m_mainWidget != nullptr) {
1731         d->m_mainWidget->setVisible(!atLeastOnePageOpened);
1732     }
1733 
1734     // Refresh actions
1735     d->m_widgetHavingSelection = qobject_cast<SKGWidget*>(sender());
1736     SKGObjectBase selection = SKGMainPanel::getMainPanel()->getFirstSelectedObject();
1737     int nbSelectedItems = SKGMainPanel::getMainPanel()->getNbSelectedObjects();
1738     bool hasFocus = SKGMainPanel::getMainPanel()->hasSelectionWithFocus();
1739 
1740     QString selectedTable = (nbSelectedItems > 0 ? selection.getRealTable() : QLatin1String(""));
1741     for (const auto& actDetails : qAsConst(d->m_registeredGlogalAction)) {
1742         QStringList tables = actDetails.tables;
1743         if (tables.count() == 1 && tables.at(0).startsWith(QLatin1String("query:"))) {
1744             // Dynamic mode
1745             getDocument()->getDistinctValues(QStringLiteral("sqlite_master"), QStringLiteral("name"), tables.at(0).right(tables.at(0).count() - 6), tables);
1746         }
1747 
1748         bool enabled = (tables.contains(selectedTable) || tables.isEmpty()) &&
1749                        (nbSelectedItems >= actDetails.min) &&
1750                        (nbSelectedItems <= actDetails.max || actDetails.max == -1) &&
1751                        (!actDetails.focus || hasFocus);
1752 
1753         if (enabled && nbSelectedItems == 0 && (actDetails.min == 0 || actDetails.min == -1)) {
1754             // Check if a page is opened
1755             SKGTabPage* page = SKGMainPanel::getMainPanel()->currentPage();
1756             if (page != nullptr) {
1757                 auto* view = qobject_cast<QAbstractItemView*>(page->mainWidget());
1758                 enabled = (actDetails.min == -1 || view != nullptr);
1759             } else {
1760                 enabled = false;
1761             }
1762         }
1763         if (actDetails.action != nullptr) {
1764             actDetails.action->setEnabled(enabled);
1765         }
1766     }
1767 
1768     // Refresh plugins
1769     int nb = d->m_pluginsList.count();
1770     for (int i = 0; i < nb; ++i) {
1771         getPluginByIndex(i)->refresh();
1772     }
1773 
1774     // Enable addTabeAction
1775     SKGTabPage* toSave = currentPage();
1776     if (d->m_switchPinState != nullptr) {
1777         if ((toSave != nullptr) && toSave->isPin()) {
1778             d->m_switchPinState->setText(i18nc("Noun, user action", "Unpin this page"));
1779         } else {
1780             d->m_switchPinState->setText(i18nc("Noun, user action", "Pin this page"));
1781         }
1782     }
1783     if (d->m_closePageAction != nullptr) {
1784         d->m_closePageAction->setEnabled(atLeastOnePageOpened && (toSave != nullptr) && !toSave->isPin());
1785     }
1786     if (d->m_switchPinState != nullptr) {
1787         d->m_switchPinState->setEnabled(atLeastOnePageOpened);
1788     }
1789     if (d->m_closeAllOtherPagesAction != nullptr) {
1790         d->m_closeAllOtherPagesAction->setEnabled(d->m_tabWidget->count() > 1);
1791     }
1792     if (d->m_reopenLastClosed != nullptr) {
1793         d->m_reopenLastClosed->setEnabled(!d->m_historyClosedPages.isEmpty());
1794     }
1795 
1796     if (d->m_saveDefaultStateAction != nullptr) {
1797         d->m_saveDefaultStateAction->setEnabled((toSave != nullptr) && !toSave->getDefaultStateAttribute().isEmpty());
1798     }
1799     if (d->m_resetDefaultStateAction != nullptr) {
1800         d->m_resetDefaultStateAction->setEnabled((toSave != nullptr) && !toSave->getDefaultStateAttribute().isEmpty());
1801     }
1802     if (d->m_overwriteBookmarkStateAction != nullptr) {
1803         d->m_overwriteBookmarkStateAction->setEnabled((toSave != nullptr) && !toSave->getBookmarkID().isEmpty());
1804     }
1805     if (d->m_enableEditorAction != nullptr) {
1806         d->m_enableEditorAction->setEnabled((toSave != nullptr) && toSave->isEditor());
1807     }
1808     if (d->m_zoomSelector != nullptr) {
1809         d->m_zoomSelector->setVisible((toSave != nullptr) && toSave->isZoomable());
1810         if (toSave != nullptr) {
1811             d->m_zoomSelector->setValue(toSave->zoomPosition());
1812             QWidget* zoomWidget = toSave->zoomableWidget();
1813             auto* treeView = qobject_cast<SKGTreeView*>(zoomWidget);
1814             if (treeView != nullptr) {
1815                 disconnect(treeView, &SKGTreeView::zoomChanged, this, nullptr);
1816                 connect(treeView, &SKGTreeView::zoomChanged, this, [ = ](int val) {
1817                     d->m_zoomSelector->setValue(val);
1818                 });
1819             } else {
1820                 auto* webView = qobject_cast<SKGWebView*>(zoomWidget);
1821                 if (webView != nullptr) {
1822                     disconnect(webView, &SKGWebView::zoomChanged, this, nullptr);
1823                     connect(webView, &SKGWebView::zoomChanged, this, [ = ](int val) {
1824                         d->m_zoomSelector->setValue(val);
1825                     });
1826                 }
1827             }
1828         }
1829     }
1830 
1831     if (d->m_actLock != nullptr) {
1832         d->m_actLock->setVisible(d->ui.kDockContext->features() == QDockWidget::AllDockWidgetFeatures);
1833     }
1834     if (d->m_actUnLock != nullptr) {
1835         d->m_actUnLock->setVisible(d->ui.kDockContext->features() == QDockWidget::NoDockWidgetFeatures);
1836     }
1837 
1838     if (d->m_previousAction != nullptr) {
1839         SKGTabPage::SKGPageHistoryItemList list;
1840         if (toSave != nullptr) {
1841             list = toSave->getPreviousPages();
1842         }
1843         d->m_previousAction->setEnabled(!list.isEmpty());
1844     }
1845     if (d->m_nextAction != nullptr) {
1846         SKGTabPage::SKGPageHistoryItemList list;
1847         if (toSave != nullptr) {
1848             list = toSave->getNextPages();
1849         }
1850         d->m_nextAction->setEnabled(!list.isEmpty());
1851     }
1852 
1853     // Set current selection of context
1854     d->ui.kContextList->clearSelection();
1855     if (toSave != nullptr) {
1856         // Get plugin of current page
1857         SKGInterfacePlugin* plugin = getPluginByName(toSave->objectName());
1858         int index = (plugin != nullptr ? plugin->property("contextItem").toInt() : -1);
1859         if (index != -1) {
1860             d->ui.kContextList->setCurrentItem(d->ui.kContextList->item(index));
1861         }
1862     }
1863 
1864     // Set window title
1865     QString modified;
1866     if (getDocument()->isFileModified()) {
1867         modified += i18nc("Noun, indicate that current document is modified", " [modified]");
1868     }
1869     if (getDocument()->isReadOnly()) {
1870         modified += i18nc("Noun, indicate that current document is loaded in read only", " [read only]");
1871     }
1872     QString fileName = getDocument()->getCurrentFileName();
1873     if (fileName.isEmpty()) {
1874         fileName = i18nc("Noun, default name for a new document", "Untitled");
1875     } else {
1876         if (fileName != d->m_fileName) {
1877             // The file name has been changed
1878             onClearMessages();
1879 
1880             d->m_fileName = fileName;
1881 #ifdef KActivities_FOUND
1882             if (!d->m_fileName.isEmpty()) {
1883                 d->m_activityResourceInstance->setUri(d->m_fileName);
1884             }
1885 #endif
1886         }
1887     }
1888     setWindowTitle(i18nc("Title of the main window", "%1%2", fileName, modified));
1889 
1890     QApplication::restoreOverrideCursor();
1891 }
1892 
1893 SKGTabPage::SKGPageHistoryItem SKGMainPanel::currentPageHistoryItem() const
1894 {
1895     SKGTabPage::SKGPageHistoryItem cpage;
1896     int currentIndex = currentPageIndex();
1897     SKGTabPage* cPage = currentPage();
1898     if (currentIndex >= 0 && (cPage != nullptr)) {
1899         cpage.plugin = cPage->objectName();
1900         SKGInterfacePlugin* plugin = SKGMainPanel::getMainPanel()->getPluginByName(cpage.plugin);
1901         if (plugin != nullptr) {
1902             cpage.name = d->m_tabWidget->tabText(currentIndex);
1903             cpage.icon = plugin->icon();
1904         }
1905         cpage.state = cPage->getState();
1906         cpage.bookmarkID = cPage->getBookmarkID();
1907     }
1908 
1909     return cpage;
1910 }
1911 
1912 SKGTabPage* SKGMainPanel::openPage(SKGInterfacePlugin* plugin, int index, const QString& parameters, const QString& title, const QString& iID, bool iSetCurrent)
1913 {
1914     SKGTRACEINFUNC(1)
1915     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1916     bool previous = d->m_tabWidget->blockSignals(true);
1917     // If the current page is pin, then open new page
1918     SKGTabPage* cPage = currentPage();
1919     if ((cPage != nullptr) && cPage->isPin()) {
1920         index = -1;
1921         iSetCurrent = true;
1922     }
1923 
1924     SKGTabPage* w = nullptr;
1925 
1926     SKGTabPage::SKGPageHistoryItemList previousPages;
1927     if (index != -1) {
1928         int currentIndex = currentPageIndex();
1929         if (currentIndex >= 0 && (cPage != nullptr)) {
1930             previousPages = cPage->getPreviousPages();
1931             previousPages.insert(0, currentPageHistoryItem());
1932 
1933             d->m_tabWidget->removeTab(currentIndex);
1934             closePage(cPage);
1935 
1936             // Repair the history of closed page
1937             if (!d->m_historyClosedPages.isEmpty()) {
1938                 d->m_historyClosedPages.removeLast();
1939             }
1940         }
1941     }
1942 
1943     if (plugin != nullptr) {
1944         w = plugin->getWidget();
1945         if (w != nullptr) {
1946             // Title
1947             QString title2 = (title.isEmpty() ? plugin->title() : title);
1948             w->setObjectName(plugin->objectName());
1949             if (!iID.isEmpty()) {
1950                 w->setBookmarkID(iID);
1951             }
1952 
1953             QString param = parameters;
1954             if (param.isEmpty()) {
1955                 QString def = w->getDefaultStateAttribute();
1956                 if (!def.isEmpty()) {
1957                     param = getDocument()->getParameter(def);
1958                 }
1959             }
1960             SKGTRACEL(10) << "state=[" << param << "]" << SKGENDL;
1961             w->setState(param);
1962             connect(w, &SKGTabPage::selectionChanged, this, &SKGMainPanel::refresh);
1963             connect(w, &SKGTabPage::selectionChanged, this, &SKGMainPanel::selectionChanged);
1964             connect(w, &SKGTabPage::selectionFocusChanged, this, &SKGMainPanel::refresh);
1965 
1966             if (index == -1) {
1967                 SKGTRACEINFUNC(20)
1968                 d->m_tabWidget->addTab(w, SKGServices::fromTheme(plugin->icon()), title2);
1969                 if (iSetCurrent) {
1970                     d->m_tabWidget->setCurrentWidget(w);
1971                 }
1972             } else {
1973                 SKGTRACEINFUNC(20)
1974                 d->m_tabWidget->insertTab(index, w, SKGServices::fromTheme(plugin->icon()), title2);
1975                 if (iSetCurrent) {
1976                     d->m_tabWidget->setCurrentWidget(w);
1977                 }
1978 
1979                 w->setPreviousPages(previousPages);
1980                 SKGTabPage::SKGPageHistoryItemList empty;
1981                 w->setNextPages(empty);
1982             }
1983             SKGTRACEL(1) << "opening plugin [" << plugin->objectName() << ']' << SKGENDL;
1984             Q_EMIT pageOpened();
1985         }
1986     } else {
1987         getDocument()->sendMessage(i18nc("An information message",  "Impossible to open the page because the plugin was not found"), SKGDocument::Error);
1988         notify();  // Due to sendMessage not in a transaction
1989     }
1990 
1991     // Show/hide main widget
1992     bool atLeastOnePageOpened = (d->m_tabWidget->count() > 0);
1993     d->m_tabWidget->setVisible(atLeastOnePageOpened);
1994     if (d->m_mainWidget != nullptr) {
1995         d->m_mainWidget->setVisible(!atLeastOnePageOpened);
1996     }
1997 
1998     d->m_tabWidget->blockSignals(previous);
1999     if (iSetCurrent) {
2000         Q_EMIT currentPageChanged();
2001     }
2002     QApplication::restoreOverrideCursor();
2003     return w;
2004 }
2005 
2006 int SKGMainPanel::currentPageIndex() const
2007 {
2008     return d->m_tabWidget->currentIndex();
2009 }
2010 
2011 SKGTabPage* SKGMainPanel::currentPage() const
2012 {
2013     return qobject_cast< SKGTabPage* >(d->m_tabWidget->currentWidget());
2014 }
2015 
2016 int SKGMainPanel::pageIndex(SKGTabPage* iPage) const
2017 {
2018     int nb = countPages();
2019     for (int i = 0; i < nb; ++i) {
2020         if (page(i) == iPage) {
2021             return i;
2022         }
2023     }
2024 
2025     return -1;
2026 }
2027 
2028 QSplashScreen* SKGMainPanel::splashScreen() const
2029 {
2030     return d->m_splashScreen;
2031 }
2032 
2033 int SKGMainPanel::countPages() const
2034 {
2035     return d->m_tabWidget->count();
2036 }
2037 
2038 SKGTabPage* SKGMainPanel::page(int iIndex) const
2039 {
2040     return qobject_cast<SKGTabPage*>(d->m_tabWidget->widget(iIndex));
2041 }
2042 
2043 void SKGMainPanel::setCurrentPage(int iIndex)
2044 {
2045     d->m_tabWidget->setCurrentIndex(iIndex);
2046 }
2047 
2048 void SKGMainPanel::onBeforeOpenContext()
2049 {
2050     d->m_middleClick = ((QApplication::mouseButtons() & Qt::MidButton) != 0u);
2051 }
2052 
2053 bool SKGMainPanel::openPage(const QUrl& iUrl, bool iNewPage)
2054 {
2055     const QUrl& url(iUrl);
2056     if (url.scheme() == QStringLiteral("skg")) {
2057         // Get plugin
2058         SKGInterfacePlugin* plugin = getPluginByName(url.host());
2059         if (plugin != nullptr) {
2060             // Open special page
2061             SKGTabPage* w = plugin->getWidget();
2062             if (w != nullptr) {
2063                 // Create xml
2064                 QString path = url.path().remove('/');
2065                 QDomDocument doc(QStringLiteral("SKGML"));
2066                 doc.setContent(getDocument()->getParameter(path.isEmpty() ? w->getDefaultStateAttribute() : path));
2067                 QDomElement root = doc.documentElement();
2068                 if (root.isNull()) {
2069                     root = doc.createElement(QStringLiteral("parameters"));
2070                     doc.appendChild(root);
2071                 }
2072                 auto params = QUrlQuery(url).queryItems();
2073                 for (const auto& p : qAsConst(params)) {
2074                     QString value = QUrl::fromPercentEncoding(p.second.toUtf8());
2075                     SKGMainPanelPrivate::setAttribute(root, p.first, value);
2076                 }
2077 
2078                 // Open page
2079                 openPage(plugin, iNewPage ? -1 : currentPageIndex(), doc.toString(), url.fragment());
2080                 return true;
2081             }
2082         } else {
2083             // Trigger action
2084             QAction* act = SKGMainPanel::getMainPanel()->getGlobalAction(url.host());
2085             if (act != nullptr) {
2086                 auto params = QUrlQuery(url).queryItems();
2087                 for (const auto& p : qAsConst(params)) {
2088                     QString value = QUrl::fromPercentEncoding(p.second.toUtf8());
2089                     act->setProperty(p.first.toUtf8().data(), value);
2090                 }
2091 
2092                 act->trigger();
2093                 return true;
2094             }
2095         }
2096     } else {
2097         QDesktopServices::openUrl(iUrl);
2098         return true;
2099     }
2100     displayErrorMessage(SKGError(ERR_ABORT, i18nc("Error message", "Unknown plugin or action [%1] in url [%2]", url.host(), iUrl.toString())));
2101 
2102     return false;
2103 }
2104 
2105 bool SKGMainPanel::openPage()
2106 {
2107     return openPage(QString());
2108 }
2109 
2110 bool SKGMainPanel::openPage(const QString& iUrl, bool iNewPage)
2111 {
2112     // Get the url
2113     QString urlString(iUrl);
2114     if (urlString.isEmpty()) {
2115         auto* act = qobject_cast< QAction* >(sender());
2116         if (act != nullptr) {
2117             urlString = act->data().toString();
2118         }
2119     }
2120 
2121     return openPage(QUrl(urlString), iNewPage);
2122 }
2123 
2124 SKGTabPage* SKGMainPanel::openPage(int iPage, bool iNewPage)
2125 {
2126     SKGTRACEINFUNC(1)
2127     SKGTRACEL(1) << "iPage=" << iPage << SKGENDL;
2128     int index = d->ui.kContextList->item(iPage)->data(12).toInt();
2129     return openPage(getPluginByIndex(index), iNewPage ? -1 : currentPageIndex());
2130 }
2131 
2132 void SKGMainPanel::onOpenContext()
2133 {
2134     SKGTRACEINFUNC(1)
2135     if (!(QApplication::mouseButtons() & Qt::RightButton)) {
2136         int cpage = -1;
2137         auto* s = qobject_cast<QAction*>(this->sender());
2138         if (s != nullptr) {
2139             cpage = s->data().toInt();
2140         } else {
2141             cpage = d->ui.kContextList->currentRow();
2142         }
2143         if (cpage != -1) {
2144             openPage(cpage, ((QApplication::keyboardModifiers() &Qt::ControlModifier) != 0u) || d->m_middleClick || ((QGuiApplication::mouseButtons() & Qt::MidButton) != 0u));
2145         }
2146     }
2147     d->m_middleClick = false;
2148 }
2149 
2150 void SKGMainPanel::switchPinPage(QWidget* iWidget)
2151 {
2152     auto* toSwitch = qobject_cast< SKGTabPage* >(iWidget);
2153     if (toSwitch == nullptr) {
2154         toSwitch = currentPage();
2155     }
2156 
2157     if (toSwitch != nullptr) {
2158         toSwitch->setPin(!toSwitch->isPin());
2159         Q_EMIT currentPageChanged();
2160     }
2161 }
2162 
2163 void SKGMainPanel::closePageByIndex(int iIndex)
2164 {
2165     QWidget* w = nullptr;
2166     if (iIndex >= 0) {
2167         w = d->m_tabWidget->widget(iIndex);
2168     } else {
2169         w = d->m_tabWidget->currentWidget();
2170     }
2171 
2172     closePage(w);
2173 }
2174 
2175 void SKGMainPanel::closeCurrentPage()
2176 {
2177     closePage(nullptr);
2178 }
2179 
2180 void SKGMainPanel::closePage(QWidget* iWidget, bool iForce)
2181 {
2182     SKGTRACEINFUNC(1)
2183     if (getDocument()->getCurrentTransaction() != 0) {
2184         QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
2185         displayMessage(i18nc("Information message", "A page cannot be closed when a transaction is running."), SKGDocument::Information);
2186         QApplication::restoreOverrideCursor();
2187     } else {
2188         auto* toRemove = qobject_cast< SKGTabPage* >(iWidget);
2189         if (toRemove == nullptr) {
2190             toRemove = currentPage();
2191         }
2192         if ((toRemove != nullptr) && toRemove->close(iForce)) {
2193             historyPage item;
2194             item.current = currentPageHistoryItem();
2195             item.next = toRemove->getNextPages();
2196             item.previous = toRemove->getPreviousPages();
2197             d->m_historyClosedPages.push_back(item);
2198             delete toRemove;
2199 
2200             emit pageClosed();
2201         }
2202     }
2203 
2204     // Show/hide main widget
2205     bool atLeastOnePageOpened = (d->m_tabWidget->count() > 0);
2206     d->m_tabWidget->setVisible(atLeastOnePageOpened);
2207     if (d->m_mainWidget != nullptr) {
2208         d->m_mainWidget->setVisible(!atLeastOnePageOpened);
2209     }
2210 
2211     if (!atLeastOnePageOpened) {
2212         d->m_fullScreenAction->setChecked(false);
2213         onFullScreen();
2214     }
2215 }
2216 
2217 void SKGMainPanel::closeAllPages(bool iForce)
2218 {
2219     SKGTRACEINFUNC(1)
2220     bool previous = d->m_tabWidget->blockSignals(true);
2221     int nb = d->m_tabWidget->count();
2222     for (int i = nb - 1; i >= 0; --i) {
2223         auto* w = qobject_cast< SKGTabPage* >(d->m_tabWidget->widget(i));
2224         if ((w != nullptr) && (iForce || !w->isPin())) {
2225             closePage(w, iForce);
2226         }
2227     }
2228     d->m_tabWidget->blockSignals(previous);
2229     KMessageBox::enableMessage(QStringLiteral("closepinnedpage"));
2230     Q_EMIT currentPageChanged();
2231 }
2232 
2233 void SKGMainPanel::closeAllOtherPages(QWidget* iWidget)
2234 {
2235     SKGTRACEINFUNC(1)
2236     bool previous = d->m_tabWidget->blockSignals(true);
2237     QWidget* toKeep = iWidget;
2238     if (toKeep == nullptr) {
2239         toKeep = currentPage();
2240     }
2241 
2242     int nb = d->m_tabWidget->count();
2243     for (int i = nb - 1; i >= 0; --i) {
2244         auto* w = qobject_cast< SKGTabPage* >(d->m_tabWidget->widget(i));
2245         if ((w != nullptr) && w != toKeep && !w->isPin()) {
2246             closePage(w);
2247         }
2248     }
2249     d->m_tabWidget->blockSignals(previous);
2250     Q_EMIT currentPageChanged();
2251 }
2252 
2253 void SKGMainPanel::saveDefaultState()
2254 {
2255     SKGTRACEINFUNC(1)
2256     SKGError err;
2257     SKGTabPage* toSave = currentPage();
2258     if (toSave != nullptr) {
2259         // Get bookmarks uuid
2260         QString uuid = toSave->getBookmarkID();
2261 
2262         // Reset bookmarks uuid to overwrite page state
2263         toSave->setBookmarkID(QLatin1String(""));
2264 
2265         // Overwrite
2266         toSave->overwrite(false);
2267 
2268         // Set original bookmarks uuid
2269         toSave->setBookmarkID(uuid);
2270     }
2271 }
2272 
2273 void SKGMainPanel::overwriteBookmarkState()
2274 {
2275     SKGTRACEINFUNC(1)
2276     SKGError err;
2277     SKGTabPage* toSave = currentPage();
2278     if (toSave != nullptr) {
2279         // Get bookmarks uuid
2280         QString uuid = toSave->getBookmarkID();
2281         if (!uuid.isEmpty()) {
2282             // Overwrite
2283             toSave->overwrite(false);
2284         }
2285     }
2286 }
2287 
2288 void SKGMainPanel::resetDefaultState()
2289 {
2290     SKGTRACEINFUNC(1)
2291     SKGError err;
2292     SKGTabPage* toSave = currentPage();
2293     if (toSave != nullptr) {
2294         QString name = toSave->getDefaultStateAttribute();
2295         if (!name.isEmpty()) {
2296             SKGBEGINLIGHTTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Reset default state"), err)
2297             IFOKDO(err, getDocument()->setParameter(name, QStringLiteral("<!DOCTYPE SKGML>")))
2298 
2299             // Refresh panel
2300             IFOK(err) toSave->setState(QLatin1String(""));
2301         }
2302     }
2303 
2304     // status bar
2305     IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Default state has been reset")))
2306     displayErrorMessage(err);
2307 }
2308 
2309 void SKGMainPanel::addTab()
2310 {
2311     SKGTRACEINFUNC(1)
2312     SKGTabPage* cPage = currentPage();
2313     if (cPage != nullptr) {
2314         openPage(getPluginByName(cPage->objectName()));
2315     }
2316 }
2317 
2318 bool SKGMainPanel::eventFilter(QObject* iObject, QEvent* iEvent)
2319 {
2320     if ((iObject != nullptr) && (iEvent != nullptr) && iEvent->type() == QEvent::Resize) {
2321         auto* rEvent = dynamic_cast<QResizeEvent*>(iEvent);
2322         if (rEvent != nullptr) {
2323             QSize newSize = rEvent->size();
2324 
2325             // Compute icon size
2326             int s = qMax(qMin(newSize.width() / 5, 64), 16);
2327             d->ui.kContextList->setIconSize(QSize(s, s));
2328         }
2329     }
2330     return KXmlGuiWindow::eventFilter(iObject, iEvent);
2331 }
2332 
2333 QStringList SKGMainPanel::getTipsOfDay() const
2334 {
2335     return d->m_tipsOfTheDay;
2336 }
2337 
2338 QString SKGMainPanel::getTipOfDay() const
2339 {
2340     auto tips = getTipsOfDay();
2341     return SKGServices::htmlToString(tips.at(QRandomGenerator::global()->bounded(tips.size())));
2342 }
2343 
2344 void SKGMainPanel::notify(int iTransaction)
2345 {
2346     SKGTRACEINFUNC(1)
2347     SKGTRACEL(1) << "iTransaction=" << iTransaction << SKGENDL;
2348 
2349     // Notify
2350     SKGObjectBase transaction(getDocument(), QStringLiteral("doctransaction"), iTransaction);
2351     if (iTransaction == 0 || transaction.getAttribute(QStringLiteral("t_mode")) != QStringLiteral("R")) {
2352         SKGDocument::SKGMessageList msg;
2353         getDocument()->getMessages(iTransaction, msg, false);
2354         int nbMessages = msg.count();
2355         if (nbMessages != 0) {
2356             // Build list of types
2357             SKGDocument::MessageType maxType = SKGDocument::Positive;
2358             QList<SKGDocument::MessageType> listGroups;
2359             listGroups.reserve(nbMessages);
2360             for (int i = 0; i < nbMessages; ++i) {
2361                 SKGDocument::SKGMessage m = msg.at(i);
2362                 // if the message has an action, it can not be grouped
2363                 if (!m.Action.isEmpty()) {
2364                     displayMessage(m.Text, m.Type, m.Action);
2365                     msg.removeAt(i);
2366                     i--;
2367                     nbMessages--;
2368                 } else {
2369                     if (listGroups.isEmpty() || m.Type != listGroups.at(listGroups.count() - 1)) {
2370                         listGroups.push_back(m.Type);
2371                     }
2372                     if (static_cast<int>(m.Type) >= static_cast<int>(maxType)) {
2373                         maxType = m.Type;
2374                     }
2375                 }
2376             }
2377 
2378             // Is the number of type acceptable?
2379             bool modeGrouped = false;
2380             if (listGroups.count() > 5 || nbMessages > 20) {
2381                 // Too many group ==> simplification
2382                 listGroups.clear();
2383                 listGroups.push_back(maxType);
2384                 modeGrouped = true;
2385             }
2386 
2387             // Build message
2388             if (nbMessages != 0) {
2389                 QString message;
2390                 int indexGroup = 0;
2391                 for (int i = 0; i < nbMessages; ++i) {
2392                     auto m = msg.at(i);
2393                     auto t = m.Type;
2394                     if (modeGrouped) {
2395                         if (t == SKGDocument::Warning) {
2396                             m.Text = i18nc("Warning header", "Warning: %1", m.Text);
2397                         } else if (t == SKGDocument::Error) {
2398                             m.Text = i18nc("Error header", "Error: %1", m.Text);
2399                         } else if (t == SKGDocument::Information) {
2400                             m.Text = i18nc("Information header", "Information: %1", m.Text);
2401                         } else if (t == SKGDocument::Positive) {
2402                             m.Text = i18nc("Done header", "Done: %1", m.Text);
2403                         }
2404                     }
2405                     if (modeGrouped || t == listGroups.at(indexGroup)) {
2406                         // Same group
2407                         if (!message.isEmpty()) {
2408                             message += QStringLiteral("<br>");
2409                         }
2410                         message += m.Text;
2411                     } else {
2412                         // Different group
2413                         displayMessage(message, listGroups.at(indexGroup));
2414 
2415                         // Reset message
2416                         message = m.Text;
2417 
2418                         indexGroup++;
2419                     }
2420                 }
2421 
2422                 if (nbMessages < 21 || !SKGServices::getEnvVariable(QStringLiteral("SKGTEST")).isEmpty()) {
2423                     // Display a simple notification
2424                     /*auto notify = new KNotification(KAboutData::applicationData().componentName() % "_info_event" , this);
2425                     notify->setText(message);
2426                     notify->sendEvent();*/
2427                     displayMessage(message, listGroups.at(indexGroup));
2428                 } else {
2429                     // Too many message, display a warning panel
2430                     KMessageBox::information(SKGMainPanel::getMainPanel(), message, i18nc("Noun",  "Notification"));
2431                 }
2432             }
2433         }
2434     }
2435 }
2436 
2437 void SKGMainPanel::changeEvent(QEvent* e)
2438 {
2439     KXmlGuiWindow::changeEvent(e);
2440 }
2441 
2442 QLabel* SKGMainPanel::statusNormalMessage() const
2443 {
2444     return d->m_kNormalMessage;
2445 }
2446 
2447 KMessageWidget* SKGMainPanel::getMessageWidget(const QString& iMessage, SKGDocument::MessageType iType, const QString& iAction, bool iAutoKillOnClick)
2448 {
2449     KMessageWidget* msg = nullptr;
2450     if (!iMessage.isEmpty()) {
2451         msg = new KMessageWidget(this);
2452         msg->setText(iMessage);
2453         msg->setIcon(SKGServices::fromTheme(iType == SKGDocument::Positive ? QStringLiteral("dialog-positive") :
2454                                             iType == SKGDocument::Information ? QStringLiteral("dialog-information") :
2455                                             iType == SKGDocument::Warning ? QStringLiteral("dialog-warning") : QStringLiteral("dialog-error")));
2456         msg->setMessageType(static_cast<KMessageWidget::MessageType>(iType));
2457         if (!iAction.isEmpty()) {
2458             QUrl url(iAction);
2459             if (url.scheme() == QStringLiteral("skg")) {
2460                 QAction* action = SKGMainPanel::getMainPanel()->getGlobalAction(url.host(), false);
2461                 QAction* act = nullptr;
2462                 if (action != nullptr) {
2463                     // This is an action
2464                     act = new QAction(action->icon(), action->text(), SKGMainPanel::getMainPanel());
2465                 } else {
2466                     // This is a default action
2467                     act = new QAction(SKGServices::fromTheme(QStringLiteral("open")), i18nc("Verb", "Open …"), SKGMainPanel::getMainPanel());
2468                 }
2469 
2470                 act->setData(iAction);
2471 
2472                 msg->addAction(act);
2473                 connect(act, &QAction::triggered, this, [ = ] { openPage(QUrl(qobject_cast< QAction* >(sender())->data().toString()), true);});
2474                 if (iAutoKillOnClick) {
2475                     connect(act, &QAction::triggered, msg, &KMessageWidget::deleteLater, Qt::QueuedConnection);
2476                 }
2477             }
2478         }
2479     }
2480     return msg;
2481 }
2482 
2483 KMessageWidget* SKGMainPanel::displayMessage(const QString& iMessage, SKGDocument::MessageType iType, const QString& iAction)
2484 {
2485     // Create message widget
2486     KMessageWidget* msg = nullptr;
2487     if (!iMessage.isEmpty()) {
2488         msg = getMessageWidget(iMessage, iType, iAction, true);
2489         QTimer::singleShot(iType == SKGDocument::Positive ? 5000 : iType == SKGDocument::Information ? 10000 : 20000, Qt::CoarseTimer, msg, &KMessageWidget::deleteLater);
2490         msg->show();
2491         d->m_mainLayout->insertWidget(qMax(d->m_mainLayout->indexOf(d->m_mainWidget) - 1, 0), msg);
2492 
2493         // Store message
2494         auto msg2 = getMessageWidget(iMessage, iType, iAction, false);
2495         auto* l = qobject_cast< QVBoxLayout* >(d->ui.kMessagesLayout->layout());
2496         if (l != nullptr) {
2497             l->insertWidget(0, msg2);
2498         }
2499     }
2500 
2501     // Emit message
2502     //  [Event/error]
2503     //  [Event/neutral]
2504     //  [Event/positive]
2505     auto notification = new KNotification(iType == SKGDocument::Error ? QStringLiteral("error") :
2506                                           (iType == SKGDocument::Positive ? QStringLiteral("positive") :
2507                                            (iType == SKGDocument::Warning ? QStringLiteral("negative") :
2508                                             QStringLiteral("neutral"))));
2509     notification->setWidget(this);
2510     notification->setText(iMessage);
2511     notification->sendEvent();
2512 
2513     // Alert
2514     if (iType == SKGDocument::Error || iType == SKGDocument::Warning) {
2515         qApp->alert(this);
2516     }
2517 
2518     return msg;
2519 }
2520 
2521 KMessageWidget* SKGMainPanel::displayErrorMessage(const QString& iMessage)
2522 {
2523     QString msg = iMessage;
2524     if (msg.isEmpty()) {
2525         auto* act = qobject_cast< QAction* >(sender());
2526         if (act != nullptr) {
2527             msg = act->data().toString();
2528         }
2529     }
2530     return displayMessage(msg, SKGDocument::Error);
2531 }
2532 
2533 KMessageWidget* SKGMainPanel::displayErrorMessage(const SKGError& iError, bool iNotifyIfNoError)
2534 {
2535     return displayErrorMessage(iError, nullptr, iNotifyIfNoError);
2536 }
2537 
2538 KMessageWidget* SKGMainPanel::displayErrorMessage(const SKGError& iError, QAction* iAction, bool iNotifyIfNoError)
2539 {
2540     SKGTRACEINFUNC(1)
2541     KMessageWidget* msg = nullptr;
2542     SKGMainPanel* parent = SKGMainPanel::getMainPanel();
2543     if (parent != nullptr) {
2544         if (iError) {
2545             // Get the message
2546             //BUG 423311: Remove history button
2547             msg = parent->displayMessage(iError.getFullMessageWithHistorical(), SKGDocument::Error, iError.getAction());
2548 
2549             // Add history action in case of
2550             /*if (iError.getHistoricalSize() != 0) {
2551                 auto history = new QAction(i18nc("Noun", "History"), msg);
2552                 history->setIcon(SKGServices::fromTheme(QStringLiteral("dialog-information")));
2553                 history->setData(iError.getFullMessageWithHistorical());
2554                 msg->addAction(history);
2555                 connect(history, &QAction::triggered, parent, [ = ] { parent->displayErrorMessage();});
2556                 connect(history, &QAction::triggered, msg, &KMessageWidget::deleteLater, Qt::QueuedConnection);
2557             }*/
2558 
2559             // Add the additional action
2560             if (iAction != nullptr) {
2561                 iAction->setParent(msg);
2562                 msg->addAction(iAction);
2563                 connect(iAction, &QAction::triggered, msg, &KMessageWidget::deleteLater, Qt::QueuedConnection);
2564             }
2565         } else {
2566             if (iNotifyIfNoError) {
2567                 auto notification = new KNotification(QStringLiteral("positive"));
2568                 notification->setWidget(parent);
2569                 notification->setText(iError.getFullMessage());
2570                 notification->sendEvent();
2571             }
2572             // Status bar
2573             QLabel* label = parent->statusNormalMessage();
2574             QString message = iError.getMessage();
2575             if ((label != nullptr) && !message.isEmpty()) {
2576                 label->setText(message);
2577             }
2578         }
2579     }
2580     return msg;
2581 }
2582 
2583 void SKGMainPanel::onCancelCurrentAction()
2584 {
2585     SKGMainPanelPrivate::m_currentActionCanceled = true;
2586 }
2587 
2588 void SKGMainPanel::onQuitAction()
2589 {
2590     // Bug 2777697: To be sure that all page modifications are closed
2591     closeAllPages(true);
2592     // Bug 2777697:
2593 
2594     qApp->closeAllWindows();
2595 }
2596 
2597 QString SKGMainPanel::getSaveFileName(const QString& iStartDir, const QString& iFilter, QWidget* iParent, QString* iCodec)
2598 {
2599     QString fileName;
2600 
2601     QString lastCodecUsed = QTextCodec::codecForLocale()->name();
2602     KEncodingFileDialog::Result result = KEncodingFileDialog::getSaveUrlAndEncoding(lastCodecUsed, QUrl::fromLocalFile(QUrl(iStartDir).toLocalFile()), iFilter, iParent);
2603     if (!result.URLs.isEmpty()) {
2604         fileName = result.URLs.at(0).toLocalFile();
2605     }
2606     if (iCodec != nullptr) {
2607         *iCodec = result.encoding;
2608     }
2609     if (fileName.isEmpty()) {
2610         return QLatin1String("");
2611     }
2612     QFile f(fileName);
2613     if (f.exists() && KMessageBox::warningContinueCancel(iParent,
2614             i18nc("Question", "File <b>%1</b> already exists. Do you really want to overwrite it?", fileName),
2615             i18nc("Question", "Warning"),
2616             KGuiItem(i18nc("Verb",  "Save"), SKGServices::fromTheme(QStringLiteral("document-save")))) != KMessageBox::Continue) {
2617         return QLatin1String("");
2618     }
2619 
2620     return fileName;
2621 }
2622 
2623 void SKGMainPanel::fillWithDistinctValue(
2624     const QList<QWidget*>& iWidgets,
2625     SKGDocument* iDoc,
2626     const QString& iTable,
2627     const QString& iAttribut,
2628     const QString& iWhereClause,
2629     bool iAddoperators)
2630 {
2631     SKGTRACEINFUNC(10)
2632 
2633     if (iDoc != nullptr) {
2634         {
2635             // Get list
2636             QStringList list;
2637             {
2638                 SKGTRACEIN(10, "SKGMainPanel::fillWithDistinctValue-build list " % iTable % " " % iAttribut)
2639                 iDoc->getDistinctValues(iTable, iAttribut, iWhereClause, list);
2640                 if (!list.isEmpty() && !list.at(0).isEmpty()) {
2641                     list.insert(0, QLatin1String(""));
2642                 }
2643 
2644                 // Sorting list
2645                 {
2646                     SKGTRACEIN(10, "SKGMainPanel::fillWithDistinctValue-build list sorting " % iTable % " " % iAttribut)
2647                     // Correction bug 202341 vvv
2648                     QCollator c;
2649                     std::sort(list.begin(), list.end(), [&](const QString & a, const QString & b) {
2650                         return c.compare(a, b) < 0;
2651                     });
2652                 }
2653 
2654                 // Add operator
2655                 if (iAddoperators) {
2656                     list.push_back('=' % i18nc("Key word to modify a string into a field", "capitalize"));
2657                     list.push_back('=' % i18nc("Key word to modify a string into a field", "capwords"));
2658                     list.push_back('=' % i18nc("Key word to modify a string into a field", "lower"));
2659                     list.push_back('=' % i18nc("Key word to modify a string into a field", "trim"));
2660                     list.push_back('=' % i18nc("Key word to modify a string into a field", "upper"));
2661                 }
2662             }
2663 
2664             {
2665                 SKGTRACEIN(10, "SKGMainPanel::fillWithDistinctValue-fill " % iTable % " " % iAttribut)
2666                 SKGTRACEL(10) << "list.count()=" << list.count() << SKGENDL;
2667                 for (auto w : qAsConst(iWidgets)) {
2668                     auto comp = new QCompleter(list, w);
2669                     if (comp != nullptr) {
2670                         comp->setCaseSensitivity(Qt::CaseInsensitive);
2671                         comp->setFilterMode(Qt::MatchContains);
2672 
2673                         // Fill completion
2674                         auto* kcmb = qobject_cast<KComboBox*> (w);
2675                         if (kcmb != nullptr) {
2676                             // Fill combo
2677                             kcmb->clear();
2678                             kcmb->addItems(list);
2679 
2680                             if (kcmb->isEditable()) {
2681                                 kcmb->setCompleter(comp);
2682                             }
2683                         } else {
2684                             auto* kline = qobject_cast<QLineEdit*> (w);
2685                             if (kline != nullptr) {
2686                                 kline->setClearButtonEnabled(true);
2687                                 kline->setCompleter(comp);
2688                             }
2689                         }
2690                     }
2691                 }
2692             }
2693         }
2694     }
2695 }
2696 
2697 SKGMainPanel* SKGMainPanel::getMainPanel()
2698 {
2699     return SKGMainPanelPrivate::m_mainPanel;
2700 }
2701 
2702 void SKGMainPanel::onZoomChanged()
2703 {
2704     SKGTabPage* toSave = currentPage();
2705     if (toSave != nullptr) {
2706         toSave->setZoomPosition(d->m_zoomSelector->value());
2707         d->m_zoomSelector->setValue(toSave->zoomPosition());  // In case of a limit is reached
2708     }
2709 }
2710 
2711 void SKGMainPanel::setMainWidget(QWidget* iWidget)
2712 {
2713     if (d->m_mainWidget == nullptr && d->m_mainLayout != nullptr && iWidget != nullptr) {
2714         d->m_mainWidget = iWidget;
2715         d->m_mainLayout->addWidget(d->m_mainWidget);
2716 
2717         // Show/hide main widget
2718         d->m_tabWidget->setVisible(d->m_tabWidget->count() != 0);
2719         if (d->m_mainWidget != nullptr) {
2720             d->m_mainWidget->setVisible(!d->m_tabWidget->isVisible());
2721         }
2722     }
2723 }
2724 
2725 SKGTabWidget* SKGMainPanel::getTabWidget() const
2726 {
2727     return d->m_tabWidget;
2728 }
2729 
2730 void SKGMainPanel::onClearMessages()
2731 {
2732     QLayout* l = d->ui.kMessagesLayout->layout();
2733     if (l != nullptr) {
2734         // Remove all item of the layout
2735         while (l->count() > 1) {
2736             QLayoutItem* child = l->takeAt(0);
2737             if (child != nullptr) {
2738                 QWidget* w = child->widget();
2739                 delete w;
2740                 delete child;
2741             }
2742         }
2743     }
2744 }
2745 
2746 void SKGMainPanel::onMigrateToSQLCipher()
2747 {
2748     SKGError err;
2749     SKGTRACEINFUNCRC(10, err)
2750     if (getDocument()->isFileModified()) {
2751         err = SKGError(ERR_ABORT, i18nc("An information message",  "The document must be saved to be migrated."), QStringLiteral("skg://file_save"));
2752     } else {
2753         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
2754 
2755         // Set parameters
2756         QString input = getDocument()->getCurrentFileName();
2757         QString tmp = input % ".sqlcipher";
2758         QString output = input % "_migrated.skg";
2759         output = output.replace(QStringLiteral(".skg_migrated"), QStringLiteral("_migrated"));
2760 
2761         // Build argument
2762         QStringList arg;
2763         arg.push_back(QStringLiteral("--in"));
2764         arg.push_back(input);
2765         arg.push_back(QStringLiteral("--out"));
2766         arg.push_back(tmp);
2767 
2768 
2769         QString password = getDocument()->getPassword();
2770         if (!password.isEmpty()) {
2771             arg.push_back(QStringLiteral("--param"));
2772             arg.push_back(QStringLiteral("password"));
2773             arg.push_back(QStringLiteral("--value"));
2774             arg.push_back(password);
2775             password = " --param password --value \"" % password % "\"";
2776         }
2777 
2778         // Conversion skg => sqlcipher
2779         QString cmd = "skroogeconvert --in \"" % input % "\" --out \"" % tmp % "\"" % password;
2780         int rc = QProcess::execute(QStringLiteral("skroogeconvert"), arg);
2781         if (rc != 0) {
2782             err.setReturnCode(ERR_FAIL).setMessage(i18nc("Error message",  "The following command line failed with code %2:\n'%1'", cmd, rc));
2783         } else {
2784             cmd = "skroogeconvert --in \"" % tmp % "\" --out \"" % output % "\"" % password;
2785             arg[1] = tmp;
2786             arg[3] = output;
2787             rc = QProcess::execute(QStringLiteral("skroogeconvert"), arg);
2788             if (rc != 0) {
2789                 err.setReturnCode(ERR_FAIL).setMessage(i18nc("Error message",  "The following command line failed with code %2:\n'%1'", cmd, rc));
2790             } else {
2791                 getDocument()->sendMessage(i18nc("Positive message", "You document has been migrated.\nHere is the new file:\n%1", output), SKGDocument::Positive, "skg://file_open/?filename=" % output);
2792                 notify();
2793             }
2794         }
2795         QFile(tmp).remove();
2796 
2797         QApplication::restoreOverrideCursor();
2798     }
2799 
2800     // Display error
2801     SKGMainPanel::displayErrorMessage(err);
2802 }
2803 
2804 QString SKGMainPanel::dateToString(QDate  iDate)
2805 {
2806     switch (skgbasegui_settings::date_format()) {
2807     case 0:
2808         return QLocale().toString(iDate, QLocale::ShortFormat);
2809     case 1:
2810         return QLocale().toString(iDate, QLocale::LongFormat);
2811     case 3:
2812         return KFormat().formatRelativeDate(iDate, QLocale::LongFormat);
2813     case 4:
2814         return iDate.toString(Qt::ISODate);
2815     case 2:
2816     default:
2817         return KFormat().formatRelativeDate(iDate, QLocale::ShortFormat);
2818     }
2819 }