File indexing completed on 2024-04-14 14:18:01

0001 // -*- c-basic-offset:4; indent-tabs-mode:nil -*-
0002 /*
0003     This file is part of the KDE libraries
0004     SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
0005     SPDX-FileCopyrightText: 2003 Alexander Kellett <lypanov@kde.org>
0006     SPDX-FileCopyrightText: 2008 Norbert Frese <nf2@scheinwelt.at>
0007 
0008     SPDX-License-Identifier: LGPL-2.0-only
0009 */
0010 
0011 #include "kbookmarkmanager.h"
0012 #include "kbookmarkdialog.h"
0013 #include "kbookmarkimporter.h"
0014 #include "kbookmarkmenu.h"
0015 #include "kbookmarkmenu_p.h"
0016 #include "kbookmarks_debug.h"
0017 #ifndef KBOOKMARKS_NO_DBUS
0018 #include "kbookmarkmanageradaptor_p.h"
0019 #endif
0020 
0021 #include <QDir>
0022 #include <QFile>
0023 #include <QFileInfo>
0024 #include <QProcess>
0025 #include <QRegularExpression>
0026 #include <QTextCodec>
0027 #include <QTextStream>
0028 #ifndef KBOOKMARKS_NO_DBUS
0029 #include <QDBusConnection>
0030 #include <QDBusMessage>
0031 #endif
0032 #include <QApplication>
0033 #include <QMessageBox>
0034 #include <QReadWriteLock>
0035 #include <QThread>
0036 
0037 #include <KBackup>
0038 #include <KConfig>
0039 #include <KConfigGroup>
0040 #include <KDirWatch>
0041 #include <QSaveFile>
0042 #include <QStandardPaths>
0043 
0044 namespace
0045 {
0046 namespace Strings
0047 {
0048 QString bookmarkChangeNotifyInterface()
0049 {
0050     return QStringLiteral("org.kde.KIO.KBookmarkManager");
0051 }
0052 QString piData()
0053 {
0054     return QStringLiteral("version=\"1.0\" encoding=\"UTF-8\"");
0055 }
0056 }
0057 }
0058 
0059 class KBookmarkManagerList : public QList<KBookmarkManager *>
0060 {
0061 public:
0062     KBookmarkManagerList();
0063     ~KBookmarkManagerList()
0064     {
0065         cleanup();
0066     }
0067     void cleanup()
0068     {
0069         QList<KBookmarkManager *> copy = *this;
0070         qDeleteAll(copy); // auto-delete functionality
0071         clear();
0072     }
0073 
0074     QReadWriteLock lock;
0075 };
0076 
0077 Q_GLOBAL_STATIC(KBookmarkManagerList, s_pSelf)
0078 
0079 static void deleteManagers()
0080 {
0081     if (s_pSelf.exists()) {
0082         s_pSelf->cleanup();
0083     }
0084 }
0085 
0086 KBookmarkManagerList::KBookmarkManagerList()
0087 {
0088     // Delete the KBookmarkManagers while qApp exists, since we interact with the DBus thread
0089     qAddPostRoutine(deleteManagers);
0090 }
0091 
0092 class KBookmarkMap : private KBookmarkGroupTraverser
0093 {
0094 public:
0095     KBookmarkMap()
0096         : m_mapNeedsUpdate(true)
0097     {
0098     }
0099     void setNeedsUpdate()
0100     {
0101         m_mapNeedsUpdate = true;
0102     }
0103     void update(KBookmarkManager *);
0104     QList<KBookmark> find(const QString &url) const
0105     {
0106         return m_bk_map.value(url);
0107     }
0108 
0109 private:
0110     void visit(const KBookmark &) override;
0111     void visitEnter(const KBookmarkGroup &) override
0112     {
0113         ;
0114     }
0115     void visitLeave(const KBookmarkGroup &) override
0116     {
0117         ;
0118     }
0119 
0120 private:
0121     typedef QList<KBookmark> KBookmarkList;
0122     QMap<QString, KBookmarkList> m_bk_map;
0123     bool m_mapNeedsUpdate;
0124 };
0125 
0126 void KBookmarkMap::update(KBookmarkManager *manager)
0127 {
0128     if (m_mapNeedsUpdate) {
0129         m_mapNeedsUpdate = false;
0130 
0131         m_bk_map.clear();
0132         KBookmarkGroup root = manager->root();
0133         traverse(root);
0134     }
0135 }
0136 
0137 void KBookmarkMap::visit(const KBookmark &bk)
0138 {
0139     if (!bk.isSeparator()) {
0140         // add bookmark to url map
0141         m_bk_map[bk.internalElement().attribute(QStringLiteral("href"))].append(bk);
0142     }
0143 }
0144 
0145 // #########################
0146 // KBookmarkManagerPrivate
0147 class KBookmarkManagerPrivate
0148 {
0149 public:
0150     KBookmarkManagerPrivate(bool bDocIsloaded, const QString &dbusObjectName = QString())
0151         : m_doc(QStringLiteral("xbel"))
0152         , m_dbusObjectName(dbusObjectName)
0153         , m_docIsLoaded(bDocIsloaded)
0154         , m_update(false)
0155         , m_dialogAllowed(true)
0156         , m_dialogParent(nullptr)
0157         , m_browserEditor(false)
0158         , m_typeExternal(false)
0159         , m_dirWatch(nullptr)
0160     {
0161     }
0162 
0163     ~KBookmarkManagerPrivate()
0164     {
0165         delete m_dirWatch;
0166     }
0167 
0168     mutable QDomDocument m_doc;
0169     mutable QDomDocument m_toolbarDoc;
0170     QString m_bookmarksFile;
0171     QString m_dbusObjectName;
0172     mutable bool m_docIsLoaded;
0173     bool m_update;
0174     bool m_dialogAllowed;
0175     QWidget *m_dialogParent;
0176 
0177     bool m_browserEditor;
0178     QString m_editorCaption;
0179 
0180     bool m_typeExternal;
0181     KDirWatch *m_dirWatch; // for external bookmark files
0182 
0183     KBookmarkMap m_map;
0184 };
0185 
0186 // ################
0187 // KBookmarkManager
0188 
0189 static KBookmarkManager *lookupExisting(const QString &bookmarksFile)
0190 {
0191     for (KBookmarkManagerList::ConstIterator bmit = s_pSelf()->constBegin(), bmend = s_pSelf()->constEnd(); bmit != bmend; ++bmit) {
0192         if ((*bmit)->path() == bookmarksFile) {
0193             return *bmit;
0194         }
0195     }
0196     return nullptr;
0197 }
0198 
0199 KBookmarkManager *KBookmarkManager::managerForFile(const QString &bookmarksFile, const QString &dbusObjectName)
0200 {
0201     KBookmarkManager *mgr(nullptr);
0202     {
0203         QReadLocker readLock(&s_pSelf()->lock);
0204         mgr = lookupExisting(bookmarksFile);
0205         if (mgr) {
0206             return mgr;
0207         }
0208     }
0209 
0210     QWriteLocker writeLock(&s_pSelf()->lock);
0211     mgr = lookupExisting(bookmarksFile);
0212     if (mgr) {
0213         return mgr;
0214     }
0215 
0216     mgr = new KBookmarkManager(bookmarksFile, dbusObjectName);
0217     s_pSelf()->append(mgr);
0218     return mgr;
0219 }
0220 
0221 KBookmarkManager *KBookmarkManager::managerForExternalFile(const QString &bookmarksFile)
0222 {
0223     KBookmarkManager *mgr(nullptr);
0224     {
0225         QReadLocker readLock(&s_pSelf()->lock);
0226         mgr = lookupExisting(bookmarksFile);
0227         if (mgr) {
0228             return mgr;
0229         }
0230     }
0231 
0232     QWriteLocker writeLock(&s_pSelf()->lock);
0233     mgr = lookupExisting(bookmarksFile);
0234     if (mgr) {
0235         return mgr;
0236     }
0237 
0238     mgr = new KBookmarkManager(bookmarksFile);
0239     s_pSelf()->append(mgr);
0240     return mgr;
0241 }
0242 
0243 // principally used for filtered toolbars
0244 KBookmarkManager *KBookmarkManager::createTempManager()
0245 {
0246     KBookmarkManager *mgr = new KBookmarkManager();
0247     s_pSelf()->append(mgr);
0248     return mgr;
0249 }
0250 
0251 static QDomElement createXbelTopLevelElement(QDomDocument &doc)
0252 {
0253     QDomElement topLevel = doc.createElement(QStringLiteral("xbel"));
0254     topLevel.setAttribute(QStringLiteral("xmlns:mime"), QStringLiteral("http://www.freedesktop.org/standards/shared-mime-info"));
0255     topLevel.setAttribute(QStringLiteral("xmlns:bookmark"), QStringLiteral("http://www.freedesktop.org/standards/desktop-bookmarks"));
0256     topLevel.setAttribute(QStringLiteral("xmlns:kdepriv"), QStringLiteral("http://www.kde.org/kdepriv"));
0257     doc.appendChild(topLevel);
0258     doc.insertBefore(doc.createProcessingInstruction(QStringLiteral("xml"), Strings::piData()), topLevel);
0259     return topLevel;
0260 }
0261 
0262 KBookmarkManager::KBookmarkManager(const QString &bookmarksFile, const QString &dbusObjectName)
0263     : d(new KBookmarkManagerPrivate(false, dbusObjectName))
0264 {
0265     if (dbusObjectName.isNull()) { // get dbusObjectName from file
0266         if (QFile::exists(d->m_bookmarksFile)) {
0267             parse(); // sets d->m_dbusObjectName
0268         }
0269     }
0270 
0271     init(QLatin1String("/KBookmarkManager/") + d->m_dbusObjectName);
0272 
0273     d->m_update = true;
0274 
0275     Q_ASSERT(!bookmarksFile.isEmpty());
0276     d->m_bookmarksFile = bookmarksFile;
0277 
0278     if (!QFile::exists(d->m_bookmarksFile)) {
0279         QDomElement topLevel = createXbelTopLevelElement(d->m_doc);
0280         topLevel.setAttribute(QStringLiteral("dbusName"), dbusObjectName);
0281         d->m_docIsLoaded = true;
0282     }
0283 }
0284 
0285 KBookmarkManager::KBookmarkManager(const QString &bookmarksFile)
0286     : d(new KBookmarkManagerPrivate(false))
0287 {
0288     // use QFileSystemWatcher to monitor this bookmarks file
0289     d->m_typeExternal = true;
0290     d->m_update = true;
0291 
0292     Q_ASSERT(!bookmarksFile.isEmpty());
0293     d->m_bookmarksFile = bookmarksFile;
0294 
0295     if (!QFile::exists(d->m_bookmarksFile)) {
0296         createXbelTopLevelElement(d->m_doc);
0297     } else {
0298         parse();
0299     }
0300     d->m_docIsLoaded = true;
0301 
0302     // start KDirWatch
0303     d->m_dirWatch = new KDirWatch;
0304     d->m_dirWatch->addFile(d->m_bookmarksFile);
0305     QObject::connect(d->m_dirWatch, &KDirWatch::dirty, this, &KBookmarkManager::slotFileChanged);
0306     QObject::connect(d->m_dirWatch, &KDirWatch::created, this, &KBookmarkManager::slotFileChanged);
0307     QObject::connect(d->m_dirWatch, &KDirWatch::deleted, this, &KBookmarkManager::slotFileChanged);
0308 
0309     // qCDebug(KBOOKMARKS_LOG) << "starting KDirWatch for" << d->m_bookmarksFile;
0310 }
0311 
0312 KBookmarkManager::KBookmarkManager()
0313     : d(new KBookmarkManagerPrivate(true))
0314 {
0315     init(QStringLiteral("/KBookmarkManager/generated"));
0316     d->m_update = false; // TODO - make it read/write
0317 
0318     createXbelTopLevelElement(d->m_doc);
0319 }
0320 
0321 void KBookmarkManager::init(const QString &dbusPath)
0322 {
0323 #ifndef KBOOKMARKS_NO_DBUS
0324     // A KBookmarkManager without a dbus name is a temporary one, like those used by importers;
0325     // no need to register them to dbus
0326     if (dbusPath != QLatin1String("/KBookmarkManager/") && dbusPath != QLatin1String("/KBookmarkManager/generated")) {
0327         new KBookmarkManagerAdaptor(this);
0328         QDBusConnection::sessionBus().registerObject(dbusPath, this);
0329 
0330         QDBusConnection::sessionBus().connect(QString(),
0331                                               dbusPath,
0332                                               Strings::bookmarkChangeNotifyInterface(),
0333                                               QStringLiteral("bookmarksChanged"),
0334                                               this,
0335                                               SLOT(notifyChanged(QString, QDBusMessage)));
0336         QDBusConnection::sessionBus()
0337             .connect(QString(), dbusPath, Strings::bookmarkChangeNotifyInterface(), QStringLiteral("bookmarkConfigChanged"), this, SLOT(notifyConfigChanged()));
0338     }
0339 #endif
0340 }
0341 
0342 void KBookmarkManager::startKEditBookmarks(const QStringList &args)
0343 {
0344     bool success = false;
0345     const QString exec = QStandardPaths::findExecutable(QStringLiteral(KEDITBOOKMARKS_BINARY));
0346     if (!exec.isEmpty()) {
0347         success = QProcess::startDetached(exec, args);
0348     }
0349 
0350     if (!success) {
0351         QString err =
0352             tr("Cannot launch keditbookmarks.\n\n"
0353                "Most likely you do not have keditbookmarks currently installed");
0354 
0355         if (d->m_dialogAllowed && qobject_cast<QApplication *>(qApp) && QThread::currentThread() == qApp->thread()) {
0356             QMessageBox::warning(QApplication::activeWindow(), QApplication::applicationName(), err);
0357         }
0358 
0359         qCWarning(KBOOKMARKS_LOG) << QStringLiteral("Failed to start keditbookmarks");
0360         Q_EMIT this->error(err);
0361     }
0362 }
0363 
0364 void KBookmarkManager::slotFileChanged(const QString &path)
0365 {
0366     if (path == d->m_bookmarksFile) {
0367         // qCDebug(KBOOKMARKS_LOG) << "file changed (KDirWatch) " << path ;
0368         // Reparse
0369         parse();
0370         // Tell our GUI
0371         // (emit where group is "" to directly mark the root menu as dirty)
0372         Q_EMIT changed(QLatin1String(""), QString());
0373     }
0374 }
0375 
0376 KBookmarkManager::~KBookmarkManager()
0377 {
0378     if (!s_pSelf.isDestroyed()) {
0379         s_pSelf()->removeAll(this);
0380     }
0381 }
0382 
0383 bool KBookmarkManager::autoErrorHandlingEnabled() const
0384 {
0385     return d->m_dialogAllowed;
0386 }
0387 
0388 void KBookmarkManager::setAutoErrorHandlingEnabled(bool enable, QWidget *parent)
0389 {
0390     d->m_dialogAllowed = enable;
0391     d->m_dialogParent = parent;
0392 }
0393 
0394 void KBookmarkManager::setUpdate(bool update)
0395 {
0396     d->m_update = update;
0397 }
0398 
0399 QDomDocument KBookmarkManager::internalDocument() const
0400 {
0401     if (!d->m_docIsLoaded) {
0402         parse();
0403         d->m_toolbarDoc.clear();
0404     }
0405     return d->m_doc;
0406 }
0407 
0408 void KBookmarkManager::parse() const
0409 {
0410     d->m_docIsLoaded = true;
0411     // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::parse " << d->m_bookmarksFile;
0412     QFile file(d->m_bookmarksFile);
0413     if (!file.open(QIODevice::ReadOnly)) {
0414         qCWarning(KBOOKMARKS_LOG) << "Can't open" << d->m_bookmarksFile;
0415         d->m_doc = QDomDocument(QStringLiteral("xbel"));
0416         createXbelTopLevelElement(d->m_doc);
0417         return;
0418     }
0419     d->m_doc = QDomDocument(QStringLiteral("xbel"));
0420     d->m_doc.setContent(&file);
0421 
0422     if (d->m_doc.documentElement().isNull()) {
0423         qCWarning(KBOOKMARKS_LOG) << "KBookmarkManager::parse : main tag is missing, creating default " << d->m_bookmarksFile;
0424         QDomElement element = d->m_doc.createElement(QStringLiteral("xbel"));
0425         d->m_doc.appendChild(element);
0426     }
0427 
0428     QDomElement docElem = d->m_doc.documentElement();
0429 
0430     QString mainTag = docElem.tagName();
0431     if (mainTag != QLatin1String("xbel")) {
0432         qCWarning(KBOOKMARKS_LOG) << "KBookmarkManager::parse : unknown main tag " << mainTag;
0433     }
0434 
0435     if (d->m_dbusObjectName.isNull()) {
0436         d->m_dbusObjectName = docElem.attribute(QStringLiteral("dbusName"));
0437     } else if (docElem.attribute(QStringLiteral("dbusName")) != d->m_dbusObjectName) {
0438         docElem.setAttribute(QStringLiteral("dbusName"), d->m_dbusObjectName);
0439         save();
0440     }
0441 
0442     QDomNode n = d->m_doc.documentElement().previousSibling();
0443     if (n.isProcessingInstruction()) {
0444         QDomProcessingInstruction pi = n.toProcessingInstruction();
0445         pi.parentNode().removeChild(pi);
0446     }
0447 
0448     QDomProcessingInstruction pi;
0449     pi = d->m_doc.createProcessingInstruction(QStringLiteral("xml"), Strings::piData());
0450     d->m_doc.insertBefore(pi, docElem);
0451 
0452     file.close();
0453 
0454     d->m_map.setNeedsUpdate();
0455 }
0456 
0457 bool KBookmarkManager::save(bool toolbarCache) const
0458 {
0459     return saveAs(d->m_bookmarksFile, toolbarCache);
0460 }
0461 
0462 bool KBookmarkManager::saveAs(const QString &filename, bool toolbarCache) const
0463 {
0464     // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::save " << filename;
0465 
0466     // Save the bookmark toolbar folder for quick loading
0467     // but only when it will actually make things quicker
0468     const QString cacheFilename = filename + QLatin1String(".tbcache");
0469     if (toolbarCache && !root().isToolbarGroup()) {
0470         QSaveFile cacheFile(cacheFilename);
0471         if (cacheFile.open(QIODevice::WriteOnly)) {
0472             QString str;
0473             QTextStream stream(&str, QIODevice::WriteOnly);
0474             stream << root().findToolbar();
0475             const QByteArray cstr = str.toUtf8();
0476             cacheFile.write(cstr.data(), cstr.length());
0477             cacheFile.commit();
0478         }
0479     } else { // remove any (now) stale cache
0480         QFile::remove(cacheFilename);
0481     }
0482 
0483     // Create parent dirs
0484     QFileInfo info(filename);
0485     QDir().mkpath(info.absolutePath());
0486 
0487     QSaveFile file(filename);
0488     if (file.open(QIODevice::WriteOnly)) {
0489         KBackup::simpleBackupFile(file.fileName(), QString(), QStringLiteral(".bak"));
0490         QTextStream stream(&file);
0491         // In Qt6 it's UTF-8 by default
0492 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0493         stream.setCodec(QTextCodec::codecForName("UTF-8"));
0494 #endif
0495         stream << internalDocument().toString();
0496         stream.flush();
0497         if (file.commit()) {
0498             return true;
0499         }
0500     }
0501 
0502     static int hadSaveError = false;
0503     if (!hadSaveError) {
0504         QString err = tr("Unable to save bookmarks in %1. Reported error was: %2. "
0505                          "This error message will only be shown once. The cause "
0506                          "of the error needs to be fixed as quickly as possible, "
0507                          "which is most likely a full hard drive.")
0508                           .arg(filename, file.errorString());
0509 
0510         if (d->m_dialogAllowed && qobject_cast<QApplication *>(qApp) && QThread::currentThread() == qApp->thread()) {
0511             QMessageBox::critical(QApplication::activeWindow(), QApplication::applicationName(), err);
0512         }
0513 
0514         qCCritical(KBOOKMARKS_LOG)
0515             << QStringLiteral("Unable to save bookmarks in %1. File reported the following error-code: %2.").arg(filename).arg(file.error());
0516         Q_EMIT const_cast<KBookmarkManager *>(this)->error(err);
0517     }
0518     hadSaveError = true;
0519     return false;
0520 }
0521 
0522 QString KBookmarkManager::path() const
0523 {
0524     return d->m_bookmarksFile;
0525 }
0526 
0527 KBookmarkGroup KBookmarkManager::root() const
0528 {
0529     return KBookmarkGroup(internalDocument().documentElement());
0530 }
0531 
0532 KBookmarkGroup KBookmarkManager::toolbar()
0533 {
0534     // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::toolbar begin";
0535     // Only try to read from a toolbar cache if the full document isn't loaded
0536     if (!d->m_docIsLoaded) {
0537         // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::toolbar trying cache";
0538         const QString cacheFilename = d->m_bookmarksFile + QLatin1String(".tbcache");
0539         QFileInfo bmInfo(d->m_bookmarksFile);
0540         QFileInfo cacheInfo(cacheFilename);
0541         if (d->m_toolbarDoc.isNull() && QFile::exists(cacheFilename) && bmInfo.lastModified() < cacheInfo.lastModified()) {
0542             // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::toolbar reading file";
0543             QFile file(cacheFilename);
0544 
0545             if (file.open(QIODevice::ReadOnly)) {
0546                 d->m_toolbarDoc = QDomDocument(QStringLiteral("cache"));
0547                 d->m_toolbarDoc.setContent(&file);
0548                 // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::toolbar opened";
0549             }
0550         }
0551         if (!d->m_toolbarDoc.isNull()) {
0552             // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::toolbar returning element";
0553             QDomElement elem = d->m_toolbarDoc.firstChild().toElement();
0554             return KBookmarkGroup(elem);
0555         }
0556     }
0557 
0558     // Fallback to the normal way if there is no cache or if the bookmark file
0559     // is already loaded
0560     QDomElement elem = root().findToolbar();
0561     if (elem.isNull()) {
0562         // Root is the bookmark toolbar if none has been set.
0563         // Make it explicit to speed up invocations of findToolbar()
0564         root().internalElement().setAttribute(QStringLiteral("toolbar"), QStringLiteral("yes"));
0565         return root();
0566     } else {
0567         return KBookmarkGroup(elem);
0568     }
0569 }
0570 
0571 KBookmark KBookmarkManager::findByAddress(const QString &address)
0572 {
0573     // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::findByAddress " << address;
0574     KBookmark result = root();
0575     // The address is something like /5/10/2+
0576     static const QRegularExpression separator(QStringLiteral("[/+]"));
0577     const QStringList addresses = address.split(separator, Qt::SkipEmptyParts);
0578     // qCWarning(KBOOKMARKS_LOG) << addresses.join(",");
0579     for (QStringList::const_iterator it = addresses.begin(); it != addresses.end();) {
0580         bool append = ((*it) == QLatin1String("+"));
0581         uint number = (*it).toUInt();
0582         Q_ASSERT(result.isGroup());
0583         KBookmarkGroup group = result.toGroup();
0584         KBookmark bk = group.first();
0585         KBookmark lbk = bk; // last non-null bookmark
0586         for (uint i = 0; ((i < number) || append) && !bk.isNull(); ++i) {
0587             lbk = bk;
0588             bk = group.next(bk);
0589             // qCWarning(KBOOKMARKS_LOG) << i;
0590         }
0591         it++;
0592         // qCWarning(KBOOKMARKS_LOG) << "found section";
0593         result = bk;
0594     }
0595     if (result.isNull()) {
0596         qCWarning(KBOOKMARKS_LOG) << "KBookmarkManager::findByAddress: couldn't find item " << address;
0597     }
0598     // qCWarning(KBOOKMARKS_LOG) << "found " << result.address();
0599     return result;
0600 }
0601 
0602 void KBookmarkManager::emitChanged()
0603 {
0604     emitChanged(root());
0605 }
0606 
0607 void KBookmarkManager::emitChanged(const KBookmarkGroup &group)
0608 {
0609     (void)save(); // KDE5 TODO: emitChanged should return a bool? Maybe rename it to saveAndEmitChanged?
0610 
0611     // Tell the other processes too
0612     // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::emitChanged : broadcasting change " << group.address();
0613 
0614     Q_EMIT bookmarksChanged(group.address());
0615 
0616     // We do get our own broadcast, so no need for this anymore
0617     // emit changed( group );
0618 }
0619 
0620 void KBookmarkManager::emitConfigChanged()
0621 {
0622     Q_EMIT bookmarkConfigChanged();
0623 }
0624 
0625 void KBookmarkManager::notifyCompleteChange(const QString &caller) // DBUS call
0626 {
0627     if (!d->m_update) {
0628         return;
0629     }
0630 
0631     // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::notifyCompleteChange";
0632     // The bk editor tells us we should reload everything
0633     // Reparse
0634     parse();
0635     // Tell our GUI
0636     // (emit where group is "" to directly mark the root menu as dirty)
0637     Q_EMIT changed(QLatin1String(""), caller);
0638 }
0639 
0640 void KBookmarkManager::notifyConfigChanged() // DBUS call
0641 {
0642     // qCDebug(KBOOKMARKS_LOG) << "reloaded bookmark config!";
0643     KBookmarkSettings::self()->readSettings();
0644     parse(); // reload, and thusly recreate the menus
0645     Q_EMIT configChanged();
0646 }
0647 
0648 #ifndef KBOOKMARKS_NO_DBUS
0649 void KBookmarkManager::notifyChanged(const QString &groupAddress, const QDBusMessage &msg) // DBUS call
0650 {
0651     // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::notifyChanged ( "<<groupAddress<<")";
0652     if (!d->m_update) {
0653         return;
0654     }
0655 
0656     // Reparse (the whole file, no other choice)
0657     // if someone else notified us
0658     if (msg.service() != QDBusConnection::sessionBus().baseService()) {
0659         parse();
0660     }
0661 
0662     // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::notifyChanged " << groupAddress;
0663     // KBookmarkGroup group = findByAddress( groupAddress ).toGroup();
0664     // Q_ASSERT(!group.isNull());
0665     Q_EMIT changed(groupAddress, QString());
0666 }
0667 #endif
0668 
0669 void KBookmarkManager::setEditorOptions(const QString &caption, bool browser)
0670 {
0671     d->m_editorCaption = caption;
0672     d->m_browserEditor = browser;
0673 }
0674 
0675 void KBookmarkManager::slotEditBookmarks()
0676 {
0677     QStringList args;
0678     if (!d->m_editorCaption.isEmpty()) {
0679         args << QStringLiteral("--customcaption") << d->m_editorCaption;
0680     }
0681     if (!d->m_browserEditor) {
0682         args << QStringLiteral("--nobrowser");
0683     }
0684     if (!d->m_dbusObjectName.isEmpty()) {
0685         args << QStringLiteral("--dbusObjectName") << d->m_dbusObjectName;
0686     }
0687     args << d->m_bookmarksFile;
0688     startKEditBookmarks(args);
0689 }
0690 
0691 void KBookmarkManager::slotEditBookmarksAtAddress(const QString &address)
0692 {
0693     QStringList args;
0694     if (!d->m_editorCaption.isEmpty()) {
0695         args << QStringLiteral("--customcaption") << d->m_editorCaption;
0696     }
0697     if (!d->m_browserEditor) {
0698         args << QStringLiteral("--nobrowser");
0699     }
0700     if (!d->m_dbusObjectName.isEmpty()) {
0701         args << QStringLiteral("--dbusObjectName") << d->m_dbusObjectName;
0702     }
0703     args << QStringLiteral("--address") << address << d->m_bookmarksFile;
0704     startKEditBookmarks(args);
0705 }
0706 
0707 ///////
0708 bool KBookmarkManager::updateAccessMetadata(const QString &url)
0709 {
0710     d->m_map.update(this);
0711     QList<KBookmark> list = d->m_map.find(url);
0712     if (list.isEmpty()) {
0713         return false;
0714     }
0715 
0716     for (QList<KBookmark>::iterator it = list.begin(); it != list.end(); ++it) {
0717         (*it).updateAccessMetadata();
0718     }
0719 
0720     return true;
0721 }
0722 
0723 void KBookmarkManager::updateFavicon(const QString &url, const QString & /*faviconurl*/)
0724 {
0725     d->m_map.update(this);
0726     QList<KBookmark> list = d->m_map.find(url);
0727     for (QList<KBookmark>::iterator it = list.begin(); it != list.end(); ++it) {
0728         // TODO - update favicon data based on faviconurl
0729         //        but only when the previously used icon
0730         //        isn't a manually set one.
0731     }
0732 }
0733 
0734 KBookmarkManager *KBookmarkManager::userBookmarksManager()
0735 {
0736     const QString bookmarksFile =
0737         QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/konqueror/bookmarks.xml");
0738     KBookmarkManager *bookmarkManager = KBookmarkManager::managerForFile(bookmarksFile, QStringLiteral("konqueror"));
0739     QString caption = QGuiApplication::applicationDisplayName();
0740     if (caption.isEmpty()) {
0741         caption = QCoreApplication::applicationName();
0742     }
0743     bookmarkManager->setEditorOptions(caption, true);
0744     return bookmarkManager;
0745 }
0746 
0747 KBookmarkSettings *KBookmarkSettings::s_self = nullptr;
0748 
0749 void KBookmarkSettings::readSettings()
0750 {
0751     KConfig config(QStringLiteral("kbookmarkrc"), KConfig::NoGlobals);
0752     KConfigGroup cg(&config, "Bookmarks");
0753 
0754     // add bookmark dialog usage - no reparse
0755     s_self->m_advancedaddbookmark = cg.readEntry("AdvancedAddBookmarkDialog", false);
0756 
0757     // this one alters the menu, therefore it needs a reparse
0758     s_self->m_contextmenu = cg.readEntry("ContextMenuActions", true);
0759 }
0760 
0761 KBookmarkSettings *KBookmarkSettings::self()
0762 {
0763     if (!s_self) {
0764         s_self = new KBookmarkSettings;
0765         readSettings();
0766     }
0767     return s_self;
0768 }
0769 
0770 #include "moc_kbookmarkmanager.cpp"