1 /***************************************************************************
2  * SPDX-FileCopyrightText: 2021 S. MANKOWSKI stephane@mankowski.fr
3  * SPDX-FileCopyrightText: 2021 G. DE BURE support@mankowski.fr
4  * SPDX-License-Identifier: GPL-3.0-or-later
5  ***************************************************************************/
6 /** @file
7 * This file defines classes skgmainpanel.
8 *
9 * @author Stephane MANKOWSKI / Guillaume DE BURE
10 */
11 #include "skgmainpanel.h"
12 
13 #include <kaboutdata.h>
14 #include <kactioncollection.h>
15 #include <kactionmenu.h>
16 #include <kcombobox.h>
17 #include <kconfigdialog.h>
18 #include <kencodingfiledialog.h>
19 #include <kformat.h>
20 #include <kmessagebox.h>
21 #include <kmessagewidget.h>
22 #include <knotification.h>
23 #include <knotifyconfigwidget.h>
24 #include <kselectaction.h>
25 #include <kservice.h>
26 #include <kservicetypetrader.h>
27 #include <kstandardaction.h>
28 #include <kstatusnotifieritem.h>
29 #include <kstringhandler.h>
30 #include <ktoggleaction.h>
31 #include <ktoolbar.h>
32 #include <ktoolbarpopupaction.h>
33 #include <kxmlguifactory.h>
34 
35 #ifdef KActivities_FOUND
36 #include <kactivities/resourceinstance.h>
37 #endif
38 
39 #ifdef SKG_DBUS
40 #include <QDBusConnection>
41 #include <QDBusMessage>
42 #endif
43 
44 #include <qaction.h>
45 #include <qapplication.h>
46 #include <qcollator.h>
47 #include <qcompleter.h>
48 #include <qdesktopservices.h>
49 #include <qdir.h>
50 #include <qdom.h>
51 #include <qevent.h>
52 #include <qfiledialog.h>
53 #include <qheaderview.h>
54 #include <qlineedit.h>
55 #include <qmenu.h>
56 #include <qprocess.h>
57 #include <qprogressbar.h>
58 #include <qpushbutton.h>
59 #include <qscrollarea.h>
60 #include <qsplashscreen.h>
61 #include <qtabbar.h>
62 #include <qtabwidget.h>
63 #include <qtextcodec.h>
64 #include <qurlquery.h>
65 #include <QRandomGenerator>
66 
67 #include <algorithm>    // std::sort
68 
69 #include "skgdefine.h"
70 #include "skginterfaceplugin.h"
71 #include "skgservices.h"
72 #include "skgtraces.h"
73 #include "skgtransactionmng.h"
74 #include "skgtreeview.h"
75 #include "skgwebview.h"
76 #include "skgzoomselector.h"
77 
78 struct doublePointer {
79     void* p1;
80     void* p2;
81 };
82 
83 struct historyPage {
84     SKGTabPage::SKGPageHistoryItem current;
85     SKGTabPage::SKGPageHistoryItemList next;
86     SKGTabPage::SKGPageHistoryItemList previous;
87 };
88 
89 struct actionDetails {
90     QPointer<QAction> action;
91     QStringList tables;
92     int min{};
93     int max{};
94     int ranking{};
95     bool focus{};
96 };
97 class SKGMainPanelPrivate
98 {
99 public:
100     Ui::skgmainpanel_base ui{};
101     Ui::skgmainpanel_pref uipref{};
102 
103     SKGTabWidget* m_tabWidget;
104     QSplashScreen* m_splashScreen;
105 
106     SKGDocument* m_currentDocument;
107 
108     QList<SKGInterfacePlugin*> m_pluginsList;
109     QMap<QString, actionDetails > m_registeredGlogalAction;
110     QList<historyPage> m_historyClosedPages;
111 
112     QMenu* m_contextMenu;
113     QAction* m_actHideContextItem;
114     QAction* m_actShowAllContextItems;
115     QAction* m_actDelete;
116     QAction* m_closePageAction;
117     QAction* m_closeAllOtherPagesAction;
118     QAction* m_switchPinState;
119     QAction* m_saveDefaultStateAction;
120     QAction* m_resetDefaultStateAction;
121     QAction* m_overwriteBookmarkStateAction;
122     QAction* m_configureAction;
123     QAction* m_enableEditorAction;
124     QAction* m_fullScreenAction;
125     QAction* m_actLock;
126     QAction* m_actUnLock;
127     QAction* m_reopenLastClosed;
128     KStatusNotifierItem* m_kSystemTrayIcon;
129     QLabel* m_kNormalMessage;
130     SKGZoomSelector* m_zoomSelector;
131 
132     KToolBarPopupAction* m_previousAction;
133     KToolBarPopupAction* m_nextAction;
134     KToolBarPopupAction* m_buttonMenuAction;
135     KToggleAction* m_showMenuBarAction;
136 
137     QMenu* m_previousMenu;
138     QMenu* m_nextMenu;
139     QMenu* m_buttonMenu;
140     QWidget* m_mainWidget;
141     QVBoxLayout* m_mainLayout{};
142 
143     doublePointer m_progressObjects{};
144     bool m_middleClick;
145     bool m_saveOnClose;
146     QString m_fileName;
147     SKGWidget* m_widgetHavingSelection;
148     QStringList m_tipsOfTheDay;
149 
150 #ifdef KActivities_FOUND
151     KActivities::ResourceInstance* m_activityResourceInstance;
152 #endif
153 
154     static bool m_currentActionCanceled;
155     static SKGMainPanel* m_mainPanel;
156 
SKGMainPanelPrivate()157     SKGMainPanelPrivate()
158     {
159         m_actHideContextItem = nullptr; m_actShowAllContextItems = nullptr; m_actDelete = nullptr;
160         m_closePageAction = nullptr; m_closeAllOtherPagesAction = nullptr; m_switchPinState = nullptr; m_saveDefaultStateAction = nullptr;
161         m_resetDefaultStateAction = nullptr; m_overwriteBookmarkStateAction = nullptr; m_configureAction = nullptr; m_enableEditorAction = nullptr; m_fullScreenAction = nullptr; m_actLock = nullptr;
162         m_actUnLock = nullptr;
163         m_kSystemTrayIcon = nullptr;
164         m_kNormalMessage = nullptr; m_zoomSelector = nullptr;  m_previousAction = nullptr; m_nextAction = nullptr; m_buttonMenuAction = nullptr;
165         m_mainWidget = nullptr; m_mainLayout = nullptr; m_middleClick = false; m_saveOnClose = false;
166         m_widgetHavingSelection = nullptr;
167         m_tabWidget = nullptr; m_splashScreen = nullptr; m_currentDocument = nullptr;
168         m_contextMenu = nullptr;
169         m_reopenLastClosed = nullptr;
170         m_showMenuBarAction = nullptr;
171         m_previousMenu = nullptr;
172         m_nextMenu = nullptr;
173         m_buttonMenu = nullptr;
174         m_progressObjects.p1 = nullptr;
175         m_progressObjects.p2 = nullptr;
176 #ifdef KActivities_FOUND
177         m_activityResourceInstance = nullptr;
178 #endif
179     }
180 
progressBarCallBack(int iPos,qint64 iTime,const QString & iName,void * iProgressBar)181     static int progressBarCallBack(int iPos, qint64 iTime, const QString& iName, void* iProgressBar)
182     {
183         Q_UNUSED(iTime)
184         QProgressBar* progressBar = nullptr;
185         QPushButton* button = nullptr;
186         auto* pointers = static_cast<doublePointer*>(iProgressBar);
187         if (pointers != nullptr) {
188             progressBar = static_cast<QProgressBar*>(pointers->p1);
189             button = static_cast<QPushButton*>(pointers->p2);
190         }
191 
192         bool visible = (iPos > 0 && iPos <= 100);
193         if (progressBar != nullptr) {
194             QString commonFormat = QStringLiteral("%p%");
195             /*if (iPos > 0) {
196              *           qint64 estimatedTime = 100 * iTime / iPos;
197              *           qint64 remainingTime = estimatedTime - iTime;
198              *           commonFormat = commonFormat % " - " % i18nc("To print a remaining time (in seconde) / estimated time (in second)", "%1s / %2s",
199              *                          SKGServices::intToString(remainingTime / 1000),
200              *                          SKGServices::intToString(estimatedTime / 1000));
201             }*/
202             progressBar->setFormat(iName.isEmpty() ? commonFormat : commonFormat % '\n' % iName);
203             progressBar->setValue(iPos);
204             progressBar->setVisible(visible);
205             if (iPos == 100) {
206                 QTimer::singleShot(300, Qt::CoarseTimer, progressBar, &QProgressBar::hide);
207             }
208             progressBar->setToolTip(iName);
209 #ifdef SKG_DBUS
210             QDBusMessage message = QDBusMessage::createSignal(QLatin1String("/org/") + KAboutData::applicationData().componentName() + QLatin1String("/UnityLauncher"),
211                                    QStringLiteral("com.canonical.Unity.LauncherEntry"),
212                                    QStringLiteral("Update"));
213             message << "application://org.kde.skrooge.desktop";
214 
215             QVariantMap setProperty;
216             if (iPos == 100) {
217                 setProperty.insert(QStringLiteral("progress"), 0.0);
218                 setProperty.insert(QStringLiteral("progress-visible"), false);
219             } else {
220                 setProperty.insert(QStringLiteral("progress"), static_cast<double>(iPos / 100.0));
221                 setProperty.insert(QStringLiteral("progress-visible"), true);
222             }
223             message << setProperty;
224             QDBusConnection::sessionBus().send(message);
225 #endif
226         }
227         if (button != nullptr) {
228             button->setVisible(visible);
229             if (iPos == 100) {
230                 QTimer::singleShot(300, Qt::CoarseTimer, button, &QPushButton::hide);
231             }
232         }
233 
234 
235         SKGMainPanelPrivate::m_currentActionCanceled = false;
236         if (iPos != 0 && iPos != 100) {
237             qApp->processEvents(QEventLoop::AllEvents, 500);
238         }
239         return (SKGMainPanelPrivate::m_currentActionCanceled ? 1 : 0);
240     }
241 
adviceLessThan(const SKGAdvice & s1,const SKGAdvice & s2)242     static bool adviceLessThan(const SKGAdvice& s1, const SKGAdvice& s2)
243     {
244         if (s1.getPriority() == s2.getPriority()) {
245             return (s1.getShortMessage() > s2.getShortMessage());
246         }
247         return (s1.getPriority() > s2.getPriority());
248     }
249 
setAttribute(QDomElement & iRoot,const QString & iPath,const QString & iValue)250     static void setAttribute(QDomElement& iRoot, const QString& iPath, const QString& iValue)
251     {
252         int pos = iPath.indexOf('.');
253         if (pos == -1) {
254             iRoot.setAttribute(iPath, iValue);
255         } else {
256             QString newElementName = iPath.left(pos);
257             QString newAttribue = iPath.right(iPath.count() - pos - 1);
258 
259             QDomDocument doc(QStringLiteral("SKGML"));
260             doc.setContent(iRoot.attribute(newElementName));
261             QDomElement root = doc.documentElement();
262             if (root.isNull()) {
263                 root = doc.createElement(QStringLiteral("parameters"));
264                 doc.appendChild(root);
265             }
266             setAttribute(root, newAttribue, iValue);
267 
268             iRoot.setAttribute(newElementName, doc.toString());
269         }
270     }
271 
refreshTabPosition()272     void refreshTabPosition()
273     {
274         m_tabWidget->setTabPosition(static_cast<QTabWidget::TabPosition>(skgbasegui_settings::main_tabs_position()));
275     }
276 
rebuildSystemTray()277     void rebuildSystemTray()
278     {
279         if (skgbasegui_settings::icon_in_system_tray()) {
280             if (m_kSystemTrayIcon == nullptr) {
281                 m_kSystemTrayIcon = new KStatusNotifierItem(m_mainPanel);
282                 m_kSystemTrayIcon->setStandardActionsEnabled(true);
283                 m_kSystemTrayIcon->setAssociatedWidget(m_mainPanel);
284                 m_kSystemTrayIcon->setIconByPixmap(QApplication::windowIcon());
285             }
286         } else {
287             if (m_kSystemTrayIcon != nullptr) {
288                 delete m_kSystemTrayIcon;
289                 m_kSystemTrayIcon = nullptr;
290             }
291         }
292     }
293 };
294 
295 
296 bool SKGMainPanelPrivate::m_currentActionCanceled = false;
297 SKGMainPanel* SKGMainPanelPrivate::m_mainPanel = nullptr;
298 
SKGMainPanel(QSplashScreen * iSplashScreen,SKGDocument * iDocument)299 SKGMainPanel::SKGMainPanel(QSplashScreen* iSplashScreen, SKGDocument* iDocument)
300     :  d(new SKGMainPanelPrivate)
301 {
302     SKGTRACEINFUNC(1)
303     d->m_tabWidget = new SKGTabWidget(this);
304     d->m_splashScreen = iSplashScreen;
305     d->m_currentDocument = iDocument;
306 
307     setComponentName(QStringLiteral("skg"), KAboutData::applicationData().displayName());
308     setObjectName(qApp->applicationDisplayName());
309 
310     // Set main panel
311     SKGMainPanelPrivate::m_mainPanel = this;
312     auto w = new QScrollArea(this);
313     w->setFrameShape(QFrame::NoFrame);
314     w->setFocusPolicy(Qt::NoFocus);
315     d->m_mainLayout = new QVBoxLayout(w);
316     d->m_mainLayout->setSpacing(0);
317     d->m_mainLayout->setContentsMargins(0, 0, 0, 0);
318     d->m_mainLayout->addWidget(d->m_tabWidget);
319     d->ui.setupUi(this);
320 
321     // setMainWidget(new QLabel("hello"));
322 
323     // Initialize settings
324     KSharedConfigPtr config = KSharedConfig::openConfig();
325     KConfigGroup prefskg = config->group("Main Panel");
326     int option = prefskg.readEntry("update_modified_contexts", 100);
327     if (option == 100) {
328         // First call, we set default values
329         prefskg.writeEntry("update_modified_contexts", 2, KConfigBase::Normal);
330         // NEVER: set following setting
331         KMessageBox::saveDontShowAgainYesNo(QStringLiteral("updateContextOnClose"), KMessageBox::No);
332         SKGTRACEL(1) << "update_modified_contexts set to NEVER" << SKGENDL;
333         SKGTRACEL(1) << "updateContextOnClose set to No" << SKGENDL;
334     }
335 
336     option = prefskg.readEntry("update_modified_bookmarks", 100);
337     if (option == 100) {
338         // First call, we set default values
339         prefskg.writeEntry("update_modified_bookmarks", 2, KConfigBase::Normal);
340         // NEVER: set following setting
341         KMessageBox::saveDontShowAgainYesNo(QStringLiteral("updateBookmarkOnClose"), KMessageBox::No);
342         SKGTRACEL(1) << "update_modified_bookmarks set to NEVER" << SKGENDL;
343         SKGTRACEL(1) << "updateBookmarkOnClose set to No" << SKGENDL;
344     }
345 
346     // Search plugins
347     KService::List offers = KServiceTypeTrader::self()->query(QStringLiteral("SKG GUI/Plugin"));
348 
349     // Load plugins
350     int nb = offers.count();
351     SKGTRACEL(1) << nb << " plugins found" << SKGENDL;
352     if (d->m_splashScreen != nullptr) {
353         d->m_splashScreen->showMessage(i18nc("Splash screen message", "Loading plugins..."), Qt::AlignLeft, QColor(221, 130, 8));    // krazy:exclude=qmethods
354     }
355     SKGError err;
356     QStringList listAuthors;
357     QStringList listTasks;
358     QStringList listEmails;
359     QStringList listOscs;
360     for (int i = 0; i < nb; ++i) {
361         KService::Ptr service = offers.at(i);
362         QString name = service->name();
363         QString version = service->property(QStringLiteral("X-KDE-PluginInfo-Version"), QVariant::String).toString();
364         QString id = service->property(QStringLiteral("X-Krunner-ID"), QVariant::String).toString();
365         QString msg = i18nc("Splash screen message", "Loading plugin %1/%2: %3...", i + 1, nb, name);
366         SKGTRACEL(1) << msg << SKGENDL;
367         if (d->m_splashScreen != nullptr) {
368             d->m_splashScreen->showMessage(msg, Qt::AlignLeft, QColor(221, 130, 8));    // krazy:exclude=qmethods
369         }
370         KPluginLoader loader(service->library());
371         if (version.isEmpty() || SKGServices::stringToDouble(version) < 1) {
372             SKGTRACE << "WARNING: plugin [" << name << "] not loaded because of version too old (<1)" << SKGENDL;
373         } else {
374             KPluginFactory* factory2 = loader.factory();
375             if (factory2 != nullptr) {
376                 auto* pluginInterface = factory2->create<SKGInterfacePlugin> (this);
377                 if (pluginInterface != nullptr) {
378                     if (pluginInterface->isEnabled() && pluginInterface->setupActions(getDocument())) {
379                         // Add plugin in about
380                         QStringList listOfAuthors;
381                         QStringList listOfEmails;
382                         QStringList listOfPlugins;
383                         QString author = service->property(QStringLiteral("X-KDE-PluginInfo-Author"), QVariant::String).toString();
384                         QString email = service->property(QStringLiteral("X-KDE-PluginInfo-Email"), QVariant::String).toString();
385                         if (!author.isEmpty()) {
386                             listOfAuthors.push_back(author);
387                             listOfEmails.push_back(email);
388                             listOfPlugins.push_back(name);
389                         }
390                         const auto subPlugins = pluginInterface->subPlugins();
391                         for (const QString& subPlugin : subPlugins) {
392                             KService::List subOffers = KServiceTypeTrader::self()->query(subPlugin);
393                             int nbSubOffers = subOffers.count();
394                             for (int j = 0; j < nbSubOffers; ++j) {
395                                 KService::Ptr subService = subOffers.at(j);
396                                 QString author2 = subService->property(QStringLiteral("X-KDE-PluginInfo-Author"), QVariant::String).toString();
397                                 QString email2 = subService->property(QStringLiteral("X-KDE-PluginInfo-Email"), QVariant::String).toString();
398                                 if (!author2.isEmpty()) {
399                                     listOfAuthors.push_back(author2);
400                                     listOfEmails.push_back(email2);
401                                     listOfPlugins.push_back(subService->name());
402                                 }
403                             }
404                         }
405 
406                         int nbAuthors = listOfAuthors.count();
407                         for (int j = 0; j < nbAuthors; ++j) {
408                             QString authorId;
409                             QString author = listOfAuthors.at(j);
410                             QStringList authors = SKGServices::splitCSVLine(author, ',');
411                             if (authors.count() == 2) {
412                                 author = authors.at(0);
413                                 authorId = authors.at(1);
414                             }
415                             int pos2 = listAuthors.indexOf(author);
416                             if (pos2 == -1) {
417                                 listAuthors.push_back(author);
418                                 listTasks.push_back(i18n("Developer of plugin '%1'", listOfPlugins.at(j)));
419                                 listEmails.push_back(listOfEmails.at(j));
420                                 listOscs.push_back(authorId);
421                             } else {
422                                 listTasks[pos2] += i18n(", '%1'", listOfPlugins.at(j));
423                             }
424                         }
425 
426                         // Store plugin
427                         int nbplugin = d->m_pluginsList.count();
428                         int pos3 = nbplugin;
429                         for (int j = nbplugin - 1; j >= 0; --j) {
430                             if (pluginInterface->getOrder() < d->m_pluginsList.at(j)->getOrder()) {
431                                 pos3 = j;
432                             }
433                         }
434 
435                         d->m_pluginsList.insert(pos3, pluginInterface);
436                         pluginInterface->setObjectName(id);
437 
438                         // Add tips
439                         d->m_tipsOfTheDay += pluginInterface->tips();
440                     }
441                 }
442             } else {
443                 QString msg2 = i18nc("An information message",  "Loading plugin %1 failed because the factory could not be found in %2: %3", id, service->library(), loader.errorString());
444                 getDocument()->sendMessage(msg2);
445                 SKGTRACEL(1) << "WARNING:" << msg2 << SKGENDL;
446             }
447         }
448     }
449 
450     // Add credits
451     KAboutData about = KAboutData::applicationData();
452     nb = listAuthors.count();
453     for (int i = 0; i < nb; ++i) {
454         about.addCredit(listAuthors.at(i), listTasks.at(i), listEmails.at(i), QLatin1String(""), listOscs.at(i));
455     }
456     KAboutData::setApplicationData(about);
457 
458     // accept dnd
459     setAcceptDrops(true);
460 
461     // tell the KXmlGuiWindow that this is indeed the main widget
462     setCentralWidget(w);
463 
464     d->refreshTabPosition();
465     d->m_tabWidget->setMovable(true);
466     d->m_tabWidget->setTabsClosable(true);
467     d->m_tabWidget->setUsesScrollButtons(true);
468     d->m_tabWidget->tabBar()->setSelectionBehaviorOnRemove(QTabBar::SelectPreviousTab);
469     d->m_tabWidget->setDocumentMode(true);
470     d->m_tabWidget->show();
471 
472     // System tray
473     d->rebuildSystemTray();
474 
475     // then, setup our actions
476     setupActions();
477 
478     displayErrorMessage(err);
479 
480     // Add a status bar
481     statusBar()->show();
482 
483     QPalette palette2 = QApplication::palette();
484     palette2.setColor(QPalette::Base, Qt::transparent);
485     d->ui.kContextList->setPalette(palette2);
486 
487     QAction* toggle = d->ui.kDockContext->toggleViewAction();
488     QAction* panelAction = actionCollection()->addAction(QStringLiteral("view_context"));
489     registerGlobalAction(QStringLiteral("view_context"), panelAction);
490     panelAction->setCheckable(true);
491     panelAction->setChecked(toggle->isChecked());
492     panelAction->setText(toggle->text());
493     actionCollection()->setDefaultShortcut(panelAction, Qt::SHIFT + Qt::Key_F9);
494     connect(panelAction, &QAction::triggered, toggle, &QAction::trigger);
495     connect(toggle, &QAction::toggled, panelAction, &QAction::setChecked);
496 
497     QAction* toggle2 = d->ui.kDockMessages->toggleViewAction();
498     QAction* panelAction2 = actionCollection()->addAction(QStringLiteral("view_messages"));
499     registerGlobalAction(QStringLiteral("view_messages"), panelAction2);
500     panelAction2->setCheckable(true);
501     panelAction2->setChecked(toggle2->isChecked());
502     panelAction2->setText(toggle2->text());
503     actionCollection()->setDefaultShortcut(panelAction2, Qt::SHIFT + Qt::Key_F8);
504     connect(panelAction2, &QAction::triggered, toggle2, &QAction::trigger);
505     connect(toggle2, &QAction::toggled, panelAction2, &QAction::setChecked);
506 
507     auto contextMenu = new KSelectAction(SKGServices::fromTheme(QStringLiteral("tab-new")), i18nc("Noun", "Pages"), this);
508     registerGlobalAction(QStringLiteral("view_contextmenu"), contextMenu);
509 
510     // Add plugin in client in right order
511     QList<QListWidgetItem*> pageNotVisible;
512     KConfigGroup prefContextVisibility = config->group("Context Visibility");
513     int shortCutIndex = 0;
514     QString shortCutPrefix = QStringLiteral("Ctrl+");
515     int nbplugin = d->m_pluginsList.count();
516     QList<QAction*> contextActionList;
517     for (int j = 0; j < nbplugin; ++j) {
518         SKGInterfacePlugin* pluginInterface = d->m_pluginsList.at(j);
519         if (pluginInterface != nullptr) {
520             // Creation of the item
521             QString title = pluginInterface->title();
522             SKGTRACEL(1) << "Add plugin (" << pluginInterface->getOrder() << ") : " << title << SKGENDL;
523             if (!title.isEmpty()) {
524                 // Add menu item with shortcuts
525                 if (pluginInterface->isInPagesChooser()) {
526                     bool visible = prefContextVisibility.readEntry(pluginInterface->objectName(), true);
527                     QIcon icon = SKGServices::fromTheme(pluginInterface->icon());
528 
529                     auto contextItem = new QListWidgetItem(icon, title);
530                     contextItem->setStatusTip(pluginInterface->statusTip());
531                     contextItem->setToolTip(pluginInterface->toolTip());
532                     contextItem->setData(12, j);     // context item ==> plugin
533                     int page2 = d->ui.kContextList->count();
534                     pluginInterface->setProperty("contextItem", page2);  // plugin ==> context item
535 
536                     d->ui.kContextList->addItem(contextItem);
537 
538                     QAction* newAction = contextMenu->addAction(icon, title);
539                     if (newAction != nullptr) {
540                         newAction->setCheckable(false);
541                         newAction->setData(page2);
542                         contextItem->setData(15, QVariant::fromValue(static_cast<void*>(newAction)));     // context item ==> action
543                         if (!shortCutPrefix.isEmpty()) {
544                             ++shortCutIndex;
545                             if (shortCutIndex == 10) {
546                                 shortCutIndex = 0;
547                                 if (shortCutPrefix == QStringLiteral("Ctrl+")) {
548                                     shortCutPrefix += QStringLiteral("Alt+");
549                                 } else {
550                                     shortCutPrefix = QLatin1String("");
551                                 }
552                             }
553                             if (!shortCutPrefix.isEmpty()) {
554                                 actionCollection()->setDefaultShortcut(newAction, QString(shortCutPrefix % SKGServices::intToString(shortCutIndex)));
555                             }
556                         }
557                         connect(newAction, &QAction::triggered, this, &SKGMainPanel::onOpenContext);
558 
559                         contextActionList.append(newAction);
560 
561                         if (!visible) {
562                             pageNotVisible.push_back(contextItem);
563                         }
564 
565                         // Register action
566                         QString id = "page_" % pluginInterface->objectName();
567                         registerGlobalAction(id, newAction);
568                         registerGlobalAction(id, newAction);
569                     }
570                 }
571 
572                 // Create dock if needed
573                 QDockWidget* dockWidget = pluginInterface->getDockWidget();
574                 if (dockWidget != nullptr) {
575                     addDockWidget(Qt::LeftDockWidgetArea, dockWidget);
576                     tabifyDockWidget(d->ui.kDockContext, dockWidget);
577                     tabifyDockWidget(dockWidget, d->ui.kDockContext);
578                 }
579             }
580         }
581     }
582 
583     // Lock docs if needed
584     KConfigGroup pref = getMainConfigGroup();
585     if (pref.readEntry("docks_locked", false)) {
586         onLockDocks();
587     }
588 
589     if (!pref.readEntry("first_launch", false)) {
590         this->setWindowState(windowState() | Qt::WindowMaximized);
591         pref.writeEntry("first_launch", true);
592     }
593 
594     // a call to KXmlGuiWindow::setupGUI() populates the GUI
595     // with actions, using KXMLGUI.
596     // It also applies the saved mainwindow settings, if any, and ask the
597     // mainwindow to automatically save settings if changed: window size,
598     // toolbar position, icon size, etc.
599     setupGUI(Default, QStringLiteral("skgmainpanel.rc"));
600     plugActionList(QStringLiteral("context_actionlist"), contextActionList);
601 
602     for (int j = 0; j < nbplugin; ++j) {
603         SKGInterfacePlugin* pluginInterface = d->m_pluginsList.at(j);
604         if (pluginInterface != nullptr) {
605             QString title = pluginInterface->title();
606             SKGTRACEL(1) << "Add plugin client (" << pluginInterface->getOrder() << ") : " << title << SKGENDL;
607             guiFactory()->addClient(pluginInterface);
608         }
609     }
610 
611     // Hide items in context
612     nb = pageNotVisible.count();
613     for (int i = 0; i < nb; ++i) {
614         setContextVisibility(pageNotVisible.at(i), false);
615     }
616 
617     // Set status bar
618     d->m_kNormalMessage = new QLabel(this);
619     d->m_kNormalMessage->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred));
620 
621     auto kProgressBar = new QProgressBar(this);
622     kProgressBar->setObjectName(QStringLiteral("kProgressBar"));
623     kProgressBar->setValue(0);
624     kProgressBar->setMinimumSize(100, 20);
625     QSizePolicy sizePolicyProgessBar(QSizePolicy::Fixed, QSizePolicy::Expanding);
626     kProgressBar->setSizePolicy(sizePolicyProgessBar);
627     kProgressBar->setToolTip(i18nc("Widget description", "Progress of the current action"));
628     QFont f = kProgressBar->font();
629     f.setPointSize(6.0);
630     kProgressBar->setFont(f);
631     kProgressBar->setVisible(false);
632     kProgressBar->setAlignment(Qt::AlignTop | Qt::AlignHCenter);
633     kProgressBar->setRange(0, 100);
634 
635     d->m_zoomSelector = new SKGZoomSelector(this);
636     d->m_zoomSelector->setSizePolicy(sizePolicyProgessBar);
637     connect(d->m_zoomSelector, &SKGZoomSelector::changed, this, &SKGMainPanel::onZoomChanged);
638 
639     statusBar()->addWidget(d->m_kNormalMessage, 1);
640     statusBar()->addPermanentWidget(kProgressBar);
641 
642     // Set Cancel button
643     auto kCancelButton = new QPushButton();
644     kCancelButton->setObjectName(QStringLiteral("kCancelButton"));
645     kCancelButton->setIcon(SKGServices::fromTheme(QStringLiteral("media-playback-stop")));
646     kCancelButton->setToolTip(i18nc("Widget description", "Cancel the current action"));
647     kCancelButton->setStatusTip(i18nc("Widget description", "Cancel the current action"));
648     kCancelButton->setVisible(false);
649     connect(kCancelButton, &QPushButton::clicked, this, &SKGMainPanel::onCancelCurrentAction);
650 
651     //
652     d->ui.kClearMessageBtn->setIcon(SKGServices::fromTheme(QStringLiteral("edit-clear")));
653     connect(d->ui.kClearMessageBtn, &QPushButton::clicked, this, &SKGMainPanel::onClearMessages);
654 
655     // Add special button in toolbar
656     KToolBar* tb = toolBar();
657     if (tb != nullptr) {
658         auto label = new QLabel(this);
659         QSizePolicy sizePolicyLabel(QSizePolicy::Expanding, QSizePolicy::Preferred);
660         sizePolicyLabel.setHorizontalStretch(0);
661         sizePolicyLabel.setVerticalStretch(0);
662         sizePolicyLabel.setHeightForWidth(label->sizePolicy().hasHeightForWidth());
663         label->setSizePolicy(sizePolicyLabel);
664         tb->addWidget(label);
665         tb->addAction(d->m_buttonMenuAction);
666     }
667 
668     statusBar()->addPermanentWidget(kCancelButton);
669     statusBar()->addPermanentWidget(d->m_zoomSelector);
670 
671     // Set progress bar call back
672     if (getDocument() != nullptr) {
673         d->m_progressObjects.p1 = static_cast<void*>(kProgressBar);
674         d->m_progressObjects.p2 = static_cast<void*>(kCancelButton);
675         getDocument()->setProgressCallback(&SKGMainPanelPrivate::progressBarCallBack, static_cast<void*>(&d->m_progressObjects));
676     }
677 
678     // Connection
679     if (getDocument() != nullptr) {
680         connect(getDocument(), &SKGDocument::transactionSuccessfullyEnded, this, &SKGMainPanel::refresh, Qt::QueuedConnection);
681         connect(getDocument(), &SKGDocument::transactionSuccessfullyEnded, this, &SKGMainPanel::notify, Qt::QueuedConnection);
682     }
683     connect(d->ui.kContextList, &QListWidget::itemPressed, this, &SKGMainPanel::onBeforeOpenContext);
684     connect(d->ui.kContextList, &QListWidget::itemPressed, this, &SKGMainPanel::onOpenContext);
685     connect(d->m_tabWidget->tabBar(), &QTabBar::tabCloseRequested, this, &SKGMainPanel::closePageByIndex);
686     connect(d->m_tabWidget->tabBar(), &QTabBar::tabBarClicked, this, [ = ](int index) {
687         if ((QApplication::mouseButtons() & Qt::MidButton) != 0u) {
688             closePageByIndex(index);
689         }
690     });
691     connect(d->m_tabWidget->tabBar(), &QTabBar::tabBarDoubleClicked, this, &SKGMainPanel::addTab);
692     connect(d->m_tabWidget->tabBar(), &QTabBar::currentChanged, this, &SKGMainPanel::currentPageChanged);
693     connect(this, &SKGMainPanel::currentPageChanged, this, &SKGMainPanel::refresh);
694     connect(this, &SKGMainPanel::currentPageChanged, this, &SKGMainPanel::selectionChanged);
695 
696     // Refresh
697     refresh();
698 
699     d->ui.kContextList->installEventFilter(this);
700 
701     // Build contextual menu
702     d->ui.kContextList->setContextMenuPolicy(Qt::CustomContextMenu);
703 
704     d->m_contextMenu = new QMenu(d->ui.kContextList);
705     d->m_actHideContextItem = d->m_contextMenu->addAction(SKGServices::fromTheme(QStringLiteral("layer-visible-off")), i18nc("Verb", "Hide"));
706     connect(d->m_actHideContextItem, &QAction::triggered, this, &SKGMainPanel::onHideContextItem);
707 
708     d->m_actShowAllContextItems = d->m_contextMenu->addAction(SKGServices::fromTheme(QStringLiteral("layer-visible-on")), i18nc("Verb", "Show all"));
709     connect(d->m_actShowAllContextItems, &QAction::triggered, this, &SKGMainPanel::onShowAllContextItems);
710 
711     d->m_contextMenu->addSeparator();
712     d->m_contextMenu->addAction(getGlobalAction(QStringLiteral("tab_savedefaultstate")));
713     d->m_contextMenu->addAction(getGlobalAction(QStringLiteral("tab_resetdefaultstate")));
714 
715     connect(d->ui.kContextList, &QListWidget::customContextMenuRequested, this, &SKGMainPanel::showMenu);
716 
717 #ifdef KActivities_FOUND
718     // Initialize kactivities resource instance
719     d->m_activityResourceInstance = new KActivities::ResourceInstance(window()->winId());
720     d->m_activityResourceInstance->setParent(this);
721 #endif
722 
723     // Show menu bar and status bar
724     menuBar()->show();
725     statusBar()->show();
726 
727     notify();  // Due to sendMessage not in a transaction
728     d->m_splashScreen = nullptr;
729 }
730 
~SKGMainPanel()731 SKGMainPanel::~SKGMainPanel()
732 {
733     SKGTRACEINFUNC(1)
734     SKGMainPanelPrivate::m_mainPanel = nullptr;
735     disconnect(getDocument(), nullptr, this, nullptr);
736 
737     // close plugins
738     int nb = d->m_pluginsList.count();
739     for (int i = 0; i < nb; ++i) {
740         getPluginByIndex(i)->close();
741     }
742 
743     if (getDocument() != nullptr) {
744         getDocument()->close();
745     }
746     delete d;
747 }
748 
showMenu(const QPoint iPos)749 void SKGMainPanel::showMenu(const QPoint iPos)
750 {
751     if (d->m_contextMenu != nullptr) {
752         d->m_contextMenu->popup(d->ui.kContextList->mapToGlobal(iPos));
753     }
754 }
755 
setContextVisibility(QListWidgetItem * iItem,bool iVisibility)756 void SKGMainPanel::setContextVisibility(QListWidgetItem* iItem, bool iVisibility)
757 {
758     if (iItem != nullptr) {
759         // Hide item in context
760         iItem->setHidden(!iVisibility);
761 
762         // Hide corresponding action
763         QAction* act = static_cast< QAction* >(iItem->data(15).value<void*>());
764         if (act != nullptr) {
765             act->setVisible(iVisibility);
766         }
767 
768         // Save state in settings
769         SKGInterfacePlugin* plugin = getPluginByIndex(iItem->data(12).toInt());
770         if (plugin != nullptr) {
771             KSharedConfigPtr config = KSharedConfig::openConfig();
772             KConfigGroup pref = config->group("Context Visibility");
773             pref.writeEntry(plugin->objectName(), iVisibility);
774         }
775     }
776 }
777 
setContextVisibility(int iPage,bool iVisibility)778 void SKGMainPanel::setContextVisibility(int iPage, bool iVisibility)
779 {
780     setContextVisibility(d->ui.kContextList->item(iPage), iVisibility);
781 }
782 
onHideContextItem()783 void SKGMainPanel::onHideContextItem()
784 {
785     setContextVisibility(d->ui.kContextList->currentRow(), false);
786 }
787 
onShowAllContextItems()788 void SKGMainPanel::onShowAllContextItems()
789 {
790     int nb = d->ui.kContextList->count();
791     for (int i = 0; i < nb; ++i) {
792         setContextVisibility(i, true);
793     }
794 }
795 
getDocument() const796 SKGDocument* SKGMainPanel::getDocument() const
797 {
798     return d->m_currentDocument;
799 }
800 
processArguments(const QStringList & iArgument)801 QStringList SKGMainPanel::processArguments(const QStringList& iArgument)
802 {
803     QStringList output = iArgument;
804     for (auto plugin : qAsConst(d->m_pluginsList)) {
805         if (plugin != nullptr) {
806             output = plugin->processArguments(output);
807         }
808     }
809     return output;
810 }
811 
registerGlobalAction(const QString & iIdentifier,QAction * iAction,bool iAddInCollection,const QStringList & iListOfTable,int iMinSelection,int iMaxSelection,int iRanking,bool iSelectionMustHaveFocus)812 void SKGMainPanel::registerGlobalAction(const QString& iIdentifier, QAction* iAction, bool iAddInCollection,
813                                         const QStringList& iListOfTable, int iMinSelection, int iMaxSelection,
814                                         int iRanking, bool iSelectionMustHaveFocus)
815 {
816     if (iAction == nullptr) {
817         SKGTRACE << "WARNING: registerGlobalAction(" << iIdentifier << ",nullptr)" << SKGENDL;
818     } else {
819         QStringList keys = d->m_registeredGlogalAction.keys();
820         for (const auto& id : qAsConst(keys)) {
821             QPointer<QAction> act = d->m_registeredGlogalAction.value(id).action;
822             if ((act != nullptr) && iIdentifier != id && act != iAction && !act->shortcut().isEmpty() && act->shortcut() == iAction->shortcut()) {
823                 SKGTRACE << "WARNING: The actions [" << iAction->text() << " (" << iIdentifier << ")] and [" << act->text() << " (" << id << ")] has same shortcut [" << iAction->shortcut().toString() << "]" << SKGENDL;
824             }
825         }
826         actionDetails actDetails;
827         actDetails.action = iAction;
828         actDetails.tables = iListOfTable;
829         actDetails.min = iMinSelection;
830         actDetails.max = iMaxSelection;
831         actDetails.focus = iSelectionMustHaveFocus;
832         actDetails.ranking = (iRanking == -1 ? 10 * (d->m_registeredGlogalAction.count() + 1) : iRanking);
833         d->m_registeredGlogalAction[iIdentifier] = actDetails;
834 
835         // This connect has not been migrated on new connect mechanism to avoid crash when leaving the application
836         connect(iAction, SIGNAL(destroyed(QObject*)), this, SLOT(unRegisterGlobalAction(QObject*)));  // clazy:exclude=old-style-connect
837         if (iAddInCollection) {
838             QKeySequence shortCut = iAction->shortcut();
839             if (!shortCut.isEmpty()) {
840                 iAction->setShortcut(QKeySequence());
841             }
842             actionCollection()->addAction(iIdentifier, iAction);
843             if (!shortCut.isEmpty()) {
844                 actionCollection()->setDefaultShortcut(iAction, shortCut);
845             }
846         }
847     }
848 }
849 
unRegisterGlobalAction(QObject * iAction)850 void SKGMainPanel::unRegisterGlobalAction(QObject* iAction)
851 {
852     auto* act = qobject_cast< QAction* >(iAction);
853     if (act != nullptr) {
854         const auto keys = d->m_registeredGlogalAction.keys();
855         for (const auto& id : keys) {
856             if (d->m_registeredGlogalAction.value(id).action == QPointer<QAction>(act)) {
857                 d->m_registeredGlogalAction.remove(id);
858             }
859         }
860     }
861 }
862 
getGlobalAction(const QString & iIdentifier,bool iWarnIfNotExist)863 QPointer<QAction> SKGMainPanel::getGlobalAction(const QString& iIdentifier, bool iWarnIfNotExist)
864 {
865     QAction* act = d->m_registeredGlogalAction.value(iIdentifier).action;
866     if (act == nullptr && iWarnIfNotExist) {
867         SKGTRACE << "WARNING: getGlobalAction(" << iIdentifier << ")=nullptr" << SKGENDL;
868     }
869     return act;
870 }
871 
getActionsForContextualMenu(const QString & iTable)872 QList< QPointer< QAction > > SKGMainPanel::getActionsForContextualMenu(const QString& iTable)
873 {
874     // Filter action
875     QVector<actionDetails> tmp;
876     for (const auto& actDetails : qAsConst(d->m_registeredGlogalAction)) {
877         if (actDetails.ranking > 0 && actDetails.min > 0) {
878             if (actDetails.tables.isEmpty() || actDetails.tables.contains(iTable)) {
879                 tmp.push_back(actDetails);
880             } else if (actDetails.tables.count() == 1 && actDetails.tables.at(0).startsWith(QLatin1String("query:"))) {
881                 // Dynamic mode
882                 QStringList tmpListTable;
883                 getDocument()->getDistinctValues(QStringLiteral("sqlite_master"), QStringLiteral("name"), actDetails.tables.at(0).right(actDetails.tables.at(0).count() - 6), tmpListTable);
884                 if (tmpListTable.contains(iTable)) {
885                     tmp.push_back(actDetails);
886                 }
887             }
888         }
889     }
890 
891     // Sort
892     std::sort(tmp.begin(), tmp.end(), [&](const actionDetails & a, const actionDetails & b) {
893         return a.ranking < b.ranking;
894     });
895 
896     // Generate output
897     int previousGroup = -1;
898     QList< QPointer< QAction > > output;
899     output.reserve(tmp.count());
900     for (const auto& actDetails : qAsConst(tmp)) {
901         int currentGroup = (actDetails.ranking) / 100;
902         if (currentGroup != previousGroup) {
903             output.push_back(nullptr);
904             previousGroup = currentGroup;
905         }
906         output.push_back(actDetails.action);
907     }
908     return output;
909 }
910 
getGlobalActions() const911 QMap<QString, QPointer<QAction> > SKGMainPanel::getGlobalActions() const
912 {
913     QMap<QString, QPointer<QAction> > map;
914     const auto keys = d->m_registeredGlogalAction.keys();
915     for (const auto& id : keys) {
916         map[id] = d->m_registeredGlogalAction[id].action;
917     }
918     return map;
919 }
920 
getPluginByIndex(int iIndex)921 SKGInterfacePlugin* SKGMainPanel::getPluginByIndex(int iIndex)
922 {
923     SKGInterfacePlugin* output = nullptr;
924     if (iIndex >= 0 && iIndex < d->m_pluginsList.count()) {
925         output = d->m_pluginsList.value(iIndex);
926     }
927 
928     return output;
929 }
930 
getPluginByName(const QString & iName)931 SKGInterfacePlugin* SKGMainPanel::getPluginByName(const QString& iName)
932 {
933     SKGInterfacePlugin* output = nullptr;
934     int nbplugin = d->m_pluginsList.count();
935     QString name = iName.toLower();
936     for (int j = 0; (output == nullptr) && j < nbplugin; ++j) {
937         QString namep = d->m_pluginsList.at(j)->objectName().toLower();
938         if (namep == name || namep.replace(' ', '_') == name) {
939             output = d->m_pluginsList.at(j);
940         }
941     }
942 
943     return output;
944 }
945 
setupActions()946 void SKGMainPanel::setupActions()
947 {
948     SKGTRACEINFUNC(1)
949 
950     // Std File
951     KStandardAction::quit(this, SLOT(onQuitAction()), actionCollection());
952     KStandardAction::configureNotifications(this, SLOT(onConfigureNotifications()), actionCollection());
953 
954     // Preferences
955     KStandardAction::preferences(this, SLOT(optionsPreferences()), actionCollection());
956 
957     // New Tab
958     auto actAddTab = new QAction(SKGServices::fromTheme(QStringLiteral("tab-new-background")), i18nc("Noun, user action", "New Tab"), this);
959     actionCollection()->setDefaultShortcut(actAddTab, Qt::CTRL + Qt::SHIFT + Qt::Key_W);
960     connect(actAddTab, &QAction::triggered, this, &SKGMainPanel::addTab);
961     registerGlobalAction(QStringLiteral("new_tab"), actAddTab, true, QStringList(), -1);
962 
963     // Add new tab widget
964     auto addTabButton = new QToolButton(this);
965     addTabButton->setIcon(actAddTab->icon());
966     addTabButton->setAutoRaise(true);
967     addTabButton->raise();
968     addTabButton->setDefaultAction(actAddTab);
969     addTabButton->setToolButtonStyle(Qt::ToolButtonIconOnly);
970     addTabButton->setFocusPolicy(Qt::NoFocus);
971     d->m_tabWidget->setCornerWidget(addTabButton);
972 
973     d->m_actLock = new QAction(SKGServices::fromTheme(QStringLiteral("document-encrypt")), i18nc("Verb", "Lock panels"), this);
974     connect(d->m_actLock, &QAction::triggered, this, &SKGMainPanel::onLockDocks);
975     registerGlobalAction(QStringLiteral("view_lock"), d->m_actLock);
976 
977     d->m_actUnLock = new QAction(SKGServices::fromTheme(QStringLiteral("document-decrypt")), i18nc("Verb", "Unlock panels"), this);
978     connect(d->m_actUnLock, &QAction::triggered, this, &SKGMainPanel::onUnlockDocks);
979     registerGlobalAction(QStringLiteral("view_unlock"), d->m_actUnLock);
980 
981     d->m_switchPinState = new QAction(SKGServices::fromTheme(QStringLiteral("document-encrypt")), i18nc("Noun, user action", "Pin this page"), this);
982     connect(d->m_switchPinState, &QAction::triggered, this, [ = ] {this->switchPinPage(nullptr);});
983     registerGlobalAction(QStringLiteral("tab_switchpin"), d->m_switchPinState);
984 
985     //
986     d->m_closePageAction = actionCollection()->addAction(KStandardAction::Close, QStringLiteral("tab_close"), this, SLOT(closeCurrentPage()));
987     registerGlobalAction(QStringLiteral("tab_close"), d->m_closePageAction);
988 
989     //
990     auto actCloseAllPages = new QAction(SKGServices::fromTheme(QStringLiteral("window-close")), i18nc("Noun, user action", "Close All"), this);
991     actionCollection()->setDefaultShortcut(actCloseAllPages, Qt::ALT + Qt::Key_W);
992     connect(actCloseAllPages, &QAction::triggered, this, &SKGMainPanel::closeAllPages);
993     registerGlobalAction(QStringLiteral("tab_closeall"), actCloseAllPages, true, QStringList(), -1);
994 
995     //
996     d->m_closeAllOtherPagesAction = new QAction(SKGServices::fromTheme(QStringLiteral("window-close")), i18nc("Noun, user action", "Close All Other"), this);
997     actionCollection()->setDefaultShortcut(d->m_closeAllOtherPagesAction, Qt::CTRL + Qt::ALT + Qt::Key_W);
998     connect(d->m_closeAllOtherPagesAction, &QAction::triggered, this, [ = ] {this->closeAllOtherPages(nullptr);});
999     registerGlobalAction(QStringLiteral("tab_closeallother"), d->m_closeAllOtherPagesAction);
1000 
1001     //
1002     d->m_saveDefaultStateAction = new QAction(SKGServices::fromTheme(QStringLiteral("document-save")), i18nc("Noun, user action", "Save page state"), this);
1003     actionCollection()->setDefaultShortcut(d->m_saveDefaultStateAction, Qt::CTRL + Qt::ALT + Qt::Key_S);
1004     connect(d->m_saveDefaultStateAction, &QAction::triggered, this, &SKGMainPanel::saveDefaultState);
1005     registerGlobalAction(QStringLiteral("tab_savedefaultstate"), d->m_saveDefaultStateAction);
1006 
1007     //
1008     d->m_resetDefaultStateAction = new QAction(SKGServices::fromTheme(QStringLiteral("edit-clear")), i18nc("Noun, user action", "Reset page state"), this);
1009     actionCollection()->setDefaultShortcut(d->m_resetDefaultStateAction, Qt::CTRL + Qt::ALT + Qt::Key_R);
1010     connect(d->m_resetDefaultStateAction, &QAction::triggered, this, &SKGMainPanel::resetDefaultState);
1011     registerGlobalAction(QStringLiteral("tab_resetdefaultstate"), d->m_resetDefaultStateAction);
1012 
1013     //
1014     d->m_reopenLastClosed = new QAction(SKGServices::fromTheme(QStringLiteral("tab-new")), i18nc("Noun, user action", "Reopen last page closed"), this);
1015     actionCollection()->setDefaultShortcut(d->m_reopenLastClosed, Qt::CTRL + Qt::ALT + Qt::Key_T);
1016     connect(d->m_reopenLastClosed, &QAction::triggered, this, &SKGMainPanel::onReopenLastClosed);
1017     registerGlobalAction(QStringLiteral("tab_reopenlastclosed"), d->m_reopenLastClosed);
1018 
1019     //
1020     QStringList overlay;
1021     overlay.push_back(QStringLiteral("bookmarks"));
1022     d->m_overwriteBookmarkStateAction = new QAction(SKGServices::fromTheme(QStringLiteral("document-save"), overlay), i18nc("Noun, user action", "Overwrite bookmark state"), this);
1023     connect(d->m_overwriteBookmarkStateAction, &QAction::triggered, this, &SKGMainPanel::overwriteBookmarkState);
1024     actionCollection()->setDefaultShortcut(d->m_overwriteBookmarkStateAction, Qt::CTRL + Qt::ALT + Qt::Key_B);
1025     registerGlobalAction(QStringLiteral("tab_overwritebookmark"), d->m_overwriteBookmarkStateAction);
1026 
1027     //
1028     d->m_configureAction = new QAction(SKGServices::fromTheme(QStringLiteral("configure")), i18nc("Noun, user action", "Configure..."), this);
1029     connect(d->m_configureAction, &QAction::triggered, this, [ = ] {this->optionsPreferences();});
1030     registerGlobalAction(QStringLiteral("tab_configure"), d->m_configureAction);
1031 
1032     // Menu
1033     d->m_buttonMenuAction = new KToolBarPopupAction(SKGServices::fromTheme(QStringLiteral("configure")), QLatin1String(""), this);
1034     d->m_buttonMenuAction->setToolTip(i18nc("Noun, user action", "Menu"));
1035     d->m_buttonMenu = d->m_buttonMenuAction->menu();
1036     connect(d->m_buttonMenu, &QMenu::aboutToShow, this, &SKGMainPanel::onShowButtonMenu);
1037     d->m_buttonMenuAction->setDelayed(false);
1038     registerGlobalAction(QStringLiteral("view_menu"), d->m_buttonMenuAction);
1039 
1040     d->m_showMenuBarAction = KStandardAction::showMenubar(this, SLOT(onShowMenuBar()), actionCollection());
1041     KConfigGroup pref = getMainConfigGroup();
1042     d->m_showMenuBarAction->setChecked(pref.readEntry("menubar_shown", true));
1043     QTimer::singleShot(200, Qt::CoarseTimer, this, &SKGMainPanel::onShowMenuBar);
1044     registerGlobalAction(QStringLiteral("options_show_menubar"), d->m_showMenuBarAction);
1045 
1046     //
1047     d->m_previousAction = new KToolBarPopupAction(SKGServices::fromTheme(QStringLiteral("go-previous")), i18nc("Noun, user action", "Previous"), this);
1048     connect(d->m_previousAction, &QAction::triggered, this, &SKGMainPanel::onPrevious);
1049     actionCollection()->setDefaultShortcut(d->m_previousAction, Qt::ALT + Qt::Key_Left);
1050     d->m_previousAction->setPriority(QAction::LowPriority);
1051     d->m_previousMenu = d->m_previousAction->menu();
1052     connect(d->m_previousMenu, &QMenu::aboutToShow, this, &SKGMainPanel::onShowPreviousMenu);
1053     d->m_previousAction->setStickyMenu(false);
1054     d->m_previousAction->setData(0);
1055     registerGlobalAction(QStringLiteral("go_previous"), d->m_previousAction);
1056 
1057     //
1058     d->m_nextAction = new KToolBarPopupAction(SKGServices::fromTheme(QStringLiteral("go-next")), i18nc("Noun, user action", "Next"), this);
1059     connect(d->m_nextAction, &QAction::triggered, this, &SKGMainPanel::onNext);
1060     actionCollection()->setDefaultShortcut(d->m_nextAction, Qt::ALT + Qt::Key_Right);
1061     d->m_nextAction->setPriority(QAction::LowPriority);
1062     d->m_nextMenu = d->m_nextAction->menu();
1063     connect(d->m_nextMenu, &QMenu::aboutToShow, this, &SKGMainPanel::onShowNextMenu);
1064     d->m_nextAction->setStickyMenu(false);
1065     d->m_nextAction->setData(0);
1066     registerGlobalAction(QStringLiteral("go_next"), d->m_nextAction);
1067 
1068     //
1069     d->m_fullScreenAction = actionCollection()->addAction(KStandardAction::FullScreen, QStringLiteral("fullscreen"), this, SLOT(onFullScreen()));
1070     registerGlobalAction(QStringLiteral("fullscreen"), d->m_fullScreenAction);
1071 
1072     //
1073     d->m_enableEditorAction = new QAction(SKGServices::fromTheme(QStringLiteral("appointment-new")), i18nc("Noun, user action", "Enable editor"), this);
1074     actionCollection()->setDefaultShortcut(d->m_enableEditorAction, Qt::CTRL + Qt::Key_Insert);
1075     connect(d->m_enableEditorAction, &QAction::triggered, this, &SKGMainPanel::enableEditor);
1076     registerGlobalAction(QStringLiteral("enable_editor"), d->m_enableEditorAction);
1077 
1078     //
1079     auto migrateSQLCipher = new QAction(SKGServices::fromTheme(QStringLiteral("run-build")), i18nc("Noun, user action", "Migrate to SQLCipher format"), this);
1080     connect(migrateSQLCipher, &QAction::triggered, this, &SKGMainPanel::onMigrateToSQLCipher);
1081     registerGlobalAction(QStringLiteral("migrate_sqlcipher"), migrateSQLCipher);
1082 
1083     // Contextual menu
1084     d->m_tabWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
1085     d->m_tabWidget->insertAction(nullptr, getGlobalAction(QStringLiteral("new_tab")));
1086     d->m_tabWidget->insertAction(nullptr, getGlobalAction(QStringLiteral("tab_reopenlastclosed")));
1087     d->m_tabWidget->insertAction(nullptr, getGlobalAction(QStringLiteral("tab_close")));
1088     d->m_tabWidget->insertAction(nullptr, getGlobalAction(QStringLiteral("tab_closeall")));
1089     d->m_tabWidget->insertAction(nullptr, getGlobalAction(QStringLiteral("tab_closeallother")));
1090     {
1091         auto sep = new QAction(this);
1092         sep->setSeparator(true);
1093         d->m_tabWidget->insertAction(nullptr, sep);
1094     }
1095     d->m_tabWidget->insertAction(nullptr, getGlobalAction(QStringLiteral("tab_switchpin")));
1096     d->m_tabWidget->insertAction(nullptr, getGlobalAction(QStringLiteral("tab_resetdefaultstate")));
1097     d->m_tabWidget->insertAction(nullptr, getGlobalAction(QStringLiteral("tab_savedefaultstate")));
1098     d->m_tabWidget->insertAction(nullptr, getGlobalAction(QStringLiteral("tab_overwritebookmark")));
1099     d->m_tabWidget->insertAction(nullptr, getGlobalAction(QStringLiteral("tab_configure")));
1100     {
1101         auto sep = new QAction(this);
1102         sep->setSeparator(true);
1103         d->m_tabWidget->insertAction(nullptr, sep);
1104     }
1105     d->m_tabWidget->insertAction(nullptr, getGlobalAction(QStringLiteral("fullscreen")));
1106 }
1107 
enableEditor()1108 void SKGMainPanel::enableEditor()
1109 {
1110     SKGTabPage* cPage = currentPage();
1111     if (cPage != nullptr) {
1112         cPage->activateEditor();
1113     }
1114 }
1115 
onPrevious()1116 void SKGMainPanel::onPrevious()
1117 {
1118     SKGError err;
1119     SKGTRACEINFUNCRC(10, err)
1120 
1121     SKGTabPage* cPage = currentPage();
1122     if (cPage != nullptr) {
1123         // Get index in history of page to refresh
1124         int indexPrevious = qobject_cast<QAction*>(sender())->data().toInt();
1125 
1126         // Get lists
1127         SKGTabPage::SKGPageHistoryItemList listPrevious = cPage->getPreviousPages();
1128         if (indexPrevious < listPrevious.count()) {
1129             SKGTabPage::SKGPageHistoryItemList listNext = cPage->getNextPages();
1130             SKGTabPage::SKGPageHistoryItem current = currentPageHistoryItem();
1131 
1132             // Get item to refresh
1133             SKGTabPage::SKGPageHistoryItem item = listPrevious.at(indexPrevious);
1134 
1135             // Open page
1136             cPage = openPage(getPluginByName(item.plugin), currentPageIndex(), item.state, item.name, item.bookmarkID);
1137             if (cPage != nullptr) {
1138                 cPage->setBookmarkID(item.bookmarkID);
1139 
1140                 // Update lists
1141                 listNext.insert(0, current);
1142                 listPrevious.removeAt(indexPrevious);
1143                 for (int i = 0; i < indexPrevious; ++i) {
1144                     SKGTabPage::SKGPageHistoryItem itemPrevious = listPrevious.at(0);  // Because the list is modified
1145                     listNext.insert(0, itemPrevious);
1146                     listPrevious.removeAt(0);
1147                 }
1148 
1149                 // Set lists
1150                 cPage->setPreviousPages(listPrevious);
1151                 cPage->setNextPages(listNext);
1152             }
1153 
1154             refresh();
1155         }
1156     }
1157 }
1158 
onShowPreviousMenu()1159 void SKGMainPanel::onShowPreviousMenu()
1160 {
1161     if (d->m_previousMenu != nullptr) {
1162         d->m_previousMenu->clear();
1163 
1164         SKGTabPage* cPage = currentPage();
1165         if (cPage != nullptr) {
1166             SKGTabPage::SKGPageHistoryItemList list = cPage->getPreviousPages();
1167             int nb = list.count();
1168             for (int i = 0; i < nb; ++i) {
1169                 QAction* act = d->m_previousMenu->addAction(SKGServices::fromTheme(list.at(i).icon), list.at(i).name);
1170                 if (act != nullptr) {
1171                     act->setData(i);
1172                     connect(act, &QAction::triggered, this, &SKGMainPanel::onPrevious);
1173                 }
1174             }
1175         }
1176     }
1177 }
1178 
onShowMenuBar()1179 void SKGMainPanel::onShowMenuBar()
1180 {
1181     bool test = d->m_showMenuBarAction->isChecked();
1182     menuBar()->setVisible(test);
1183     d->m_buttonMenuAction->setVisible(!test);
1184 
1185     KConfigGroup pref = getMainConfigGroup();
1186     pref.writeEntry("menubar_shown", test);
1187 }
1188 
onFullScreen()1189 void SKGMainPanel::onFullScreen()
1190 {
1191     auto* p = d->m_tabWidget;
1192     if (p != nullptr) {
1193         if (!d->m_fullScreenAction->isChecked()) {
1194             // No Full screen
1195             p->setWindowState(p->windowState() & ~Qt::WindowFullScreen);   // reset
1196             d->m_mainLayout->addWidget(d->m_tabWidget);
1197         } else {
1198             bool atLeastOnePageOpened = (d->m_tabWidget->count() > 0);
1199             if (atLeastOnePageOpened) {
1200                 // Activate Full screen mode
1201                 p->setParent(nullptr);
1202                 p->setWindowFlags(p->windowFlags() | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint);
1203                 p->setWindowState(p->windowState() | Qt::WindowFullScreen);   // set
1204                 p->show();
1205 
1206                 displayMessage(i18nc("Information message", "You can exit full screen mode with %1 or with the contextual menu", d->m_fullScreenAction->shortcut().toString()));
1207             } else {
1208                 d->m_fullScreenAction->setChecked(false);
1209                 displayMessage(i18nc("Information message", "At least one page must be opened to enable full screen mode"), SKGDocument::Error);
1210             }
1211         }
1212     }
1213 }
1214 
onNext()1215 void SKGMainPanel::onNext()
1216 {
1217     SKGError err;
1218     SKGTRACEINFUNCRC(10, err)
1219 
1220     SKGTabPage* cPage = currentPage();
1221     if (cPage != nullptr) {
1222         // Get index in history of page to refresh
1223         int posNext = qobject_cast<QAction*>(sender())->data().toInt();
1224 
1225         // Get lists
1226         SKGTabPage::SKGPageHistoryItemList listPrevious = cPage->getPreviousPages();
1227         SKGTabPage::SKGPageHistoryItemList listNext = cPage->getNextPages();
1228         SKGTabPage::SKGPageHistoryItem current = currentPageHistoryItem();
1229 
1230         // Get item to refresh
1231         SKGTabPage::SKGPageHistoryItem item = listNext.at(posNext);
1232 
1233         // Open page
1234         cPage = openPage(getPluginByName(item.plugin), currentPageIndex(), item.state, item.name, item.bookmarkID);
1235         if (cPage != nullptr) {
1236             cPage->setBookmarkID(item.bookmarkID);
1237 
1238             // Update lists
1239             listPrevious.insert(0, current);
1240             listNext.removeAt(posNext);
1241             for (int i = 0; i < posNext; ++i) {
1242                 SKGTabPage::SKGPageHistoryItem itemNext = listNext.at(0);  // Because the list is modified
1243                 listPrevious.insert(0, itemNext);
1244                 listNext.removeAt(0);
1245             }
1246 
1247             // Set lists
1248             cPage->setPreviousPages(listPrevious);
1249             cPage->setNextPages(listNext);
1250         }
1251 
1252         refresh();
1253     }
1254 }
1255 
onReopenLastClosed()1256 void SKGMainPanel::onReopenLastClosed()
1257 {
1258     SKGError err;
1259     SKGTRACEINFUNCRC(10, err)
1260 
1261     SKGTabPage::SKGPageHistoryItem current = currentPageHistoryItem();
1262 
1263     // Get item to refresh
1264     historyPage item = d->m_historyClosedPages.takeLast();
1265 
1266     // Open page
1267     SKGTabPage* cPage = openPage(getPluginByName(item.current.plugin), -1, item.current.state, item.current.name, item.current.bookmarkID);
1268     if (cPage != nullptr) {
1269         cPage->setBookmarkID(item.current.bookmarkID);
1270         cPage->setNextPages(item.next);
1271         cPage->setPreviousPages(item.previous);
1272     }
1273 
1274     refresh();
1275 }
1276 
onLockDocks()1277 void SKGMainPanel::onLockDocks()
1278 {
1279     QObjectList cs = children();
1280     for (auto c : qAsConst(cs)) {
1281         auto* doc = qobject_cast<QDockWidget*>(c);
1282         if (doc != nullptr) {
1283             doc->setFeatures(QDockWidget::NoDockWidgetFeatures);
1284         }
1285     }
1286 
1287     KConfigGroup pref = getMainConfigGroup();
1288     pref.writeEntry("docks_locked", true);
1289 
1290     refresh();
1291 }
1292 
onUnlockDocks()1293 void SKGMainPanel::onUnlockDocks()
1294 {
1295     QObjectList cs = children();
1296     for (auto c : qAsConst(cs)) {
1297         auto* doc = qobject_cast<QDockWidget*>(c);
1298         if (doc != nullptr) {
1299             doc->setFeatures(QDockWidget::AllDockWidgetFeatures);
1300         }
1301     }
1302 
1303     KConfigGroup pref = getMainConfigGroup();
1304     pref.writeEntry("docks_locked", false);
1305 
1306     refresh();
1307 }
1308 
onConfigureNotifications()1309 void SKGMainPanel::onConfigureNotifications()
1310 {
1311     KNotifyConfigWidget::configure(this);
1312 }
1313 
onShowNextMenu()1314 void SKGMainPanel::onShowNextMenu()
1315 {
1316     if (d->m_nextMenu != nullptr) {
1317         d->m_nextMenu->clear();
1318 
1319         SKGTabPage* cPage = currentPage();
1320         if (cPage != nullptr) {
1321             SKGTabPage::SKGPageHistoryItemList list = cPage->getNextPages();
1322             int nb = list.count();
1323             for (int i = 0; i < nb; ++i) {
1324                 QAction* act = d->m_nextMenu->addAction(SKGServices::fromTheme(list.at(i).icon), list.at(i).name);
1325                 if (act != nullptr) {
1326                     act->setData(i);
1327                     connect(act, &QAction::triggered, this, &SKGMainPanel::onNext);
1328                 }
1329             }
1330         }
1331     }
1332 }
1333 
onShowButtonMenu()1334 void SKGMainPanel::onShowButtonMenu()
1335 {
1336     if (d->m_buttonMenu != nullptr) {
1337         d->m_buttonMenu->clear();
1338 
1339         QMenuBar* mb = menuBar();
1340         if (mb != nullptr) {
1341             d->m_buttonMenu->addActions(mb->actions());
1342         }
1343     }
1344 }
1345 
queryClose()1346 bool SKGMainPanel::queryClose()
1347 {
1348     SKGTRACEINFUNC(1)
1349     // Bug 2777697: To be sure that all page modifications are closed
1350     closeAllPages();
1351     // Bug 2777697:
1352 
1353     bool output = queryFileClose();
1354 
1355     // To be sure that the application is not closed in fullscreen mode
1356     if (output) {
1357         if (d->m_fullScreenAction->isChecked()) {
1358             d->m_fullScreenAction->trigger();
1359         }
1360     }
1361 
1362     return output;
1363 }
1364 
setSaveOnClose(bool iSaveOnClose)1365 void SKGMainPanel::setSaveOnClose(bool iSaveOnClose)
1366 {
1367     d->m_saveOnClose = iSaveOnClose;
1368 }
1369 
queryFileClose()1370 bool SKGMainPanel::queryFileClose()
1371 {
1372     SKGTRACEINFUNC(1)
1373     bool output = true;
1374     if (getDocument()->getCurrentTransaction() != 0) {
1375         displayMessage(i18nc("skgtestimportskg", "The application cannot be closed when an operation is running."), SKGDocument::Error);
1376         output = false;
1377     } else if (getDocument()->isFileModified()) {
1378         QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
1379         int code = KMessageBox::Yes;
1380         QString fileName = getDocument()->getCurrentFileName();
1381         QAction* save = getGlobalAction(fileName.isEmpty() ? QStringLiteral("file_save_as") : QStringLiteral("file_save"));
1382         if (save != nullptr) {
1383             if (!d->m_saveOnClose) {
1384                 code = KMessageBox::questionYesNoCancel(this, i18nc("Question", "The document has been modified.\nDo you want to save it before closing?"),
1385                                                         QString(),
1386                                                         KGuiItem(fileName.isEmpty() ?  i18nc("Question", "Save as") : i18nc("Question", "Save"),
1387                                                                 SKGServices::fromTheme(fileName.isEmpty() ?  QStringLiteral("document-save-as") : QStringLiteral("document-save"))),
1388                                                         KGuiItem(i18nc("Question", "Do not save")));
1389             }
1390             if (code == KMessageBox::Yes) {
1391                 save->trigger();
1392             }
1393             output = (code == KMessageBox::No || code == KMessageBox::Yes);
1394         } else {
1395             code = KMessageBox::questionYesNo(this, i18nc("Question", "Current modifications will not be saved.\nDo you want to continue?"));
1396             output = (code == KMessageBox::Yes);
1397         }
1398         QApplication::restoreOverrideCursor();
1399     }
1400 
1401     return output;
1402 }
1403 
getFirstSelectedObject() const1404 SKGObjectBase SKGMainPanel::getFirstSelectedObject() const
1405 {
1406     SKGObjectBase selection;
1407     SKGWidget* cPage = (d->m_widgetHavingSelection != nullptr ? d->m_widgetHavingSelection : currentPage());
1408     if (cPage != nullptr) {
1409         selection = cPage->getFirstSelectedObject();
1410     }
1411     return selection;
1412 }
1413 
getSelectedObjects() const1414 SKGObjectBase::SKGListSKGObjectBase SKGMainPanel::getSelectedObjects() const
1415 {
1416     SKGObjectBase::SKGListSKGObjectBase selection;
1417     SKGWidget* cPage = (d->m_widgetHavingSelection != nullptr ? d->m_widgetHavingSelection : currentPage());
1418     if (cPage != nullptr) {
1419         selection = cPage->getSelectedObjects();
1420     }
1421     return selection;
1422 }
1423 
getNbSelectedObjects() const1424 int SKGMainPanel::getNbSelectedObjects() const
1425 {
1426     int nb = 0;
1427     SKGWidget* cPage = (d->m_widgetHavingSelection != nullptr ? d->m_widgetHavingSelection : currentPage());
1428     if (cPage != nullptr) {
1429         nb = cPage->getNbSelectedObjects();
1430     }
1431     return nb;
1432 }
1433 
hasSelectionWithFocus()1434 bool SKGMainPanel::hasSelectionWithFocus()
1435 {
1436     bool output = false;
1437     SKGWidget* cPage = (d->m_widgetHavingSelection != nullptr ? d->m_widgetHavingSelection : currentPage());
1438     if (cPage != nullptr) {
1439         output = cPage->hasSelectionWithFocus();
1440     }
1441     return output;
1442 }
1443 
getAdvice() const1444 SKGAdviceList SKGMainPanel::getAdvice() const
1445 {
1446     SKGTRACEINFUNC(1)
1447     // Get list of ignored advice
1448     QString currentMonth = QDate::currentDate().toString(QStringLiteral("yyyy-MM"));
1449     QStringList ignoredAdvice = getDocument()->getParameters(QStringLiteral("advice"), "t_value='I' OR t_value='I_" % currentMonth % '\'');
1450 
1451     // Build the list of all advice by requesting all plugins
1452     SKGAdviceList globalAdviceList;
1453     int index = 0;
1454     while (index >= 0) {
1455         SKGInterfacePlugin* plugin = SKGMainPanel::getMainPanel()->getPluginByIndex(index);
1456         if (plugin != nullptr) {
1457             const auto list = plugin->advice(ignoredAdvice);
1458             for (const auto& ad : list) {
1459                 if (!ignoredAdvice.contains(ad.getUUID()) && !ignoredAdvice.contains(SKGServices::splitCSVLine(ad.getUUID(), '|').at(0))) {
1460                     globalAdviceList.push_back(ad);
1461                 }
1462             }
1463         } else {
1464             index = -2;
1465         }
1466         ++index;
1467     }
1468     std::sort(globalAdviceList.begin(), globalAdviceList.end(), SKGMainPanelPrivate::adviceLessThan);
1469 
1470     return globalAdviceList;
1471 }
1472 
getMainConfigGroup()1473 KConfigGroup SKGMainPanel::getMainConfigGroup()
1474 {
1475     KSharedConfigPtr config = KSharedConfig::openConfig();
1476     return config->group("Main Panel");
1477 }
1478 
optionsPreferences(const QString & iPluginName)1479 void SKGMainPanel::optionsPreferences(const QString& iPluginName)
1480 {
1481     SKGTRACEINFUNC(1)
1482     // Compute page
1483     QString pluginName = iPluginName;
1484     if (pluginName.isEmpty()) {
1485         auto* act = qobject_cast<QAction*>(sender());
1486         if (act != nullptr) {
1487             pluginName = act->property("page").toString();
1488         }
1489     }
1490     if (pluginName.isEmpty() && (this->currentPage() != nullptr)) {
1491         pluginName = this->currentPage()->objectName();
1492     }
1493     SKGTRACEL(1) << "Open setting page: " << pluginName << SKGENDL;
1494     // Synchronize setting with confirmation panel
1495     if (skgbasegui_settings::update_modified_bookmarks() == 0) {
1496         KMessageBox::ButtonCode confirm;
1497         bool ask = KMessageBox::shouldBeShownYesNo(QStringLiteral("updateBookmarkOnClose"), confirm);
1498 
1499         KConfigGroup pref = getMainConfigGroup();
1500         if (ask) {
1501             pref.writeEntry("update_modified_bookmarks", 0, KConfigBase::Normal);
1502             SKGTRACEL(1) << "update_modified_bookmarks set to ASK" << SKGENDL;
1503         } else if (confirm == KMessageBox::Yes) {
1504             pref.writeEntry("update_modified_bookmarks", 1, KConfigBase::Normal);
1505             SKGTRACEL(1) << "update_modified_bookmarks set to ALWAYS" << SKGENDL;
1506         } else {
1507             pref.writeEntry("update_modified_bookmarks", 2, KConfigBase::Normal);
1508             SKGTRACEL(1) << "update_modified_bookmarks set to NEVER" << SKGENDL;
1509         }
1510     }
1511 
1512     if (skgbasegui_settings::update_modified_contexts() == 0) {
1513         KMessageBox::ButtonCode confirm;
1514         bool ask = KMessageBox::shouldBeShownYesNo(QStringLiteral("updateContextOnClose"), confirm);
1515 
1516         KConfigGroup pref = getMainConfigGroup();
1517         if (ask) {
1518             pref.writeEntry("update_modified_contexts", 0, KConfigBase::Normal);
1519             SKGTRACEL(1) << "update_modified_contexts set to ASK" << SKGENDL;
1520         } else if (confirm == KMessageBox::Yes) {
1521             pref.writeEntry("update_modified_contexts", 1, KConfigBase::Normal);
1522             SKGTRACEL(1) << "update_modified_contexts set to ALWAYS" << SKGENDL;
1523         } else {
1524             pref.writeEntry("update_modified_contexts", 2, KConfigBase::Normal);
1525             SKGTRACEL(1) << "update_modified_contexts set to NEVER" << SKGENDL;
1526         }
1527     }
1528     skgbasegui_settings::self()->load();
1529 
1530 
1531     if (KConfigDialog::showDialog(QStringLiteral("settings"))) {
1532         return;
1533     }
1534 
1535     auto dialog = new KConfigDialog(this, QStringLiteral("settings"), skgbasegui_settings::self());
1536 
1537     // Add main
1538     auto w = new QWidget();
1539     d->uipref.setupUi(w);
1540     d->uipref.kcfg_date_format->addItem(i18nc("Date format", "Short date (%1, %2)",
1541                                         QLocale().toString(QDate::currentDate(), QLocale::ShortFormat),
1542                                         QLocale().toString(QDate::currentDate().addDays(-10), QLocale::ShortFormat)));
1543     d->uipref.kcfg_date_format->addItem(i18nc("Date format", "Long date (%1, %2)",
1544                                         QLocale().toString(QDate::currentDate(), QLocale::LongFormat),
1545                                         QLocale().toString(QDate::currentDate().addDays(-10), QLocale::LongFormat)));
1546     d->uipref.kcfg_date_format->addItem(i18nc("Date format", "Fancy short date (%1, %2)",
1547                                         KFormat().formatRelativeDate(QDate::currentDate(), QLocale::ShortFormat),
1548                                         KFormat().formatRelativeDate(QDate::currentDate().addDays(-10), QLocale::ShortFormat)));
1549     d->uipref.kcfg_date_format->addItem(i18nc("Date format", "Fancy long date (%1, %2)",
1550                                         KFormat().formatRelativeDate(QDate::currentDate(), QLocale::LongFormat),
1551                                         KFormat().formatRelativeDate(QDate::currentDate().addDays(-10), QLocale::LongFormat)));
1552     d->uipref.kcfg_date_format->addItem(i18nc("Date format", "ISO date (%1, %2)",
1553                                         QDate::currentDate().toString(Qt::ISODate),
1554                                         QDate::currentDate().addDays(-10).toString(Qt::ISODate)));
1555     dialog->addPage(w, skgbasegui_settings::self(), i18nc("Noun", "General"), QStringLiteral("preferences-other"));
1556 
1557     // Add plugin in client in right order
1558     int nbplugin = d->m_pluginsList.count();
1559     for (int j = 0; j < nbplugin; ++j) {
1560         SKGInterfacePlugin* pluginInterface = getPluginByIndex(j);
1561         if (pluginInterface != nullptr) {
1562             QWidget* w2 = pluginInterface->getPreferenceWidget();
1563             if (w2 != nullptr) {
1564                 auto icon = SKGServices::fromTheme(pluginInterface->icon());
1565                 KPageWidgetItem* p = dialog->addPage(w2, pluginInterface->getPreferenceSkeleton(), pluginInterface->title(), icon.name());
1566                 if ((p != nullptr) && pluginName == pluginInterface->objectName()) {
1567                     dialog->setCurrentPage(p);
1568                 }
1569             }
1570         }
1571     }
1572 
1573     connect(dialog, &KConfigDialog::settingsChanged, this, &SKGMainPanel::onSettingsChanged);
1574 
1575     dialog->setAttribute(Qt::WA_DeleteOnClose);
1576     dialog->show();
1577 
1578     // Refresh
1579     refresh();
1580 }
1581 
onSettingsChanged()1582 void SKGMainPanel::onSettingsChanged()
1583 {
1584     SKGError err;
1585     SKGTRACEINFUNCRC(1, err) {
1586         int nb = d->m_pluginsList.count();
1587         SKGBEGINPROGRESSTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Save settings"), err, nb)
1588 
1589         // Refresh plugins
1590         for (int i = 0; !err && i < nb; ++i) {
1591             err = getPluginByIndex(i)->savePreferences();
1592             IFOKDO(err, getDocument()->stepForward(i + 1))
1593         }
1594 
1595         // Setting for tab position
1596         d->refreshTabPosition();
1597 
1598         // Setting for bookmarks modification
1599         {
1600             int option = skgbasegui_settings::update_modified_bookmarks();
1601             if (option == 0) {
1602                 // ASK: remove following setting
1603                 KMessageBox::enableMessage(QStringLiteral("updateBookmarkOnClose"));
1604                 SKGTRACEL(1) << "updateBookmarkOnClose set to ASK" << SKGENDL;
1605             } else if (option == 1) {
1606                 // ALWAYS: set following setting
1607                 KMessageBox::saveDontShowAgainYesNo(QStringLiteral("updateBookmarkOnClose"), KMessageBox::Yes);
1608                 SKGTRACEL(1) << "updateBookmarkOnClose set to Yes" << SKGENDL;
1609             } else {
1610                 // NEVER: set following setting
1611                 KMessageBox::saveDontShowAgainYesNo(QStringLiteral("updateBookmarkOnClose"), KMessageBox::No);
1612                 SKGTRACEL(1) << "updateBookmarkOnClose set to No" << SKGENDL;
1613             }
1614         }
1615         {
1616             int option = skgbasegui_settings::update_modified_contexts();
1617             if (option == 0) {
1618                 // ASK: remove following setting
1619                 KMessageBox::enableMessage(QStringLiteral("updateContextOnClose"));
1620                 SKGTRACEL(1) << "updateContextOnClose set to ASK" << SKGENDL;
1621             } else if (option == 1) {
1622                 // ALWAYS: set following setting
1623                 KMessageBox::saveDontShowAgainYesNo(QStringLiteral("updateContextOnClose"), KMessageBox::Yes);
1624                 SKGTRACEL(1) << "updateContextOnClose set to Yes" << SKGENDL;
1625             } else {
1626                 // NEVER: set following setting
1627                 KMessageBox::saveDontShowAgainYesNo(QStringLiteral("updateContextOnClose"), KMessageBox::No);
1628                 SKGTRACEL(1) << "updateContextOnClose set to No" << SKGENDL;
1629             }
1630         }
1631         skgbasegui_settings::self()->load();
1632     }
1633 
1634     // Rebuild system tray
1635     d->rebuildSystemTray();
1636 
1637     emit settingsChanged();
1638 
1639     // Display error
1640     displayErrorMessage(err);
1641 }
1642 
refresh()1643 void SKGMainPanel::refresh()
1644 {
1645     SKGTRACEINFUNC(1)
1646 
1647     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1648 
1649     // Show/hide main widget
1650     bool atLeastOnePageOpened = (d->m_tabWidget->count() > 0);
1651     d->m_tabWidget->setVisible(atLeastOnePageOpened);
1652     if (d->m_mainWidget != nullptr) {
1653         d->m_mainWidget->setVisible(!atLeastOnePageOpened);
1654     }
1655 
1656     // Refresh actions
1657     d->m_widgetHavingSelection = qobject_cast<SKGWidget*>(sender());
1658     SKGObjectBase selection = SKGMainPanel::getMainPanel()->getFirstSelectedObject();
1659     int nbSelectedItems = SKGMainPanel::getMainPanel()->getNbSelectedObjects();
1660     bool hasFocus = SKGMainPanel::getMainPanel()->hasSelectionWithFocus();
1661 
1662     QString selectedTable = (nbSelectedItems > 0 ? selection.getRealTable() : QLatin1String(""));
1663     for (const auto& actDetails : qAsConst(d->m_registeredGlogalAction)) {
1664         QStringList tables = actDetails.tables;
1665         if (tables.count() == 1 && tables.at(0).startsWith(QLatin1String("query:"))) {
1666             // Dynamic mode
1667             getDocument()->getDistinctValues(QStringLiteral("sqlite_master"), QStringLiteral("name"), tables.at(0).right(tables.at(0).count() - 6), tables);
1668         }
1669 
1670         bool enabled = (tables.contains(selectedTable) || tables.isEmpty()) &&
1671                        (nbSelectedItems >= actDetails.min) &&
1672                        (nbSelectedItems <= actDetails.max || actDetails.max == -1) &&
1673                        (!actDetails.focus || hasFocus);
1674 
1675         if (enabled && nbSelectedItems == 0 && (actDetails.min == 0 || actDetails.min == -1)) {
1676             // Check if a page is opened
1677             SKGTabPage* page = SKGMainPanel::getMainPanel()->currentPage();
1678             if (page != nullptr) {
1679                 auto* view = qobject_cast<QAbstractItemView*>(page->mainWidget());
1680                 enabled = (actDetails.min == -1 || view != nullptr);
1681             } else {
1682                 enabled = false;
1683             }
1684         }
1685         if (actDetails.action != nullptr) {
1686             actDetails.action->setEnabled(enabled);
1687         }
1688     }
1689 
1690     // Refresh plugins
1691     int nb = d->m_pluginsList.count();
1692     for (int i = 0; i < nb; ++i) {
1693         getPluginByIndex(i)->refresh();
1694     }
1695 
1696     // Enable addTabeAction
1697     SKGTabPage* toSave = currentPage();
1698     if (d->m_switchPinState != nullptr) {
1699         if ((toSave != nullptr) && toSave->isPin()) {
1700             d->m_switchPinState->setText(i18nc("Noun, user action", "Unpin this page"));
1701         } else {
1702             d->m_switchPinState->setText(i18nc("Noun, user action", "Pin this page"));
1703         }
1704     }
1705     if (d->m_closePageAction != nullptr) {
1706         d->m_closePageAction->setEnabled(atLeastOnePageOpened && (toSave != nullptr) && !toSave->isPin());
1707     }
1708     if (d->m_switchPinState != nullptr) {
1709         d->m_switchPinState->setEnabled(atLeastOnePageOpened);
1710     }
1711     if (d->m_closeAllOtherPagesAction != nullptr) {
1712         d->m_closeAllOtherPagesAction->setEnabled(d->m_tabWidget->count() > 1);
1713     }
1714     if (d->m_reopenLastClosed != nullptr) {
1715         d->m_reopenLastClosed->setEnabled(!d->m_historyClosedPages.isEmpty());
1716     }
1717 
1718     if (d->m_saveDefaultStateAction != nullptr) {
1719         d->m_saveDefaultStateAction->setEnabled((toSave != nullptr) && !toSave->getDefaultStateAttribute().isEmpty());
1720     }
1721     if (d->m_resetDefaultStateAction != nullptr) {
1722         d->m_resetDefaultStateAction->setEnabled((toSave != nullptr) && !toSave->getDefaultStateAttribute().isEmpty());
1723     }
1724     if (d->m_overwriteBookmarkStateAction != nullptr) {
1725         d->m_overwriteBookmarkStateAction->setEnabled((toSave != nullptr) && !toSave->getBookmarkID().isEmpty());
1726     }
1727     if (d->m_enableEditorAction != nullptr) {
1728         d->m_enableEditorAction->setEnabled((toSave != nullptr) && toSave->isEditor());
1729     }
1730     if (d->m_zoomSelector != nullptr) {
1731         d->m_zoomSelector->setVisible((toSave != nullptr) && toSave->isZoomable());
1732         if (toSave != nullptr) {
1733             d->m_zoomSelector->setValue(toSave->zoomPosition());
1734             QWidget* zoomWidget = toSave->zoomableWidget();
1735             auto* treeView = qobject_cast<SKGTreeView*>(zoomWidget);
1736             if (treeView != nullptr) {
1737                 disconnect(treeView, &SKGTreeView::zoomChanged, this, nullptr);
1738                 connect(treeView, &SKGTreeView::zoomChanged, this, [ = ](int val) {
1739                     d->m_zoomSelector->setValue(val);
1740                 });
1741             } else {
1742                 auto* webView = qobject_cast<SKGWebView*>(zoomWidget);
1743                 if (webView != nullptr) {
1744                     disconnect(webView, &SKGWebView::zoomChanged, this, nullptr);
1745                     connect(webView, &SKGWebView::zoomChanged, this, [ = ](int val) {
1746                         d->m_zoomSelector->setValue(val);
1747                     });
1748                 }
1749             }
1750         }
1751     }
1752 
1753     if (d->m_actLock != nullptr) {
1754         d->m_actLock->setVisible(d->ui.kDockContext->features() == QDockWidget::AllDockWidgetFeatures);
1755     }
1756     if (d->m_actUnLock != nullptr) {
1757         d->m_actUnLock->setVisible(d->ui.kDockContext->features() == QDockWidget::NoDockWidgetFeatures);
1758     }
1759 
1760     if (d->m_previousAction != nullptr) {
1761         SKGTabPage::SKGPageHistoryItemList list;
1762         if (toSave != nullptr) {
1763             list = toSave->getPreviousPages();
1764         }
1765         d->m_previousAction->setEnabled(!list.isEmpty());
1766     }
1767     if (d->m_nextAction != nullptr) {
1768         SKGTabPage::SKGPageHistoryItemList list;
1769         if (toSave != nullptr) {
1770             list = toSave->getNextPages();
1771         }
1772         d->m_nextAction->setEnabled(!list.isEmpty());
1773     }
1774 
1775     // Set current selection of context
1776     d->ui.kContextList->clearSelection();
1777     if (toSave != nullptr) {
1778         // Get plugin of current page
1779         SKGInterfacePlugin* plugin = getPluginByName(toSave->objectName());
1780         int index = (plugin != nullptr ? plugin->property("contextItem").toInt() : -1);
1781         if (index != -1) {
1782             d->ui.kContextList->setCurrentItem(d->ui.kContextList->item(index));
1783         }
1784     }
1785 
1786     // Set window title
1787     QString modified;
1788     if (getDocument()->isFileModified()) {
1789         modified += i18nc("Noun, indicate that current document is modified", " [modified]");
1790     }
1791     if (getDocument()->isReadOnly()) {
1792         modified += i18nc("Noun, indicate that current document is loaded in read only", " [read only]");
1793     }
1794     QString fileName = getDocument()->getCurrentFileName();
1795     if (fileName.isEmpty()) {
1796         fileName = i18nc("Noun, default name for a new document", "Untitled");
1797     } else {
1798         if (fileName != d->m_fileName) {
1799             // The file name has been changed
1800             onClearMessages();
1801 
1802             d->m_fileName = fileName;
1803 #ifdef KActivities_FOUND
1804             if (!d->m_fileName.isEmpty()) {
1805                 d->m_activityResourceInstance->setUri(d->m_fileName);
1806             }
1807 #endif
1808         }
1809     }
1810     setWindowTitle(i18nc("Title of the main window", "%1%2", fileName, modified));
1811 
1812     QApplication::restoreOverrideCursor();
1813 }
1814 
currentPageHistoryItem() const1815 SKGTabPage::SKGPageHistoryItem SKGMainPanel::currentPageHistoryItem() const
1816 {
1817     SKGTabPage::SKGPageHistoryItem cpage;
1818     int currentIndex = currentPageIndex();
1819     SKGTabPage* cPage = currentPage();
1820     if (currentIndex >= 0 && (cPage != nullptr)) {
1821         cpage.plugin = cPage->objectName();
1822         SKGInterfacePlugin* plugin = SKGMainPanel::getMainPanel()->getPluginByName(cpage.plugin);
1823         if (plugin != nullptr) {
1824             cpage.name = d->m_tabWidget->tabText(currentIndex);
1825             cpage.icon = plugin->icon();
1826         }
1827         cpage.state = cPage->getState();
1828         cpage.bookmarkID = cPage->getBookmarkID();
1829     }
1830 
1831     return cpage;
1832 }
1833 
openPage(SKGInterfacePlugin * plugin,int index,const QString & parameters,const QString & title,const QString & iID,bool iSetCurrent)1834 SKGTabPage* SKGMainPanel::openPage(SKGInterfacePlugin* plugin, int index, const QString& parameters, const QString& title, const QString& iID, bool iSetCurrent)
1835 {
1836     SKGTRACEINFUNC(1)
1837     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1838     bool previous = d->m_tabWidget->blockSignals(true);
1839     // If the current page is pin, then open new page
1840     SKGTabPage* cPage = currentPage();
1841     if ((cPage != nullptr) && cPage->isPin()) {
1842         index = -1;
1843         iSetCurrent = true;
1844     }
1845 
1846     SKGTabPage* w = nullptr;
1847 
1848     SKGTabPage::SKGPageHistoryItemList previousPages;
1849     if (index != -1) {
1850         int currentIndex = currentPageIndex();
1851         if (currentIndex >= 0 && (cPage != nullptr)) {
1852             previousPages = cPage->getPreviousPages();
1853             previousPages.insert(0, currentPageHistoryItem());
1854 
1855             d->m_tabWidget->removeTab(currentIndex);
1856             closePage(cPage);
1857 
1858             // Repair the history of closed page
1859             if (!d->m_historyClosedPages.isEmpty()) {
1860                 d->m_historyClosedPages.removeLast();
1861             }
1862         }
1863     }
1864 
1865     if (plugin != nullptr) {
1866         w = plugin->getWidget();
1867         if (w != nullptr) {
1868             // Title
1869             QString title2 = (title.isEmpty() ? plugin->title() : title);
1870             w->setObjectName(plugin->objectName());
1871             if (!iID.isEmpty()) {
1872                 w->setBookmarkID(iID);
1873             }
1874 
1875             QString param = parameters;
1876             if (param.isEmpty()) {
1877                 QString def = w->getDefaultStateAttribute();
1878                 if (!def.isEmpty()) {
1879                     param = getDocument()->getParameter(def);
1880                 }
1881             }
1882             SKGTRACEL(10) << "state=[" << param << "]" << SKGENDL;
1883             w->setState(param);
1884             connect(w, &SKGTabPage::selectionChanged, this, &SKGMainPanel::refresh);
1885             connect(w, &SKGTabPage::selectionChanged, this, &SKGMainPanel::selectionChanged);
1886             connect(w, &SKGTabPage::selectionFocusChanged, this, &SKGMainPanel::refresh);
1887 
1888             if (index == -1) {
1889                 SKGTRACEINFUNC(20)
1890                 d->m_tabWidget->addTab(w, SKGServices::fromTheme(plugin->icon()), title2);
1891                 if (iSetCurrent) {
1892                     d->m_tabWidget->setCurrentWidget(w);
1893                 }
1894             } else {
1895                 SKGTRACEINFUNC(20)
1896                 d->m_tabWidget->insertTab(index, w, SKGServices::fromTheme(plugin->icon()), title2);
1897                 if (iSetCurrent) {
1898                     d->m_tabWidget->setCurrentWidget(w);
1899                 }
1900 
1901                 w->setPreviousPages(previousPages);
1902                 SKGTabPage::SKGPageHistoryItemList empty;
1903                 w->setNextPages(empty);
1904             }
1905             SKGTRACEL(1) << "opening plugin [" << plugin->objectName() << ']' << SKGENDL;
1906             Q_EMIT pageOpened();
1907         }
1908     } else {
1909         getDocument()->sendMessage(i18nc("An information message",  "Impossible to open the page because the plugin was not found"), SKGDocument::Error);
1910         notify();  // Due to sendMessage not in a transaction
1911     }
1912 
1913     // Show/hide main widget
1914     bool atLeastOnePageOpened = (d->m_tabWidget->count() > 0);
1915     d->m_tabWidget->setVisible(atLeastOnePageOpened);
1916     if (d->m_mainWidget != nullptr) {
1917         d->m_mainWidget->setVisible(!atLeastOnePageOpened);
1918     }
1919 
1920     d->m_tabWidget->blockSignals(previous);
1921     if (iSetCurrent) {
1922         Q_EMIT currentPageChanged();
1923     }
1924     QApplication::restoreOverrideCursor();
1925     return w;
1926 }
1927 
currentPageIndex() const1928 int SKGMainPanel::currentPageIndex() const
1929 {
1930     return d->m_tabWidget->currentIndex();
1931 }
1932 
currentPage() const1933 SKGTabPage* SKGMainPanel::currentPage() const
1934 {
1935     return qobject_cast< SKGTabPage* >(d->m_tabWidget->currentWidget());
1936 }
1937 
pageIndex(SKGTabPage * iPage) const1938 int SKGMainPanel::pageIndex(SKGTabPage* iPage) const
1939 {
1940     int nb = countPages();
1941     for (int i = 0; i < nb; ++i) {
1942         if (page(i) == iPage) {
1943             return i;
1944         }
1945     }
1946 
1947     return -1;
1948 }
1949 
splashScreen() const1950 QSplashScreen* SKGMainPanel::splashScreen() const
1951 {
1952     return d->m_splashScreen;
1953 }
1954 
countPages() const1955 int SKGMainPanel::countPages() const
1956 {
1957     return d->m_tabWidget->count();
1958 }
1959 
page(int iIndex) const1960 SKGTabPage* SKGMainPanel::page(int iIndex) const
1961 {
1962     return qobject_cast<SKGTabPage*>(d->m_tabWidget->widget(iIndex));
1963 }
1964 
setCurrentPage(int iIndex)1965 void SKGMainPanel::setCurrentPage(int iIndex)
1966 {
1967     d->m_tabWidget->setCurrentIndex(iIndex);
1968 }
1969 
onBeforeOpenContext()1970 void SKGMainPanel::onBeforeOpenContext()
1971 {
1972     d->m_middleClick = ((QApplication::mouseButtons() & Qt::MidButton) != 0u);
1973 }
1974 
openPage(const QUrl & iUrl,bool iNewPage)1975 bool SKGMainPanel::openPage(const QUrl& iUrl, bool iNewPage)
1976 {
1977     const QUrl& url(iUrl);
1978     if (url.scheme() == QStringLiteral("skg")) {
1979         // Get plugin
1980         SKGInterfacePlugin* plugin = getPluginByName(url.host());
1981         if (plugin != nullptr) {
1982             // Open special page
1983             SKGTabPage* w = plugin->getWidget();
1984             if (w != nullptr) {
1985                 // Create xml
1986                 QString path = url.path().remove('/');
1987                 QDomDocument doc(QStringLiteral("SKGML"));
1988                 doc.setContent(getDocument()->getParameter(path.isEmpty() ? w->getDefaultStateAttribute() : path));
1989                 QDomElement root = doc.documentElement();
1990                 if (root.isNull()) {
1991                     root = doc.createElement(QStringLiteral("parameters"));
1992                     doc.appendChild(root);
1993                 }
1994                 auto params = QUrlQuery(url).queryItems();
1995                 for (const auto& p : qAsConst(params)) {
1996                     QString value = QUrl::fromPercentEncoding(p.second.toUtf8());
1997                     SKGMainPanelPrivate::setAttribute(root, p.first, value);
1998                 }
1999 
2000                 // Open page
2001                 openPage(plugin, iNewPage ? -1 : currentPageIndex(), doc.toString(), url.fragment());
2002                 return true;
2003             }
2004         } else {
2005             // Trigger action
2006             QAction* act = SKGMainPanel::getMainPanel()->getGlobalAction(url.host());
2007             if (act != nullptr) {
2008                 auto params = QUrlQuery(url).queryItems();
2009                 for (const auto& p : qAsConst(params)) {
2010                     QString value = QUrl::fromPercentEncoding(p.second.toUtf8());
2011                     act->setProperty(p.first.toUtf8().data(), value);
2012                 }
2013 
2014                 act->trigger();
2015                 return true;
2016             }
2017         }
2018     } else {
2019         QDesktopServices::openUrl(iUrl);
2020         return true;
2021     }
2022     displayErrorMessage(SKGError(ERR_ABORT, i18nc("Error message", "Unknown plugin or action [%1] in url [%2]", url.host(), iUrl.toString())));
2023 
2024     return false;
2025 }
2026 
openPage()2027 bool SKGMainPanel::openPage()
2028 {
2029     return openPage(QString());
2030 }
2031 
openPage(const QString & iUrl,bool iNewPage)2032 bool SKGMainPanel::openPage(const QString& iUrl, bool iNewPage)
2033 {
2034     // Get the url
2035     QString urlString(iUrl);
2036     if (urlString.isEmpty()) {
2037         auto* act = qobject_cast< QAction* >(sender());
2038         if (act != nullptr) {
2039             urlString = act->data().toString();
2040         }
2041     }
2042 
2043     return openPage(QUrl(urlString), iNewPage);
2044 }
2045 
openPage(int iPage,bool iNewPage)2046 SKGTabPage* SKGMainPanel::openPage(int iPage, bool iNewPage)
2047 {
2048     SKGTRACEINFUNC(1)
2049     SKGTRACEL(1) << "iPage=" << iPage << SKGENDL;
2050     int index = d->ui.kContextList->item(iPage)->data(12).toInt();
2051     return openPage(getPluginByIndex(index), iNewPage ? -1 : currentPageIndex());
2052 }
2053 
onOpenContext()2054 void SKGMainPanel::onOpenContext()
2055 {
2056     SKGTRACEINFUNC(1)
2057     if (!(QApplication::mouseButtons() & Qt::RightButton)) {
2058         int cpage = -1;
2059         auto* s = qobject_cast<QAction*>(this->sender());
2060         if (s != nullptr) {
2061             cpage = s->data().toInt();
2062         } else {
2063             cpage = d->ui.kContextList->currentRow();
2064         }
2065         if (cpage != -1) {
2066             openPage(cpage, ((QApplication::keyboardModifiers() &Qt::ControlModifier) != 0u) || d->m_middleClick || ((QGuiApplication::mouseButtons() & Qt::MidButton) != 0u));
2067         }
2068     }
2069     d->m_middleClick = false;
2070 }
2071 
switchPinPage(QWidget * iWidget)2072 void SKGMainPanel::switchPinPage(QWidget* iWidget)
2073 {
2074     auto* toSwitch = qobject_cast< SKGTabPage* >(iWidget);
2075     if (toSwitch == nullptr) {
2076         toSwitch = currentPage();
2077     }
2078 
2079     if (toSwitch != nullptr) {
2080         toSwitch->setPin(!toSwitch->isPin());
2081         Q_EMIT currentPageChanged();
2082     }
2083 }
2084 
closePageByIndex(int iIndex)2085 void SKGMainPanel::closePageByIndex(int iIndex)
2086 {
2087     QWidget* w = nullptr;
2088     if (iIndex >= 0) {
2089         w = d->m_tabWidget->widget(iIndex);
2090     } else {
2091         w = d->m_tabWidget->currentWidget();
2092     }
2093 
2094     closePage(w);
2095 }
2096 
closeCurrentPage()2097 void SKGMainPanel::closeCurrentPage()
2098 {
2099     closePage(nullptr);
2100 }
2101 
closePage(QWidget * iWidget,bool iForce)2102 void SKGMainPanel::closePage(QWidget* iWidget, bool iForce)
2103 {
2104     SKGTRACEINFUNC(1)
2105     if (getDocument()->getCurrentTransaction() != 0) {
2106         QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
2107         displayMessage(i18nc("Information message", "A page cannot be closed when an operation is running."), SKGDocument::Information);
2108         QApplication::restoreOverrideCursor();
2109     } else {
2110         auto* toRemove = qobject_cast< SKGTabPage* >(iWidget);
2111         if (toRemove == nullptr) {
2112             toRemove = currentPage();
2113         }
2114         if ((toRemove != nullptr) && toRemove->close(iForce)) {
2115             historyPage item;
2116             item.current = currentPageHistoryItem();
2117             item.next = toRemove->getNextPages();
2118             item.previous = toRemove->getPreviousPages();
2119             d->m_historyClosedPages.push_back(item);
2120             delete toRemove;
2121 
2122             emit pageClosed();
2123         }
2124     }
2125 
2126     // Show/hide main widget
2127     bool atLeastOnePageOpened = (d->m_tabWidget->count() > 0);
2128     d->m_tabWidget->setVisible(atLeastOnePageOpened);
2129     if (d->m_mainWidget != nullptr) {
2130         d->m_mainWidget->setVisible(!atLeastOnePageOpened);
2131     }
2132 
2133     if (!atLeastOnePageOpened) {
2134         d->m_fullScreenAction->setChecked(false);
2135         onFullScreen();
2136     }
2137 }
2138 
closeAllPages(bool iForce)2139 void SKGMainPanel::closeAllPages(bool iForce)
2140 {
2141     SKGTRACEINFUNC(1)
2142     bool previous = d->m_tabWidget->blockSignals(true);
2143     int nb = d->m_tabWidget->count();
2144     for (int i = nb - 1; i >= 0; --i) {
2145         auto* w = qobject_cast< SKGTabPage* >(d->m_tabWidget->widget(i));
2146         if ((w != nullptr) && (iForce || !w->isPin())) {
2147             closePage(w, iForce);
2148         }
2149     }
2150     d->m_tabWidget->blockSignals(previous);
2151     KMessageBox::enableMessage(QStringLiteral("closepinnedpage"));
2152     Q_EMIT currentPageChanged();
2153 }
2154 
closeAllOtherPages(QWidget * iWidget)2155 void SKGMainPanel::closeAllOtherPages(QWidget* iWidget)
2156 {
2157     SKGTRACEINFUNC(1)
2158     bool previous = d->m_tabWidget->blockSignals(true);
2159     QWidget* toKeep = iWidget;
2160     if (toKeep == nullptr) {
2161         toKeep = currentPage();
2162     }
2163 
2164     int nb = d->m_tabWidget->count();
2165     for (int i = nb - 1; i >= 0; --i) {
2166         auto* w = qobject_cast< SKGTabPage* >(d->m_tabWidget->widget(i));
2167         if ((w != nullptr) && w != toKeep && !w->isPin()) {
2168             closePage(w);
2169         }
2170     }
2171     d->m_tabWidget->blockSignals(previous);
2172     Q_EMIT currentPageChanged();
2173 }
2174 
saveDefaultState()2175 void SKGMainPanel::saveDefaultState()
2176 {
2177     SKGTRACEINFUNC(1)
2178     SKGError err;
2179     SKGTabPage* toSave = currentPage();
2180     if (toSave != nullptr) {
2181         // Get bookmarks uuid
2182         QString uuid = toSave->getBookmarkID();
2183 
2184         // Reset bookmarks uuid to overwrite page state
2185         toSave->setBookmarkID(QLatin1String(""));
2186 
2187         // Overwrite
2188         toSave->overwrite(false);
2189 
2190         // Set original bookmarks uuid
2191         toSave->setBookmarkID(uuid);
2192     }
2193 }
2194 
overwriteBookmarkState()2195 void SKGMainPanel::overwriteBookmarkState()
2196 {
2197     SKGTRACEINFUNC(1)
2198     SKGError err;
2199     SKGTabPage* toSave = currentPage();
2200     if (toSave != nullptr) {
2201         // Get bookmarks uuid
2202         QString uuid = toSave->getBookmarkID();
2203         if (!uuid.isEmpty()) {
2204             // Overwrite
2205             toSave->overwrite(false);
2206         }
2207     }
2208 }
2209 
resetDefaultState()2210 void SKGMainPanel::resetDefaultState()
2211 {
2212     SKGTRACEINFUNC(1)
2213     SKGError err;
2214     SKGTabPage* toSave = currentPage();
2215     if (toSave != nullptr) {
2216         QString name = toSave->getDefaultStateAttribute();
2217         if (!name.isEmpty()) {
2218             SKGBEGINLIGHTTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Reset default state"), err)
2219             IFOKDO(err, getDocument()->setParameter(name, QStringLiteral("<!DOCTYPE SKGML>")))
2220 
2221             // Refresh panel
2222             IFOK(err) toSave->setState(QLatin1String(""));
2223         }
2224     }
2225 
2226     // status bar
2227     IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Default state has been reset")))
2228     displayErrorMessage(err);
2229 }
2230 
addTab()2231 void SKGMainPanel::addTab()
2232 {
2233     SKGTRACEINFUNC(1)
2234     SKGTabPage* cPage = currentPage();
2235     if (cPage != nullptr) {
2236         openPage(getPluginByName(cPage->objectName()));
2237     }
2238 }
2239 
eventFilter(QObject * iObject,QEvent * iEvent)2240 bool SKGMainPanel::eventFilter(QObject* iObject, QEvent* iEvent)
2241 {
2242     if ((iObject != nullptr) && (iEvent != nullptr) && iEvent->type() == QEvent::Resize) {
2243         auto* rEvent = dynamic_cast<QResizeEvent*>(iEvent);
2244         if (rEvent != nullptr) {
2245             QSize newSize = rEvent->size();
2246 
2247             // Compute icon size
2248             int s = qMax(qMin(newSize.width() / 5, 64), 16);
2249             d->ui.kContextList->setIconSize(QSize(s, s));
2250         }
2251     }
2252     return KXmlGuiWindow::eventFilter(iObject, iEvent);
2253 }
2254 
getTipsOfDay() const2255 QStringList SKGMainPanel::getTipsOfDay() const
2256 {
2257     return d->m_tipsOfTheDay;
2258 }
2259 
getTipOfDay() const2260 QString SKGMainPanel::getTipOfDay() const
2261 {
2262     auto tips = getTipsOfDay();
2263     return SKGServices::htmlToString(tips.at(QRandomGenerator::global()->bounded(tips.size())));
2264 }
2265 
notify(int iTransaction)2266 void SKGMainPanel::notify(int iTransaction)
2267 {
2268     SKGTRACEINFUNC(1)
2269     SKGTRACEL(1) << "iTransaction=" << iTransaction << SKGENDL;
2270 
2271     // Notify
2272     SKGObjectBase transaction(getDocument(), QStringLiteral("doctransaction"), iTransaction);
2273     if (iTransaction == 0 || transaction.getAttribute(QStringLiteral("t_mode")) != QStringLiteral("R")) {
2274         SKGDocument::SKGMessageList msg;
2275         getDocument()->getMessages(iTransaction, msg, false);
2276         int nbMessages = msg.count();
2277         if (nbMessages != 0) {
2278             // Build list of types
2279             SKGDocument::MessageType maxType = SKGDocument::Positive;
2280             QList<SKGDocument::MessageType> listGroups;
2281             listGroups.reserve(nbMessages);
2282             for (int i = 0; i < nbMessages; ++i) {
2283                 SKGDocument::SKGMessage m = msg.at(i);
2284                 // if the message has an action, it can not be grouped
2285                 if (!m.Action.isEmpty()) {
2286                     displayMessage(m.Text, m.Type, m.Action);
2287                     msg.removeAt(i);
2288                     i--;
2289                     nbMessages--;
2290                 } else {
2291                     if (listGroups.isEmpty() || m.Type != listGroups.at(listGroups.count() - 1)) {
2292                         listGroups.push_back(m.Type);
2293                     }
2294                     if (static_cast<int>(m.Type) >= static_cast<int>(maxType)) {
2295                         maxType = m.Type;
2296                     }
2297                 }
2298             }
2299 
2300             // Is the number of type acceptable?
2301             bool modeGrouped = false;
2302             if (listGroups.count() > 5 || nbMessages > 20) {
2303                 // Too many group ==> simplification
2304                 listGroups.clear();
2305                 listGroups.push_back(maxType);
2306                 modeGrouped = true;
2307             }
2308 
2309             // Build message
2310             if (nbMessages != 0) {
2311                 QString message;
2312                 int indexGroup = 0;
2313                 for (int i = 0; i < nbMessages; ++i) {
2314                     auto m = msg.at(i);
2315                     auto t = m.Type;
2316                     if (modeGrouped) {
2317                         if (t == SKGDocument::Warning) {
2318                             m.Text = i18nc("Warning header", "Warning: %1", m.Text);
2319                         } else if (t == SKGDocument::Error) {
2320                             m.Text = i18nc("Error header", "Error: %1", m.Text);
2321                         } else if (t == SKGDocument::Information) {
2322                             m.Text = i18nc("Information header", "Information: %1", m.Text);
2323                         } else if (t == SKGDocument::Positive) {
2324                             m.Text = i18nc("Done header", "Done: %1", m.Text);
2325                         }
2326                     }
2327                     if (modeGrouped || t == listGroups.at(indexGroup)) {
2328                         // Same group
2329                         if (!message.isEmpty()) {
2330                             message += QStringLiteral("<br>");
2331                         }
2332                         message += m.Text;
2333                     } else {
2334                         // Different group
2335                         displayMessage(message, listGroups.at(indexGroup));
2336 
2337                         // Reset message
2338                         message = m.Text;
2339 
2340                         indexGroup++;
2341                     }
2342                 }
2343 
2344                 if (nbMessages < 21 || !SKGServices::getEnvVariable(QStringLiteral("SKGTEST")).isEmpty()) {
2345                     // Display a simple notification
2346                     /*auto notify = new KNotification(KAboutData::applicationData().componentName() % "_info_event" , this);
2347                     notify->setText(message);
2348                     notify->sendEvent();*/
2349                     displayMessage(message, listGroups.at(indexGroup));
2350                 } else {
2351                     // Too many message, display a warning panel
2352                     KMessageBox::information(SKGMainPanel::getMainPanel(), message, i18nc("Noun",  "Notification"));
2353                 }
2354             }
2355         }
2356     }
2357 }
2358 
changeEvent(QEvent * e)2359 void SKGMainPanel::changeEvent(QEvent* e)
2360 {
2361     KXmlGuiWindow::changeEvent(e);
2362 }
2363 
statusNormalMessage() const2364 QLabel* SKGMainPanel::statusNormalMessage() const
2365 {
2366     return d->m_kNormalMessage;
2367 }
2368 
getMessageWidget(const QString & iMessage,SKGDocument::MessageType iType,const QString & iAction,bool iAutoKillOnClick)2369 KMessageWidget* SKGMainPanel::getMessageWidget(const QString& iMessage, SKGDocument::MessageType iType, const QString& iAction, bool iAutoKillOnClick)
2370 {
2371     KMessageWidget* msg = nullptr;
2372     if (!iMessage.isEmpty()) {
2373         msg = new KMessageWidget(this);
2374         msg->setText(iMessage);
2375         msg->setIcon(SKGServices::fromTheme(iType == SKGDocument::Positive ? QStringLiteral("dialog-positive") :
2376                                             iType == SKGDocument::Information ? QStringLiteral("dialog-information") :
2377                                             iType == SKGDocument::Warning ? QStringLiteral("dialog-warning") : QStringLiteral("dialog-error")));
2378         msg->setMessageType(static_cast<KMessageWidget::MessageType>(iType));
2379         if (!iAction.isEmpty()) {
2380             QUrl url(iAction);
2381             if (url.scheme() == QStringLiteral("skg")) {
2382                 QAction* action = SKGMainPanel::getMainPanel()->getGlobalAction(url.host(), false);
2383                 QAction* act = nullptr;
2384                 if (action != nullptr) {
2385                     // This is an action
2386                     act = new QAction(action->icon(), action->text(), SKGMainPanel::getMainPanel());
2387                 } else {
2388                     // This is a default action
2389                     act = new QAction(SKGServices::fromTheme(QStringLiteral("open")), i18nc("Verb", "Open ..."), SKGMainPanel::getMainPanel());
2390                 }
2391 
2392                 act->setData(iAction);
2393 
2394                 msg->addAction(act);
2395                 connect(act, &QAction::triggered, this, [ = ] { openPage(QUrl(qobject_cast< QAction* >(sender())->data().toString()), true);});
2396                 if (iAutoKillOnClick) {
2397                     connect(act, &QAction::triggered, msg, &KMessageWidget::deleteLater, Qt::QueuedConnection);
2398                 }
2399             }
2400         }
2401     }
2402     return msg;
2403 }
2404 
displayMessage(const QString & iMessage,SKGDocument::MessageType iType,const QString & iAction)2405 KMessageWidget* SKGMainPanel::displayMessage(const QString& iMessage, SKGDocument::MessageType iType, const QString& iAction)
2406 {
2407     // Create message widget
2408     KMessageWidget* msg = nullptr;
2409     if (!iMessage.isEmpty()) {
2410         msg = getMessageWidget(iMessage, iType, iAction, true);
2411         QTimer::singleShot(iType == SKGDocument::Positive ? 5000 : iType == SKGDocument::Information ? 10000 : 20000, Qt::CoarseTimer, msg, &KMessageWidget::deleteLater);
2412         msg->show();
2413         d->m_mainLayout->insertWidget(qMax(d->m_mainLayout->indexOf(d->m_mainWidget) - 1, 0), msg);
2414 
2415         // Store message
2416         auto msg2 = getMessageWidget(iMessage, iType, iAction, false);
2417         auto* l = qobject_cast< QVBoxLayout* >(d->ui.kMessagesLayout->layout());
2418         if (l != nullptr) {
2419             l->insertWidget(0, msg2);
2420         }
2421     }
2422 
2423     // Emit message
2424     //  [Event/error]
2425     //  [Event/neutral]
2426     //  [Event/positive]
2427     auto notification = new KNotification(iType == SKGDocument::Error ? QStringLiteral("error") :
2428                                           (iType == SKGDocument::Positive ? QStringLiteral("positive") :
2429                                            (iType == SKGDocument::Warning ? QStringLiteral("negative") :
2430                                             QStringLiteral("neutral"))));
2431     notification->setWidget(this);
2432     notification->setText(iMessage);
2433     notification->sendEvent();
2434 
2435     // Alert
2436     if (iType == SKGDocument::Error || iType == SKGDocument::Warning) {
2437         qApp->alert(this);
2438     }
2439 
2440     return msg;
2441 }
2442 
displayErrorMessage(const QString & iMessage)2443 KMessageWidget* SKGMainPanel::displayErrorMessage(const QString& iMessage)
2444 {
2445     QString msg = iMessage;
2446     if (msg.isEmpty()) {
2447         auto* act = qobject_cast< QAction* >(sender());
2448         if (act != nullptr) {
2449             msg = act->data().toString();
2450         }
2451     }
2452     return displayMessage(msg, SKGDocument::Error);
2453 }
2454 
displayErrorMessage(const SKGError & iError,bool iNotifyIfNoError)2455 KMessageWidget* SKGMainPanel::displayErrorMessage(const SKGError& iError, bool iNotifyIfNoError)
2456 {
2457     return displayErrorMessage(iError, nullptr, iNotifyIfNoError);
2458 }
2459 
displayErrorMessage(const SKGError & iError,QAction * iAction,bool iNotifyIfNoError)2460 KMessageWidget* SKGMainPanel::displayErrorMessage(const SKGError& iError, QAction* iAction, bool iNotifyIfNoError)
2461 {
2462     SKGTRACEINFUNC(1)
2463     KMessageWidget* msg = nullptr;
2464     SKGMainPanel* parent = SKGMainPanel::getMainPanel();
2465     if (parent != nullptr) {
2466         if (iError) {
2467             // Get the message
2468             //BUG 423311: Remove history button
2469             msg = parent->displayMessage(iError.getFullMessageWithHistorical(), SKGDocument::Error, iError.getAction());
2470 
2471             // Add history action in case of
2472             /*if (iError.getHistoricalSize() != 0) {
2473                 auto history = new QAction(i18nc("Noun", "History"), msg);
2474                 history->setIcon(SKGServices::fromTheme(QStringLiteral("dialog-information")));
2475                 history->setData(iError.getFullMessageWithHistorical());
2476                 msg->addAction(history);
2477                 connect(history, &QAction::triggered, parent, [ = ] { parent->displayErrorMessage();});
2478                 connect(history, &QAction::triggered, msg, &KMessageWidget::deleteLater, Qt::QueuedConnection);
2479             }*/
2480 
2481             // Add the additional action
2482             if (iAction != nullptr) {
2483                 iAction->setParent(msg);
2484                 msg->addAction(iAction);
2485                 connect(iAction, &QAction::triggered, msg, &KMessageWidget::deleteLater, Qt::QueuedConnection);
2486             }
2487         } else {
2488             if (iNotifyIfNoError) {
2489                 auto notification = new KNotification(QStringLiteral("positive"));
2490                 notification->setWidget(parent);
2491                 notification->setText(iError.getFullMessage());
2492                 notification->sendEvent();
2493             }
2494             // Status bar
2495             QLabel* label = parent->statusNormalMessage();
2496             QString message = iError.getMessage();
2497             if ((label != nullptr) && !message.isEmpty()) {
2498                 label->setText(message);
2499             }
2500         }
2501     }
2502     return msg;
2503 }
2504 
onCancelCurrentAction()2505 void SKGMainPanel::onCancelCurrentAction()
2506 {
2507     SKGMainPanelPrivate::m_currentActionCanceled = true;
2508 }
2509 
onQuitAction()2510 void SKGMainPanel::onQuitAction()
2511 {
2512     // Bug 2777697: To be sure that all page modifications are closed
2513     closeAllPages(true);
2514     // Bug 2777697:
2515 
2516     qApp->closeAllWindows();
2517 }
2518 
getSaveFileName(const QString & iStartDir,const QString & iFilter,QWidget * iParent,QString * iCodec)2519 QString SKGMainPanel::getSaveFileName(const QString& iStartDir, const QString& iFilter, QWidget* iParent, QString* iCodec)
2520 {
2521     QString fileName;
2522 
2523     QString lastCodecUsed = QTextCodec::codecForLocale()->name();
2524     KEncodingFileDialog::Result result = KEncodingFileDialog::getSaveUrlAndEncoding(lastCodecUsed, QUrl(iStartDir), iFilter, iParent);
2525     if (!result.URLs.isEmpty()) {
2526         fileName = result.URLs.at(0).toLocalFile();
2527     }
2528     if (iCodec != nullptr) {
2529         *iCodec = result.encoding;
2530     }
2531     if (fileName.isEmpty()) {
2532         return QLatin1String("");
2533     }
2534     QFile f(fileName);
2535     if (f.exists() && KMessageBox::warningContinueCancel(iParent,
2536             i18nc("Question", "File <b>%1</b> already exists. Do you really want to overwrite it?", fileName),
2537             i18nc("Question", "Warning"),
2538             KGuiItem(i18nc("Verb",  "Save"), SKGServices::fromTheme(QStringLiteral("document-save")))) != KMessageBox::Continue) {
2539         return QLatin1String("");
2540     }
2541 
2542     return fileName;
2543 }
2544 
fillWithDistinctValue(const QList<QWidget * > & iWidgets,SKGDocument * iDoc,const QString & iTable,const QString & iAttribut,const QString & iWhereClause,bool iAddoperators)2545 void SKGMainPanel::fillWithDistinctValue(
2546     const QList<QWidget*>& iWidgets,
2547     SKGDocument* iDoc,
2548     const QString& iTable,
2549     const QString& iAttribut,
2550     const QString& iWhereClause,
2551     bool iAddoperators)
2552 {
2553     SKGTRACEINFUNC(10)
2554 
2555     if (iDoc != nullptr) {
2556         {
2557             // Get list
2558             QStringList list;
2559             {
2560                 SKGTRACEIN(10, "SKGMainPanel::fillWithDistinctValue-build list " % iTable % " " % iAttribut)
2561                 iDoc->getDistinctValues(iTable, iAttribut, iWhereClause, list);
2562                 if (!list.isEmpty() && !list.at(0).isEmpty()) {
2563                     list.insert(0, QLatin1String(""));
2564                 }
2565 
2566                 // Sorting list
2567                 {
2568                     SKGTRACEIN(10, "SKGMainPanel::fillWithDistinctValue-build list sorting " % iTable % " " % iAttribut)
2569                     // Correction bug 202341 vvv
2570                     QCollator c;
2571                     std::sort(list.begin(), list.end(), [&](const QString & a, const QString & b) {
2572                         return c.compare(a, b) < 0;
2573                     });
2574                 }
2575 
2576                 // Add operator
2577                 if (iAddoperators) {
2578                     list.push_back('=' % i18nc("Key word to modify a string into a field", "capitalize"));
2579                     list.push_back('=' % i18nc("Key word to modify a string into a field", "capwords"));
2580                     list.push_back('=' % i18nc("Key word to modify a string into a field", "lower"));
2581                     list.push_back('=' % i18nc("Key word to modify a string into a field", "trim"));
2582                     list.push_back('=' % i18nc("Key word to modify a string into a field", "upper"));
2583                 }
2584             }
2585 
2586             {
2587                 SKGTRACEIN(10, "SKGMainPanel::fillWithDistinctValue-fill " % iTable % " " % iAttribut)
2588                 SKGTRACEL(10) << "list.count()=" << list.count() << SKGENDL;
2589                 for (auto w : qAsConst(iWidgets)) {
2590                     auto comp = new QCompleter(list, w);
2591                     if (comp != nullptr) {
2592                         comp->setCaseSensitivity(Qt::CaseInsensitive);
2593                         comp->setFilterMode(Qt::MatchContains);
2594 
2595                         // Fill completion
2596                         auto* kcmb = qobject_cast<KComboBox*> (w);
2597                         if (kcmb != nullptr) {
2598                             // Fill combo
2599                             kcmb->clear();
2600                             kcmb->addItems(list);
2601 
2602                             if (kcmb->isEditable()) {
2603                                 kcmb->setCompleter(comp);
2604                             }
2605                         } else {
2606                             auto* kline = qobject_cast<QLineEdit*> (w);
2607                             if (kline != nullptr) {
2608                                 kline->setClearButtonEnabled(true);
2609                                 kline->setCompleter(comp);
2610                             }
2611                         }
2612                     }
2613                 }
2614             }
2615         }
2616     }
2617 }
2618 
getMainPanel()2619 SKGMainPanel* SKGMainPanel::getMainPanel()
2620 {
2621     return SKGMainPanelPrivate::m_mainPanel;
2622 }
2623 
onZoomChanged()2624 void SKGMainPanel::onZoomChanged()
2625 {
2626     SKGTabPage* toSave = currentPage();
2627     if (toSave != nullptr) {
2628         toSave->setZoomPosition(d->m_zoomSelector->value());
2629         d->m_zoomSelector->setValue(toSave->zoomPosition());  // In case of a limit is reached
2630     }
2631 }
2632 
setMainWidget(QWidget * iWidget)2633 void SKGMainPanel::setMainWidget(QWidget* iWidget)
2634 {
2635     if (d->m_mainWidget == nullptr && d->m_mainLayout != nullptr && iWidget != nullptr) {
2636         d->m_mainWidget = iWidget;
2637         d->m_mainLayout->addWidget(d->m_mainWidget);
2638 
2639         // Show/hide main widget
2640         d->m_tabWidget->setVisible(d->m_tabWidget->count() != 0);
2641         if (d->m_mainWidget != nullptr) {
2642             d->m_mainWidget->setVisible(!d->m_tabWidget->isVisible());
2643         }
2644     }
2645 }
2646 
getTabWidget() const2647 SKGTabWidget* SKGMainPanel::getTabWidget() const
2648 {
2649     return d->m_tabWidget;
2650 }
2651 
onClearMessages()2652 void SKGMainPanel::onClearMessages()
2653 {
2654     QLayout* l = d->ui.kMessagesLayout->layout();
2655     if (l != nullptr) {
2656         // Remove all item of the layout
2657         while (l->count() > 1) {
2658             QLayoutItem* child = l->takeAt(0);
2659             if (child != nullptr) {
2660                 QWidget* w = child->widget();
2661                 delete w;
2662                 delete child;
2663             }
2664         }
2665     }
2666 }
2667 
onMigrateToSQLCipher()2668 void SKGMainPanel::onMigrateToSQLCipher()
2669 {
2670     SKGError err;
2671     SKGTRACEINFUNCRC(10, err)
2672     if (getDocument()->isFileModified()) {
2673         err = SKGError(ERR_ABORT, i18nc("An information message",  "The document must be saved to be migrated."), QStringLiteral("skg://file_save"));
2674     } else {
2675         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
2676 
2677         // Set parameters
2678         QString input = getDocument()->getCurrentFileName();
2679         QString tmp = input % ".sqlcipher";
2680         QString output = input % "_migrated.skg";
2681         output = output.replace(QStringLiteral(".skg_migrated"), QStringLiteral("_migrated"));
2682 
2683         // Build argument
2684         QStringList arg;
2685         arg.push_back(QStringLiteral("--in"));
2686         arg.push_back(input);
2687         arg.push_back(QStringLiteral("--out"));
2688         arg.push_back(tmp);
2689 
2690 
2691         QString password = getDocument()->getPassword();
2692         if (!password.isEmpty()) {
2693             arg.push_back(QStringLiteral("--param"));
2694             arg.push_back(QStringLiteral("password"));
2695             arg.push_back(QStringLiteral("--value"));
2696             arg.push_back(password);
2697             password = " --param password --value \"" % password % "\"";
2698         }
2699 
2700         // Conversion skg => sqlcipher
2701         QString cmd = "skroogeconvert --in \"" % input % "\" --out \"" % tmp % "\"" % password;
2702         int rc = QProcess::execute(QStringLiteral("skroogeconvert"), arg);
2703         if (rc != 0) {
2704             err.setReturnCode(ERR_FAIL).setMessage(i18nc("Error message",  "The following command line failed with code %2:\n'%1'", cmd, rc));
2705         } else {
2706             cmd = "skroogeconvert --in \"" % tmp % "\" --out \"" % output % "\"" % password;
2707             arg[1] = tmp;
2708             arg[3] = output;
2709             rc = QProcess::execute(QStringLiteral("skroogeconvert"), arg);
2710             if (rc != 0) {
2711                 err.setReturnCode(ERR_FAIL).setMessage(i18nc("Error message",  "The following command line failed with code %2:\n'%1'", cmd, rc));
2712             } else {
2713                 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);
2714                 notify();
2715             }
2716         }
2717         QFile(tmp).remove();
2718 
2719         QApplication::restoreOverrideCursor();
2720     }
2721 
2722     // Display error
2723     SKGMainPanel::displayErrorMessage(err);
2724 }
2725 
dateToString(QDate iDate)2726 QString SKGMainPanel::dateToString(QDate  iDate)
2727 {
2728     switch (skgbasegui_settings::date_format()) {
2729     case 0:
2730         return QLocale().toString(iDate, QLocale::ShortFormat);
2731     case 1:
2732         return QLocale().toString(iDate, QLocale::LongFormat);
2733     case 3:
2734         return KFormat().formatRelativeDate(iDate, QLocale::LongFormat);
2735     case 4:
2736         return iDate.toString(Qt::ISODate);
2737     case 2:
2738     default:
2739         return KFormat().formatRelativeDate(iDate, QLocale::ShortFormat);
2740     }
2741 }
2742