File indexing completed on 2024-04-21 05:50:23

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 <KMessageBox>
0043 #include <KSharedConfig>
0044 
0045 #include <QDBusConnection>
0046 KEBApp *KEBApp::s_topLevel = nullptr;
0047 
0048 KEBApp::KEBApp(const QString &bookmarksFile, bool readonly, const QString &address, bool browser, const QString &caption, const QString &dbusObjectName)
0049     : KXmlGuiWindow()
0050     , m_bookmarksFilename(bookmarksFile)
0051     , m_caption(caption)
0052     , m_dbusObjectName(dbusObjectName)
0053     , m_readOnly(readonly)
0054     , m_browser(browser)
0055 {
0056     QDBusConnection::sessionBus().registerObject(QStringLiteral("/keditbookmarks"), this, QDBusConnection::ExportScriptableSlots);
0057     Q_UNUSED(address); // FIXME sets the current item
0058 
0059     m_cmdHistory = new CommandHistory(this);
0060     m_cmdHistory->createActions(actionCollection());
0061     connect(m_cmdHistory, &CommandHistory::notifyCommandExecuted, this, &KEBApp::notifyCommandExecuted);
0062 
0063     GlobalBookmarkManager::self()->createManager(m_bookmarksFilename, m_dbusObjectName, m_cmdHistory);
0064     connect(GlobalBookmarkManager::self(), &GlobalBookmarkManager::error, this, &KEBApp::slotManagerError);
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         uint aNum = QStringView(a).mid(aLast + 1, aNext - aLast - 1).toUInt(&okay);
0307         if (!okay)
0308             return false;
0309         uint bNum = QStringView(b).mid(bLast + 1, bNext - bLast - 1).toUInt(&okay);
0310         if (!okay)
0311             return true;
0312 
0313         if (aNum != bNum)
0314             return aNum < bNum;
0315 
0316         aLast = aNext;
0317         bLast = bNext;
0318     }
0319 }
0320 
0321 static bool lessBookmark(const KBookmark &first, const KBookmark &second) // FIXME Using internal represantation
0322 {
0323     return lessAddress(first.address(), second.address());
0324 }
0325 
0326 KBookmark::List KEBApp::allBookmarks() const
0327 {
0328     KBookmark::List bookmarks;
0329     selectedBookmarksExpandedHelper(GlobalBookmarkManager::self()->root(), bookmarks);
0330     return bookmarks;
0331 }
0332 
0333 KBookmark::List KEBApp::selectedBookmarks() const
0334 {
0335     KBookmark::List bookmarks;
0336     const QModelIndexList &list = mBookmarkListView->selectionModel()->selectedIndexes();
0337     if (!list.isEmpty()) {
0338         QModelIndexList::const_iterator it, end;
0339         end = list.constEnd();
0340         for (it = list.constBegin(); it != end; ++it) {
0341             if ((*it).column() != 0)
0342                 continue;
0343             KBookmark bk = mBookmarkListView->bookmarkModel()->bookmarkForIndex(*it);
0344             ;
0345             if (bk.address() != GlobalBookmarkManager::self()->root().address())
0346                 bookmarks.push_back(bk);
0347         }
0348         std::sort(bookmarks.begin(), bookmarks.end(), lessBookmark);
0349     } else {
0350         bookmarks.push_back(firstSelected());
0351     }
0352 
0353     return bookmarks;
0354 }
0355 
0356 KBookmark::List KEBApp::selectedBookmarksExpanded() const
0357 {
0358     KBookmark::List bookmarks = selectedBookmarks();
0359     KBookmark::List result;
0360     KBookmark::List::const_iterator it, end;
0361     end = bookmarks.constEnd();
0362     for (it = bookmarks.constBegin(); it != end; ++it) {
0363         selectedBookmarksExpandedHelper(*it, result);
0364     }
0365     return result;
0366 }
0367 
0368 void KEBApp::selectedBookmarksExpandedHelper(const KBookmark &bk, KBookmark::List &bookmarks) const
0369 {
0370     // FIXME in which order parents should ideally be: parent then child
0371     // or child and then parents
0372     if (bk.isGroup()) {
0373         KBookmarkGroup parent = bk.toGroup();
0374         KBookmark child = parent.first();
0375         while (!child.isNull()) {
0376             selectedBookmarksExpandedHelper(child, bookmarks);
0377             child = parent.next(child);
0378         }
0379     } else {
0380         bookmarks.push_back(bk);
0381     }
0382 }
0383 
0384 KEBApp::~KEBApp()
0385 {
0386     // Save again, just in case the user expanded/collapsed folders (#131127)
0387     GlobalBookmarkManager::self()->notifyManagers();
0388 
0389     s_topLevel = nullptr;
0390     delete m_cmdHistory;
0391     delete m_actionsImpl;
0392     delete mBookmarkListView;
0393     delete GlobalBookmarkManager::self();
0394 }
0395 
0396 void KEBApp::resetActions()
0397 {
0398     stateChanged(QStringLiteral("disablestuff"));
0399     stateChanged(QStringLiteral("normal"));
0400 
0401     if (!m_readOnly)
0402         stateChanged(QStringLiteral("notreadonly"));
0403 }
0404 
0405 void KEBApp::selectionChanged()
0406 {
0407     updateActions();
0408 }
0409 
0410 void KEBApp::updateActions()
0411 {
0412     resetActions();
0413     setActionsEnabled(getSelectionAbilities());
0414 }
0415 
0416 void KEBApp::slotClipboardDataChanged()
0417 {
0418     // //qCDebug(KEDITBOOKMARKS_LOG) << "KEBApp::slotClipboardDataChanged";
0419     if (!m_readOnly) {
0420         m_canPaste = KBookmark::List::canDecode(QApplication::clipboard()->mimeData());
0421         updateActions();
0422     }
0423 }
0424 
0425 /* -------------------------- */
0426 
0427 void KEBApp::notifyCommandExecuted()
0428 {
0429     // //qCDebug(KEDITBOOKMARKS_LOG) << "KEBApp::notifyCommandExecuted()";
0430     updateActions();
0431 }
0432 
0433 /* -------------------------- */
0434 
0435 void KEBApp::slotConfigureToolbars()
0436 {
0437     // PORT TO QT5 saveMainWindowSettings(KConfigGroup( KSharedConfig::openConfig(), "MainWindow") );
0438     KEditToolBar dlg(actionCollection(), this);
0439     connect(&dlg, &KEditToolBar::newToolBarConfig, this, &KEBApp::slotNewToolbarConfig);
0440     dlg.exec();
0441 }
0442 
0443 void KEBApp::slotNewToolbarConfig()
0444 {
0445     // called when OK or Apply is clicked
0446     createGUI();
0447     applyMainWindowSettings(KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("MainWindow")));
0448 }
0449 
0450 void KEBApp::slotManagerError(const QString &errorMessage)
0451 {
0452     KMessageBox::error(this, errorMessage);
0453 }
0454 
0455 /* -------------------------- */
0456 
0457 #include "moc_toplevel.cpp"