File indexing completed on 2022-12-06 21:10:43

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