File indexing completed on 2025-02-02 04:56:52

0001 /***************************************************************************
0002  * SPDX-FileCopyrightText: 2022 S. MANKOWSKI stephane@mankowski.fr
0003  * SPDX-FileCopyrightText: 2022 G. DE BURE support@mankowski.fr
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  ***************************************************************************/
0006 /** @file
0007  * This file is a plugin for bookmarks management.
0008  *
0009  * @author Stephane MANKOWSKI / Guillaume DE BURE
0010  */
0011 #include "skgbookmarkplugin.h"
0012 
0013 #include <qwidget.h>
0014 
0015 #include <kaboutdata.h>
0016 #include <kactioncollection.h>
0017 #include <kpluginfactory.h>
0018 #include <ktoolbarpopupaction.h>
0019 
0020 #include "skgbookmark_settings.h"
0021 #include "skgbookmarkplugindockwidget.h"
0022 #include "skgerror.h"
0023 #include "skgmainpanel.h"
0024 #include "skgnodeobject.h"
0025 #include "skgservices.h"
0026 #include "skgtraces.h"
0027 #include "skgtransactionmng.h"
0028 
0029 /**
0030  * This plugin factory.
0031  */
0032 K_PLUGIN_CLASS_WITH_JSON(SKGBookmarkPlugin, "metadata.json")
0033 
0034 SKGBookmarkPlugin::SKGBookmarkPlugin(QWidget* iWidget, QObject* iParent, const QVariantList& iArg) :
0035     SKGInterfacePlugin(iParent), m_currentDocument(nullptr), m_dockWidget(nullptr), m_bookmarkMenu(nullptr)
0036 {
0037     Q_UNUSED(iArg)
0038     Q_UNUSED(iWidget)
0039     SKGTRACEINFUNC(10)
0040 }
0041 
0042 SKGBookmarkPlugin::~SKGBookmarkPlugin()
0043 {
0044     SKGTRACEINFUNC(10)
0045     m_currentDocument = nullptr;
0046     m_dockWidget = nullptr;
0047     m_bookmarkMenu = nullptr;
0048 }
0049 
0050 bool SKGBookmarkPlugin::setupActions(SKGDocument* iDocument)
0051 {
0052     SKGTRACEINFUNC(10)
0053 
0054     m_currentDocument = iDocument;
0055 
0056     setComponentName(QStringLiteral("skg_bookmark"), title());
0057     setXMLFile(QStringLiteral("skg_bookmark.rc"));
0058 
0059     m_dockWidget = new QDockWidget(SKGMainPanel::getMainPanel());
0060     m_dockWidget->setObjectName(QStringLiteral("skg_bookmark_docwidget"));
0061     m_dockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
0062     m_dockWidget->setWindowTitle(title());
0063     m_dockWidget->setWidget(new SKGBookmarkPluginDockWidget(m_dockWidget, m_currentDocument));
0064 
0065     // add action to control hide / display of Bookmarks
0066     QAction* toggle = m_dockWidget->toggleViewAction();
0067     QAction* panelAction = actionCollection()->addAction(QStringLiteral("view_bookmarks"));
0068     registerGlobalAction(QStringLiteral("view_bookmarks"), panelAction);
0069     panelAction->setCheckable(true);
0070     panelAction->setChecked(toggle->isChecked());
0071     panelAction->setText(toggle->text());
0072     actionCollection()->setDefaultShortcut(panelAction, Qt::SHIFT + Qt::Key_F10);
0073     connect(panelAction, &QAction::triggered, toggle, &QAction::trigger);
0074     connect(toggle, &QAction::toggled, panelAction, &QAction::setChecked);
0075 
0076     // Import bookmarks
0077     QStringList overlaybookmarks;
0078     overlaybookmarks.push_back(icon());
0079 
0080     auto actImportStdBookmark = new QAction(SKGServices::fromTheme(QStringLiteral("document-import"), overlaybookmarks), i18nc("Verb", "Import standard bookmarks"), this);
0081     connect(actImportStdBookmark, &QAction::triggered, this, &SKGBookmarkPlugin::importStandardBookmarks);
0082     registerGlobalAction(QStringLiteral("import_standard_bookmarks"), actImportStdBookmark);
0083 
0084     //
0085     QAction* goHomeAction = KStandardAction::home(this, SLOT(goHome()), actionCollection());
0086     goHomeAction->setPriority(QAction::LowPriority);
0087     registerGlobalAction(QStringLiteral("go_home"), goHomeAction);
0088 
0089     // Bookmark
0090     auto actBookmark = new KToolBarPopupAction(SKGServices::fromTheme(QStringLiteral("bookmark-new-list")), i18nc("Verb, action to display bookmarks", "Bookmarks"), this);
0091     connect(actBookmark, &KToolBarPopupAction::triggered, this, &SKGBookmarkPlugin::onOpenBookmark);
0092     m_bookmarkMenu = actBookmark->menu();
0093     if (m_bookmarkMenu != nullptr) {
0094         m_bookmarkMenu->setProperty("id", 0);
0095         connect(m_bookmarkMenu, &QMenu::aboutToShow, this, &SKGBookmarkPlugin::onShowBookmarkMenu);
0096     }
0097 
0098     actBookmark->setStickyMenu(false);
0099     actBookmark->setDelayed(false);
0100     actBookmark->setData(0);
0101     actBookmark->setPriority(QAction::LowPriority);
0102 
0103     registerGlobalAction(QStringLiteral("edit_bookmark"), actBookmark);
0104 
0105     return true;
0106 }
0107 
0108 QWidget* SKGBookmarkPlugin::getPreferenceWidget()
0109 {
0110     SKGTRACEINFUNC(10)
0111     auto w = new QWidget();
0112     ui.setupUi(w);
0113     return w;
0114 }
0115 
0116 KConfigSkeleton* SKGBookmarkPlugin::getPreferenceSkeleton()
0117 {
0118     return skgbookmark_settings::self();
0119 }
0120 
0121 void SKGBookmarkPlugin::refresh()
0122 {
0123     SKGTRACEINFUNC(10)
0124     if (m_dockWidget != nullptr) {
0125         auto* p = qobject_cast<SKGBookmarkPluginDockWidget*>(m_dockWidget->widget());
0126         if (p != nullptr) {
0127             p->refresh();
0128         }
0129     }
0130 
0131     if (m_currentDocument != nullptr) {
0132         // Automatic bookmarks creation
0133         if (m_currentDocument->getMainDatabase() != nullptr) {
0134             QString doc_id = m_currentDocument->getUniqueIdentifier();
0135             if (m_docUniqueIdentifier != doc_id) {
0136                 m_docUniqueIdentifier = doc_id;
0137 
0138                 bool exist = false;
0139                 SKGError err = m_currentDocument->existObjects(QStringLiteral("node"), QLatin1String(""), exist);
0140                 if (!err && !exist) {
0141                     importStandardBookmarks();
0142 
0143                     // The file is considered has not modified
0144                     m_currentDocument->setFileNotModified();
0145                 }
0146 
0147                 // Automatic open of autostart bookmark
0148                 if (!err && !(QApplication::keyboardModifiers() &Qt::ShiftModifier)) {
0149                     goHome();
0150                 }
0151             }
0152         }
0153     }
0154 }
0155 
0156 QString SKGBookmarkPlugin::title() const
0157 {
0158     return i18nc("Noun, a bookmark as in a webbrowser bookmark", "Bookmarks");
0159 }
0160 
0161 QString SKGBookmarkPlugin::icon() const
0162 {
0163     return QStringLiteral("bookmarks");
0164 }
0165 
0166 QStringList SKGBookmarkPlugin::tips() const
0167 {
0168     QStringList output;
0169     output.push_back(i18nc("Description of a tips", "<p>… some bookmarks can be opened automatically when the application is launched.</p>"));
0170     output.push_back(i18nc("Description of a tips", "<p>… bookmarks can be reorganized by drag & drop.</p>"));
0171     output.push_back(i18nc("Description of a tips", "<p>… a double click on a folder of bookmarks will open all the bookmarks it contains.</p>"));
0172     output.push_back(i18nc("Description of a tips", "<p>… you can <a href=\"skg://import_standard_bookmarks\">import standard bookmarks</a>.</p>"));
0173     return output;
0174 }
0175 
0176 int SKGBookmarkPlugin::getOrder() const
0177 {
0178     return 3;
0179 }
0180 
0181 QDockWidget* SKGBookmarkPlugin::getDockWidget()
0182 {
0183     return m_dockWidget;
0184 }
0185 
0186 void SKGBookmarkPlugin::importStandardBookmarks()
0187 {
0188     SKGTRACEINFUNC(10)
0189     SKGError err;
0190     SKGBEGINTRANSACTION(*m_currentDocument, i18nc("Noun, name of the user action", "Import standard bookmarks"), err)
0191 
0192     QStringList bks;
0193     bks << i18nc("Noun, bookmark name", "Dashboard") % "|N|dashboard-show|\"Dashboard plugin\";\"Dashboard\";\"<!DOCTYPE SKGML><parameters zoomPosition=\"\"0\"\"> <ITEM-1 zoom=\"\"0\"\" index=\"\"0\"\" name=\"\"Skrooge bank plugin\"\" state=\"\"&lt;!DOCTYPE SKGML>&#xa;&lt;parameters menuOther=&quot;Y&quot; menuCurrent=&quot;Y&quot; menuFavorite=&quot;N&quot; menuAssets=&quot;Y&quot; menuInvestment=&quot;Y&quot; menuCreditCard=&quot;Y&quot;/>&#xa;\"\"/> <ITEM-2 zoom=\"\"0\"\" index=\"\"0\"\" name=\"\"Skrooge operation plugin\"\" state=\"\"&lt;!DOCTYPE SKGML>"
0194         "&#xa;&lt;parameters menuTransfert=&quot;Y&quot;/>&#xa;\"\"/> <ITEM-3 zoom=\"\"0\"\" index=\"\"1\"\" name=\"\"Skrooge operation plugin\"\" state=\"\"\"\"/> <ITEM-4 zoom=\"\"0\"\" index=\"\"0\"\" name=\"\"Skrooge scheduled plugin\"\" state=\"\"\"\"/> <ITEM-5 zoom=\"\"0\"\" index=\"\"0\"\" name=\"\"Skrooge search plugin\"\" state=\"\"&lt;!DOCTYPE SKGML>"
0195         "&#xa;&lt;parameters menuFavorite=&quot;N&quot;/>&#xa;\"\"/> <ITEM-6 zoom=\"\"0\"\" index=\"\"0\"\" name=\"\"Skrooge unit plugin\"\" state=\"\"&lt;!DOCTYPE SKGML>&#xa;&lt;parameters menuShares=&quot;Y&quot; menuSharesOwnedOnly=&quot;N&quot; menuIndexes=&quot;Y&quot;/>&#xa;\"\"/> <ITEM-7 zoom=\"\"0\"\" index=\"\"0\"\" name=\"\"Skrooge calculator plugin\"\" state=\"\"\"\"/></parameters>\""
0196         << i18nc("Noun, bookmark name", "Report > Income vs Expenditure on 12 last months") % "|N|view-statistics|\"Skrooge report plugin\";\"Rapport\";\"<!DOCTYPE SKGML><parameters  expenses=\"\"Y\"\" tableAndGraphState=\"\"&lt;!DOCTYPE SKGML>"
0197         "&#xa;&lt;parameters graphMode=&quot;1&quot; graphicViewState=&quot;&amp;lt;!DOCTYPE SKGML>&amp;#xa;&amp;lt;parameters isToolBarVisible=&amp;quot;Y&amp;quot; antialiasing=&amp;quot;Y&amp;quot;/>&amp;#xa;&quot; splitterState=&quot;000000ff00000000000000020000025b0000032901000000060100000001&quot; bottomToolBarVisible=&quot;Y&quot; filter=&quot;&quot; legendVisible=&quot;N&quot; allPositive=&quot;Y&quot; linearRegressionVisible=&quot;Y&quot; sortColumn=&quot;0&quot; limitVisible=&quot;Y&quot; web=&quot;&amp;lt;!DOCTYPE SKGML>"
0198         "&amp;#xa;&amp;lt;parameters zoomFactor=&amp;quot;0&amp;quot;/>&amp;#xa;&quot; sortOrder=&quot;0&quot;/>&#xa;\"\" mode=\"\"0\"\" period=\"\"3\"\" incomes=\"\"Y\"\" interval=\"\"2\"\" lines=\"\"t_TYPEEXPENSENLS\"\" lines2=\"\"#NOTHING#\"\" nbLevelLines=\"\"0\"\" nbLevelColumns=\"\"0\"\" forecast=\"\"0\"\" columns=\"\"d_DATEMONTH\"\" nb_intervals=\"\"12\"\" forecastValue=\"\"20\"\" transfers=\"\"N\"\" currentPage=\"\"0\"\"/>\""
0199         << i18nc("Noun, bookmark name", "Report > Pie categories in 12 last months") % "|N|view-statistics|\"Skrooge report plugin\";\"Rapport\";\"<!DOCTYPE SKGML><parameters expenses=\"\"Y\"\" tableAndGraphState=\"\"&lt;!DOCTYPE SKGML>&#xa;&lt;parameters graphMode=&quot;2&quot; graphicViewState=&quot;&amp;lt;!DOCTYPE SKGML>"
0200         "&amp;#xa;&amp;lt;parameters isToolBarVisible=&amp;quot;Y&amp;quot; antialiasing=&amp;quot;Y&amp;quot;/>&amp;#xa;&quot; splitterState=&quot;000000ff00000000000000020000025b0000032901000000060100000001&quot; bottomToolBarVisible=&quot;Y&quot; filter=&quot;&quot; legendVisible=&quot;N&quot; allPositive=&quot;Y&quot; linearRegressionVisible=&quot;Y&quot; sortColumn=&quot;-1&quot; limitVisible=&quot;Y&quot; web=&quot;&amp;lt;!DOCTYPE SKGML>"
0201         "&amp;#xa;&amp;lt;parameters zoomFactor=&amp;quot;0&amp;quot;/>&amp;#xa;&quot; sortOrder=&quot;0&quot;/>&#xa;\"\" mode=\"\"0\"\" period=\"\"3\"\" incomes=\"\"Y\"\" interval=\"\"2\"\" lines=\"\"t_REALCATEGORY\"\" lines2=\"\"#NOTHING#\"\" nbLevelLines=\"\"0\"\" nbLevelColumns=\"\"0\"\" forecast=\"\"0\"\" columns=\"\"d_DATEMONTH\"\" nb_intervals=\"\"12\"\" forecastValue=\"\"20\"\" transfers=\"\"N\"\" currentPage=\"\"0\"\"/>\""
0202         << i18nc("Noun, bookmark name", "Report > History") % "|N|view-statistics|\"Skrooge report plugin\";\"Rapport\";\"<!DOCTYPE SKGML><parameters expenses=\"\"Y\"\" tableAndGraphState=\"\"&lt;!DOCTYPE SKGML>"
0203         "&#xa;&lt;parameters graphMode=&quot;1&quot; graphicViewState=&quot;&amp;lt;!DOCTYPE SKGML>&amp;#xa;&amp;lt;parameters isToolBarVisible=&amp;quot;Y&amp;quot; antialiasing=&amp;quot;Y&amp;quot;/>&amp;#xa;&quot; splitterState=&quot;000000ff0000000000000002000002b50000032601000000060100000001&quot; bottomToolBarVisible=&quot;Y&quot; filter=&quot;&quot; legendVisible=&quot;N&quot; allPositive=&quot;N&quot; linearRegressionVisible=&quot;Y&quot; sortColumn=&quot;-1&quot; limitVisible=&quot;Y&quot; web=&quot;&amp;lt;!DOCTYPE SKGML>"
0204         "&amp;#xa;&amp;lt;parameters zoomFactor=&amp;quot;0&amp;quot;/>&amp;#xa;&quot; sortOrder=&quot;0&quot;/>&#xa;\"\" mode=\"\"1\"\" period=\"\"0\"\" incomes=\"\"Y\"\" interval=\"\"2\"\" lines=\"\"#NOTHING#\"\" lines2=\"\"#NOTHING#\"\" nbLevelLines=\"\"0\"\" nbLevelColumns=\"\"0\"\" forecast=\"\"0\"\" columns=\"\"d_DATEMONTH\"\" nb_intervals=\"\"1\"\" forecastValue=\"\"20\"\" transfers=\"\"Y\"\" currentPage=\"\"0\"\"/>\"";
0205 
0206 
0207     for (const auto& bk : qAsConst(bks)) {
0208         QStringList line = SKGServices::splitCSVLine(bk, '|', false);
0209         if (line.count() == 4) {
0210             SKGNodeObject node;
0211             err = SKGNodeObject::createPathNode(m_currentDocument, line.at(0), node);
0212             IFOKDO(err, node.setAutoStart(line.at(1) == QStringLiteral("Y")))
0213             IFOKDO(err, node.setIcon(line.at(2)))
0214             IFOKDO(err, node.setData(line.at(3)))
0215             IFOKDO(err, node.save())
0216         }
0217     }
0218 
0219     // status
0220     IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Standard bookmarks imported.")))
0221     else {
0222         err.addError(ERR_FAIL, i18nc("Error message",  "Import standard bookmarks failed"));
0223     }
0224 
0225     // Display error
0226     SKGMainPanel::displayErrorMessage(err);
0227 }
0228 
0229 void SKGBookmarkPlugin::goHome()
0230 {
0231     if (SKGMainPanel::getMainPanel() != nullptr) {
0232         SKGMainPanel::getMainPanel()->closeAllPages(false);    // Home do not close pinned pages
0233     }
0234 
0235     SKGObjectBase::SKGListSKGObjectBase oNodeList;
0236     if (m_currentDocument != nullptr) {
0237         m_currentDocument->getObjects(QStringLiteral("v_node"), QStringLiteral("t_autostart='Y' ORDER BY f_sortorder, t_name"), oNodeList);
0238     }
0239     int nbAutoStartedBookmarks = oNodeList.count();
0240     for (int i = 0; i < nbAutoStartedBookmarks; ++i) {
0241         SKGNodeObject autostarted_bookmark(oNodeList.at(i));
0242         autostarted_bookmark.load();
0243         SKGTRACEIN(10, "autostarting bookmark : " % autostarted_bookmark.getName())
0244         SKGBookmarkPluginDockWidget::openBookmark(autostarted_bookmark, i > 0, true);
0245     }
0246 }
0247 
0248 void SKGBookmarkPlugin::onOpenBookmark()
0249 {
0250     auto* callerAction = qobject_cast<QAction*>(this->sender());
0251     if (callerAction != nullptr) {
0252         SKGNodeObject node(m_currentDocument, callerAction->data().toInt());
0253         SKGBookmarkPluginDockWidget::openBookmark(node, (QGuiApplication::mouseButtons() & Qt::MidButton) != 0u || (QApplication::keyboardModifiers() & Qt::ControlModifier) != 0u);
0254     }
0255 }
0256 
0257 void SKGBookmarkPlugin::onAddBookmark()
0258 {
0259     SKGTRACEINFUNC(1)
0260     SKGError err;
0261 
0262     // Get current page
0263     SKGNodeObject node;
0264     {
0265         // Get current selection
0266         SKGNodeObject parentNode;
0267         auto* callerAction = qobject_cast<QAction*>(this->sender());
0268         if (callerAction != nullptr) {
0269             parentNode = SKGNodeObject(m_currentDocument, callerAction->data().toInt());
0270         }
0271 
0272         // Create bookmark
0273         err =  SKGBookmarkPluginDockWidget::createNodeFromPage(SKGMainPanel::getMainPanel()->currentPage(), parentNode, node);
0274     }
0275     // status bar
0276     IFOK(err) {
0277         err = SKGError(0, i18nc("Successful message after an user action", "Bookmark created"));
0278     }
0279     SKGMainPanel::displayErrorMessage(err);
0280 }
0281 
0282 void SKGBookmarkPlugin::onShowBookmarkMenu()
0283 {
0284     auto* callerMenu = qobject_cast<QMenu*>(this->sender());
0285     if ((callerMenu != nullptr) && (m_currentDocument != nullptr)) {
0286         // Remove previous menu
0287         callerMenu->clear();
0288 
0289         // Build query
0290         QString wc = QStringLiteral("rd_node_id=0 OR rd_node_id IS NULL OR rd_node_id=''");
0291         int idParent = callerMenu->property("id").toInt();
0292         if (idParent != 0) {
0293             wc = "rd_node_id=" % SKGServices::intToString(idParent);
0294         }
0295 
0296         // Build new menu
0297         SKGObjectBase::SKGListSKGObjectBase listNode;
0298         m_currentDocument->getObjects(QStringLiteral("v_node"), wc % " ORDER BY f_sortorder, t_name", listNode);
0299         int nb = listNode.count();
0300         for (int i = 0; i < nb; ++i) {
0301             SKGNodeObject node(listNode.at(i));
0302             if (node.isFolder()) {
0303                 // This is a folder
0304                 auto menu = new QMenu(callerMenu);
0305                 if (menu != nullptr) {
0306                     callerMenu->addMenu(menu);
0307                     menu->setTitle(node.getName());
0308                     menu->setIcon(node.getIcon());
0309                     menu->setProperty("id", node.getID());
0310                     connect(menu, &QMenu::aboutToShow, this, &SKGBookmarkPlugin::onShowBookmarkMenu);
0311                 }
0312             } else {
0313                 // This is a bookmark
0314                 auto act = new QAction(callerMenu);
0315                 if (act != nullptr) {
0316                     callerMenu->addAction(act);
0317                     act->setText(node.getName());
0318                     act->setIcon(node.getIcon());
0319                     act->setData(node.getID());
0320                     connect(act, &QAction::triggered, this, &SKGBookmarkPlugin::onOpenBookmark);
0321                 }
0322             }
0323         }
0324 
0325         // Add separator
0326         auto sep = new QAction(this);
0327         sep->setSeparator(true);
0328         callerMenu->addAction(sep);
0329 
0330         // This common actions
0331         {
0332             // Open all
0333             auto act = new QAction(callerMenu);
0334             if (act != nullptr) {
0335                 callerMenu->addAction(act);
0336                 act->setText(i18nc("Action", "Open all"));
0337                 act->setIcon(SKGServices::fromTheme(QStringLiteral("quickopen")));
0338                 act->setData(idParent);
0339                 connect(act, &QAction::triggered, this, &SKGBookmarkPlugin::onOpenBookmark);
0340             }
0341             if (SKGMainPanel::getMainPanel()->currentPageIndex() >= 0) {
0342                 // Bookmark current here
0343                 auto act2 = new QAction(callerMenu);
0344                 if (act2 != nullptr) {
0345                     callerMenu->addAction(act2);
0346                     act2->setText(i18nc("Action", "Bookmark current page here"));
0347                     act2->setIcon(SKGServices::fromTheme(QStringLiteral("list-add")));
0348                     act2->setData(idParent);
0349 
0350                     connect(act2, &QAction::triggered, this, &SKGBookmarkPlugin::onAddBookmark);
0351                 }
0352             }
0353         }
0354     }
0355 }
0356 
0357 #include <skgbookmarkplugin.moc>