Warning, file /utilities/keditbookmarks/src/toplevel.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* This file is part of the KDE project
0002    Copyright (C) 2000 David Faure <faure@kde.org>
0003    Copyright (C) 2002-2003 Alexander Kellett <lypanov@kde.org>
0004 
0005    This program is free software; you can redistribute it and/or
0006    modify it under the terms of the GNU General Public
0007    License version 2 or at your option version 3 as published by
0008    the Free Software Foundation.
0009 
0010    This program is distributed in the hope that it will be useful,
0011    but WITHOUT ANY WARRANTY; without even the implied warranty of
0012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013    General Public License for more details.
0014 
0015    You should have received a copy of the GNU General Public License
0016    along with this program; see the file COPYING.  If not, write to
0017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018    Boston, MA 02110-1301, USA.
0019 */
0020 
0021 #include "toplevel.h"
0022 #include "globalbookmarkmanager.h"
0023 #include <QVBoxLayout>
0024 
0025 #include "kbookmarkmodel/model.h"
0026 
0027 #include "actionsimpl.h"
0028 #include "bookmarkinfowidget.h"
0029 #include "exporters.h"
0030 #include "kbookmarkmodel/commandhistory.h"
0031 #include "kbookmarkmodel/commands.h"
0032 #include "kebsearchline.h"
0033 #include "settings.h"
0034 
0035 #include <stdlib.h>
0036 
0037 #include <QApplication>
0038 #include <QClipboard>
0039 #include <QSplitter>
0040 
0041 #include "keditbookmarks_debug.h"
0042 #include <KActionCollection>
0043 #include <KEditToolBar>
0044 #include <KLocalizedString>
0045 
0046 #include <QDBusConnection>
0047 KEBApp *KEBApp::s_topLevel = nullptr;
0048 
0049 KEBApp::KEBApp(const QString &bookmarksFile, bool readonly, const QString &address, bool browser, const QString &caption, const QString &dbusObjectName)
0050     : KXmlGuiWindow()
0051     , m_bookmarksFilename(bookmarksFile)
0052     , m_caption(caption)
0053     , m_dbusObjectName(dbusObjectName)
0054     , m_readOnly(readonly)
0055     , m_browser(browser)
0056 {
0057     QDBusConnection::sessionBus().registerObject(QStringLiteral("/keditbookmarks"), this, QDBusConnection::ExportScriptableSlots);
0058     Q_UNUSED(address); // FIXME sets the current item
0059 
0060     m_cmdHistory = new CommandHistory(this);
0061     m_cmdHistory->createActions(actionCollection());
0062     connect(m_cmdHistory, &CommandHistory::notifyCommandExecuted, this, &KEBApp::notifyCommandExecuted);
0063 
0064     GlobalBookmarkManager::self()->createManager(m_bookmarksFilename, m_dbusObjectName, m_cmdHistory);
0065 
0066     s_topLevel = this;
0067 
0068     createActions();
0069     if (m_browser)
0070         createGUI();
0071     else
0072         createGUI(QStringLiteral("keditbookmarks-genui.rc"));
0073 
0074     connect(qApp->clipboard(), &QClipboard::dataChanged, this, &KEBApp::slotClipboardDataChanged);
0075 
0076     m_canPaste = false;
0077 
0078     mBookmarkListView = new BookmarkListView();
0079     mBookmarkListView->setModel(GlobalBookmarkManager::self()->model());
0080     mBookmarkListView->setSelectionMode(QAbstractItemView::ExtendedSelection);
0081     mBookmarkListView->loadColumnSetting();
0082     mBookmarkListView->loadFoldedState();
0083 
0084     KViewSearchLineWidget *searchline = new KViewSearchLineWidget(mBookmarkListView, this);
0085 
0086     mBookmarkFolderView = new BookmarkFolderView(mBookmarkListView, this);
0087     mBookmarkFolderView->expandAll();
0088 
0089     QWidget *rightSide = new QWidget;
0090     QVBoxLayout *listLayout = new QVBoxLayout(rightSide);
0091     listLayout->setContentsMargins(0, 0, 0, 0);
0092     listLayout->addWidget(searchline);
0093     listLayout->addWidget(mBookmarkListView);
0094 
0095     m_bkinfo = new BookmarkInfoWidget(mBookmarkListView, GlobalBookmarkManager::self()->model());
0096 
0097     listLayout->addWidget(m_bkinfo);
0098 
0099     QSplitter *hsplitter = new QSplitter(this);
0100     hsplitter->setOrientation(Qt::Horizontal);
0101     hsplitter->addWidget(mBookmarkFolderView);
0102     hsplitter->addWidget(rightSide);
0103     hsplitter->setStretchFactor(1, 1);
0104 
0105     setCentralWidget(hsplitter);
0106 
0107     slotClipboardDataChanged();
0108     setAutoSaveSettings();
0109 
0110     connect(mBookmarkListView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &KEBApp::selectionChanged);
0111 
0112     connect(mBookmarkFolderView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &KEBApp::selectionChanged);
0113 
0114     setCancelFavIconUpdatesEnabled(false);
0115     setCancelTestsEnabled(false);
0116     updateActions();
0117 }
0118 
0119 void KEBApp::expandAll()
0120 {
0121     mBookmarkListView->expandAll();
0122 }
0123 
0124 void KEBApp::collapseAll()
0125 {
0126     mBookmarkListView->collapseAll();
0127 }
0128 
0129 QString KEBApp::bookmarkFilename()
0130 {
0131     return m_bookmarksFilename;
0132 }
0133 
0134 void KEBApp::reset(const QString &caption, const QString &bookmarksFileName)
0135 {
0136     // FIXME check this code, probably should be model()->setRoot instead of resetModel()
0137     m_caption = caption;
0138     m_bookmarksFilename = bookmarksFileName;
0139     GlobalBookmarkManager::self()->createManager(m_bookmarksFilename,
0140                                                  m_dbusObjectName,
0141                                                  m_cmdHistory); // FIXME this is still a memory leak (iff called by slotLoad)
0142     GlobalBookmarkManager::self()->model()->resetModel();
0143     updateActions();
0144 }
0145 
0146 void KEBApp::startEdit(Column c)
0147 {
0148     const QModelIndexList &list = mBookmarkListView->selectionModel()->selectedIndexes();
0149     QModelIndexList::const_iterator it, end;
0150     end = list.constEnd();
0151     for (it = list.constBegin(); it != end; ++it)
0152         if ((*it).column() == int(c) && (mBookmarkListView->model()->flags(*it) & Qt::ItemIsEditable))
0153             mBookmarkListView->edit(*it);
0154 }
0155 
0156 // FIXME clean up and remove unneeded things
0157 SelcAbilities KEBApp::getSelectionAbilities() const
0158 {
0159     SelcAbilities selctionAbilities;
0160     selctionAbilities.itemSelected = false;
0161     selctionAbilities.group = false;
0162     selctionAbilities.separator = false;
0163     selctionAbilities.urlIsEmpty = false;
0164     selctionAbilities.root = false;
0165     selctionAbilities.multiSelect = false;
0166     selctionAbilities.singleSelect = false;
0167     selctionAbilities.notEmpty = false;
0168     selctionAbilities.deleteEnabled = false;
0169 
0170     KBookmark nbk;
0171     QModelIndexList sel = mBookmarkListView->selectionModel()->selectedIndexes();
0172     int columnCount;
0173     if (sel.count()) {
0174         nbk = mBookmarkListView->bookmarkForIndex(sel.first());
0175         columnCount = mBookmarkListView->model()->columnCount();
0176     } else {
0177         sel = mBookmarkFolderView->selectionModel()->selectedIndexes();
0178         if (sel.count())
0179             nbk = mBookmarkFolderView->bookmarkForIndex(sel.first());
0180         columnCount = mBookmarkFolderView->model()->columnCount();
0181     }
0182 
0183     if (sel.count() > 0) {
0184         selctionAbilities.deleteEnabled = true;
0185         selctionAbilities.itemSelected = true;
0186         selctionAbilities.group = nbk.isGroup();
0187         selctionAbilities.separator = nbk.isSeparator();
0188         selctionAbilities.urlIsEmpty = nbk.url().isEmpty();
0189         selctionAbilities.root = nbk.address() == GlobalBookmarkManager::self()->root().address();
0190         selctionAbilities.multiSelect = (sel.count() > columnCount);
0191         selctionAbilities.singleSelect = (!selctionAbilities.multiSelect && selctionAbilities.itemSelected);
0192     }
0193     // FIXME check next line, if it actually works
0194     selctionAbilities.notEmpty = GlobalBookmarkManager::self()->root().first().hasParent();
0195 
0196     /*    //qCDebug(KEDITBOOKMARKS_LOG)
0197             <<"\nsa.itemSelected "<<selctionAbilities.itemSelected
0198             <<"\nsa.group        "<<selctionAbilities.group
0199             <<"\nsa.separator    "<<selctionAbilities.separator
0200             <<"\nsa.urlIsEmpty   "<<selctionAbilities.urlIsEmpty
0201             <<"\nsa.root         "<<selctionAbilities.root
0202             <<"\nsa.multiSelect  "<<selctionAbilities.multiSelect
0203             <<"\nsa.singleSelect "<<selctionAbilities.singleSelect
0204             <<"\nsa.deleteEnabled"<<selctionAbilities.deleteEnabled;
0205     */
0206     return selctionAbilities;
0207 }
0208 
0209 void KEBApp::setActionsEnabled(SelcAbilities sa)
0210 {
0211     KActionCollection *coll = actionCollection();
0212 
0213     QStringList toEnable;
0214 
0215     if (sa.multiSelect || (sa.singleSelect && !sa.root))
0216         toEnable << QStringLiteral("edit_copy");
0217 
0218     if (sa.multiSelect || (sa.singleSelect && !sa.root && !sa.urlIsEmpty && !sa.group && !sa.separator))
0219         toEnable << QStringLiteral("openlink");
0220 
0221     if (!m_readOnly) {
0222         if (sa.notEmpty)
0223             toEnable << QStringLiteral("testall") << QStringLiteral("updateallfavicons");
0224 
0225         if (sa.deleteEnabled && (sa.multiSelect || (sa.singleSelect && !sa.root)))
0226             toEnable << QStringLiteral("delete") << QStringLiteral("edit_cut");
0227 
0228         if (sa.singleSelect)
0229             if (m_canPaste)
0230                 toEnable << QStringLiteral("edit_paste");
0231 
0232         if (sa.multiSelect || (sa.singleSelect && !sa.root && !sa.urlIsEmpty && !sa.group && !sa.separator))
0233             toEnable << QStringLiteral("testlink") << QStringLiteral("updatefavicon");
0234 
0235         if (sa.singleSelect && !sa.root && !sa.separator) {
0236             toEnable << QStringLiteral("rename") << QStringLiteral("changeicon") << QStringLiteral("changecomment");
0237             if (!sa.group)
0238                 toEnable << QStringLiteral("changeurl");
0239         }
0240 
0241         if (sa.singleSelect) {
0242             toEnable << QStringLiteral("newfolder") << QStringLiteral("newbookmark") << QStringLiteral("insertseparator");
0243             if (sa.group)
0244                 toEnable << QStringLiteral("sort") << QStringLiteral("recursivesort") << QStringLiteral("setastoolbar");
0245         }
0246     }
0247 
0248     for (QStringList::const_iterator it = toEnable.constBegin(); it != toEnable.constEnd(); ++it) {
0249         ////qCDebug(KEDITBOOKMARKS_LOG) <<" enabling action "<<(*it);
0250         coll->action(*it)->setEnabled(true);
0251     }
0252 }
0253 
0254 KBookmark KEBApp::firstSelected() const
0255 {
0256     const QModelIndexList &list = mBookmarkListView->selectionModel()->selectedIndexes();
0257     if (list.count()) // selection in main listview, return bookmark for firstSelected
0258         return mBookmarkListView->bookmarkForIndex(*list.constBegin());
0259 
0260     // no selection in main listview, fall back to selection in left tree
0261     const QModelIndexList &list2 = mBookmarkFolderView->selectionModel()->selectedIndexes();
0262     return mBookmarkFolderView->bookmarkForIndex(*list2.constBegin());
0263 }
0264 
0265 QString KEBApp::insertAddress() const
0266 {
0267     KBookmark current = firstSelected();
0268     return (current.isGroup()) ? (current.address() + QStringLiteral("/0")) // FIXME internal representation used
0269                                : KBookmark::nextAddress(current.address());
0270 }
0271 
0272 static bool lessAddress(const QString &first, const QString &second)
0273 {
0274     QString a = first;
0275     QString b = second;
0276 
0277     if (a == b)
0278         return false;
0279 
0280     QString error(QStringLiteral("ERROR"));
0281     if (a == error)
0282         return false;
0283     if (b == error)
0284         return true;
0285 
0286     a += QLatin1Char('/');
0287     b += QLatin1Char('/');
0288 
0289     uint aLast = 0;
0290     uint bLast = 0;
0291     uint aEnd = a.length();
0292     uint bEnd = b.length();
0293     // Each iteration checks one "/"-delimeted part of the address
0294     // "" is treated correctly
0295     while (true) {
0296         // Invariant: a[0 ... aLast] == b[0 ... bLast]
0297         if (aLast + 1 == aEnd) // The last position was the last slash
0298             return true; // That means a is shorter than b
0299         if (bLast + 1 == bEnd)
0300             return false;
0301 
0302         uint aNext = a.indexOf(QLatin1String("/"), aLast + 1);
0303         uint bNext = b.indexOf(QLatin1String("/"), bLast + 1);
0304 
0305         bool okay;
0306 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0307         uint aNum = a.midRef(aLast + 1, aNext - aLast - 1).toUInt(&okay);
0308 #else
0309         uint aNum = QStringView(a).mid(aLast + 1, aNext - aLast - 1).toUInt(&okay);
0310 #endif
0311         if (!okay)
0312             return false;
0313 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0314         uint bNum = b.midRef(bLast + 1, bNext - bLast - 1).toUInt(&okay);
0315 #else
0316         uint bNum = QStringView(b).mid(bLast + 1, bNext - bLast - 1).toUInt(&okay);
0317 #endif
0318         if (!okay)
0319             return true;
0320 
0321         if (aNum != bNum)
0322             return aNum < bNum;
0323 
0324         aLast = aNext;
0325         bLast = bNext;
0326     }
0327 }
0328 
0329 static bool lessBookmark(const KBookmark &first, const KBookmark &second) // FIXME Using internal represantation
0330 {
0331     return lessAddress(first.address(), second.address());
0332 }
0333 
0334 KBookmark::List KEBApp::allBookmarks() const
0335 {
0336     KBookmark::List bookmarks;
0337     selectedBookmarksExpandedHelper(GlobalBookmarkManager::self()->root(), bookmarks);
0338     return bookmarks;
0339 }
0340 
0341 KBookmark::List KEBApp::selectedBookmarks() const
0342 {
0343     KBookmark::List bookmarks;
0344     const QModelIndexList &list = mBookmarkListView->selectionModel()->selectedIndexes();
0345     if (!list.isEmpty()) {
0346         QModelIndexList::const_iterator it, end;
0347         end = list.constEnd();
0348         for (it = list.constBegin(); it != end; ++it) {
0349             if ((*it).column() != 0)
0350                 continue;
0351             KBookmark bk = mBookmarkListView->bookmarkModel()->bookmarkForIndex(*it);
0352             ;
0353             if (bk.address() != GlobalBookmarkManager::self()->root().address())
0354                 bookmarks.push_back(bk);
0355         }
0356         std::sort(bookmarks.begin(), bookmarks.end(), lessBookmark);
0357     } else {
0358         bookmarks.push_back(firstSelected());
0359     }
0360 
0361     return bookmarks;
0362 }
0363 
0364 KBookmark::List KEBApp::selectedBookmarksExpanded() const
0365 {
0366     KBookmark::List bookmarks = selectedBookmarks();
0367     KBookmark::List result;
0368     KBookmark::List::const_iterator it, end;
0369     end = bookmarks.constEnd();
0370     for (it = bookmarks.constBegin(); it != end; ++it) {
0371         selectedBookmarksExpandedHelper(*it, result);
0372     }
0373     return result;
0374 }
0375 
0376 void KEBApp::selectedBookmarksExpandedHelper(const KBookmark &bk, KBookmark::List &bookmarks) const
0377 {
0378     // FIXME in which order parents should ideally be: parent then child
0379     // or child and then parents
0380     if (bk.isGroup()) {
0381         KBookmarkGroup parent = bk.toGroup();
0382         KBookmark child = parent.first();
0383         while (!child.isNull()) {
0384             selectedBookmarksExpandedHelper(child, bookmarks);
0385             child = parent.next(child);
0386         }
0387     } else {
0388         bookmarks.push_back(bk);
0389     }
0390 }
0391 
0392 KEBApp::~KEBApp()
0393 {
0394     // Save again, just in case the user expanded/collapsed folders (#131127)
0395     GlobalBookmarkManager::self()->notifyManagers();
0396 
0397     s_topLevel = nullptr;
0398     delete m_cmdHistory;
0399     delete m_actionsImpl;
0400     delete mBookmarkListView;
0401     delete GlobalBookmarkManager::self();
0402 }
0403 
0404 void KEBApp::resetActions()
0405 {
0406     stateChanged(QStringLiteral("disablestuff"));
0407     stateChanged(QStringLiteral("normal"));
0408 
0409     if (!m_readOnly)
0410         stateChanged(QStringLiteral("notreadonly"));
0411 }
0412 
0413 void KEBApp::selectionChanged()
0414 {
0415     updateActions();
0416 }
0417 
0418 void KEBApp::updateActions()
0419 {
0420     resetActions();
0421     setActionsEnabled(getSelectionAbilities());
0422 }
0423 
0424 void KEBApp::slotClipboardDataChanged()
0425 {
0426     // //qCDebug(KEDITBOOKMARKS_LOG) << "KEBApp::slotClipboardDataChanged";
0427     if (!m_readOnly) {
0428         m_canPaste = KBookmark::List::canDecode(QApplication::clipboard()->mimeData());
0429         updateActions();
0430     }
0431 }
0432 
0433 /* -------------------------- */
0434 
0435 void KEBApp::notifyCommandExecuted()
0436 {
0437     // //qCDebug(KEDITBOOKMARKS_LOG) << "KEBApp::notifyCommandExecuted()";
0438     updateActions();
0439 }
0440 
0441 /* -------------------------- */
0442 
0443 void KEBApp::slotConfigureToolbars()
0444 {
0445     // PORT TO QT5 saveMainWindowSettings(KConfigGroup( KSharedConfig::openConfig(), "MainWindow") );
0446     KEditToolBar dlg(actionCollection(), this);
0447     connect(&dlg, &KEditToolBar::newToolBarConfig, this, &KEBApp::slotNewToolbarConfig);
0448     dlg.exec();
0449 }
0450 
0451 void KEBApp::slotNewToolbarConfig()
0452 {
0453     // called when OK or Apply is clicked
0454     createGUI();
0455     applyMainWindowSettings(KConfigGroup(KSharedConfig::openConfig(), "MainWindow"));
0456 }
0457 
0458 /* -------------------------- */