1 // -*- c-basic-offset:4; indent-tabs-mode:nil -*-
2 /*
3 This file is part of the KDE libraries
4 SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
5 SPDX-FileCopyrightText: 2003 Alexander Kellett <lypanov@kde.org>
6 SPDX-FileCopyrightText: 2008 Norbert Frese <nf2@scheinwelt.at>
7
8 SPDX-License-Identifier: LGPL-2.0-only
9 */
10
11 #include "kbookmarkmanager.h"
12 #include "kbookmarkdialog.h"
13 #include "kbookmarkimporter.h"
14 #include "kbookmarkmenu.h"
15 #include "kbookmarkmenu_p.h"
16 #include "kbookmarks_debug.h"
17 #ifndef KBOOKMARKS_NO_DBUS
18 #include "kbookmarkmanageradaptor_p.h"
19 #endif
20
21 #include <QDir>
22 #include <QFile>
23 #include <QFileInfo>
24 #include <QProcess>
25 #include <QRegularExpression>
26 #include <QTextCodec>
27 #include <QTextStream>
28 #ifndef KBOOKMARKS_NO_DBUS
29 #include <QDBusConnection>
30 #include <QDBusMessage>
31 #endif
32 #include <QApplication>
33 #include <QMessageBox>
34 #include <QReadWriteLock>
35 #include <QThread>
36
37 #include <KBackup>
38 #include <KConfig>
39 #include <KConfigGroup>
40 #include <KDirWatch>
41 #include <QSaveFile>
42 #include <QStandardPaths>
43
44 namespace
45 {
46 namespace Strings
47 {
bookmarkChangeNotifyInterface()48 QString bookmarkChangeNotifyInterface()
49 {
50 return QStringLiteral("org.kde.KIO.KBookmarkManager");
51 }
piData()52 QString piData()
53 {
54 return QStringLiteral("version=\"1.0\" encoding=\"UTF-8\"");
55 }
56 }
57 }
58
59 class KBookmarkManagerList : public QList<KBookmarkManager *>
60 {
61 public:
62 KBookmarkManagerList();
~KBookmarkManagerList()63 ~KBookmarkManagerList()
64 {
65 cleanup();
66 }
cleanup()67 void cleanup()
68 {
69 QList<KBookmarkManager *> copy = *this;
70 qDeleteAll(copy); // auto-delete functionality
71 clear();
72 }
73
74 QReadWriteLock lock;
75 };
76
Q_GLOBAL_STATIC(KBookmarkManagerList,s_pSelf)77 Q_GLOBAL_STATIC(KBookmarkManagerList, s_pSelf)
78
79 static void deleteManagers()
80 {
81 if (s_pSelf.exists()) {
82 s_pSelf->cleanup();
83 }
84 }
85
KBookmarkManagerList()86 KBookmarkManagerList::KBookmarkManagerList()
87 {
88 // Delete the KBookmarkManagers while qApp exists, since we interact with the DBus thread
89 qAddPostRoutine(deleteManagers);
90 }
91
92 class KBookmarkMap : private KBookmarkGroupTraverser
93 {
94 public:
KBookmarkMap()95 KBookmarkMap()
96 : m_mapNeedsUpdate(true)
97 {
98 }
setNeedsUpdate()99 void setNeedsUpdate()
100 {
101 m_mapNeedsUpdate = true;
102 }
103 void update(KBookmarkManager *);
find(const QString & url) const104 QList<KBookmark> find(const QString &url) const
105 {
106 return m_bk_map.value(url);
107 }
108
109 private:
110 void visit(const KBookmark &) override;
visitEnter(const KBookmarkGroup &)111 void visitEnter(const KBookmarkGroup &) override
112 {
113 ;
114 }
visitLeave(const KBookmarkGroup &)115 void visitLeave(const KBookmarkGroup &) override
116 {
117 ;
118 }
119
120 private:
121 typedef QList<KBookmark> KBookmarkList;
122 QMap<QString, KBookmarkList> m_bk_map;
123 bool m_mapNeedsUpdate;
124 };
125
update(KBookmarkManager * manager)126 void KBookmarkMap::update(KBookmarkManager *manager)
127 {
128 if (m_mapNeedsUpdate) {
129 m_mapNeedsUpdate = false;
130
131 m_bk_map.clear();
132 KBookmarkGroup root = manager->root();
133 traverse(root);
134 }
135 }
136
visit(const KBookmark & bk)137 void KBookmarkMap::visit(const KBookmark &bk)
138 {
139 if (!bk.isSeparator()) {
140 // add bookmark to url map
141 m_bk_map[bk.internalElement().attribute(QStringLiteral("href"))].append(bk);
142 }
143 }
144
145 // #########################
146 // KBookmarkManagerPrivate
147 class KBookmarkManagerPrivate
148 {
149 public:
KBookmarkManagerPrivate(bool bDocIsloaded,const QString & dbusObjectName=QString ())150 KBookmarkManagerPrivate(bool bDocIsloaded, const QString &dbusObjectName = QString())
151 : m_doc(QStringLiteral("xbel"))
152 , m_dbusObjectName(dbusObjectName)
153 , m_docIsLoaded(bDocIsloaded)
154 , m_update(false)
155 , m_dialogAllowed(true)
156 , m_dialogParent(nullptr)
157 , m_browserEditor(false)
158 , m_typeExternal(false)
159 , m_dirWatch(nullptr)
160 {
161 }
162
~KBookmarkManagerPrivate()163 ~KBookmarkManagerPrivate()
164 {
165 delete m_dirWatch;
166 }
167
168 mutable QDomDocument m_doc;
169 mutable QDomDocument m_toolbarDoc;
170 QString m_bookmarksFile;
171 QString m_dbusObjectName;
172 mutable bool m_docIsLoaded;
173 bool m_update;
174 bool m_dialogAllowed;
175 QWidget *m_dialogParent;
176
177 bool m_browserEditor;
178 QString m_editorCaption;
179
180 bool m_typeExternal;
181 KDirWatch *m_dirWatch; // for external bookmark files
182
183 KBookmarkMap m_map;
184 };
185
186 // ################
187 // KBookmarkManager
188
lookupExisting(const QString & bookmarksFile)189 static KBookmarkManager *lookupExisting(const QString &bookmarksFile)
190 {
191 for (KBookmarkManagerList::ConstIterator bmit = s_pSelf()->constBegin(), bmend = s_pSelf()->constEnd(); bmit != bmend; ++bmit) {
192 if ((*bmit)->path() == bookmarksFile) {
193 return *bmit;
194 }
195 }
196 return nullptr;
197 }
198
managerForFile(const QString & bookmarksFile,const QString & dbusObjectName)199 KBookmarkManager *KBookmarkManager::managerForFile(const QString &bookmarksFile, const QString &dbusObjectName)
200 {
201 KBookmarkManager *mgr(nullptr);
202 {
203 QReadLocker readLock(&s_pSelf()->lock);
204 mgr = lookupExisting(bookmarksFile);
205 if (mgr) {
206 return mgr;
207 }
208 }
209
210 QWriteLocker writeLock(&s_pSelf()->lock);
211 mgr = lookupExisting(bookmarksFile);
212 if (mgr) {
213 return mgr;
214 }
215
216 mgr = new KBookmarkManager(bookmarksFile, dbusObjectName);
217 s_pSelf()->append(mgr);
218 return mgr;
219 }
220
managerForExternalFile(const QString & bookmarksFile)221 KBookmarkManager *KBookmarkManager::managerForExternalFile(const QString &bookmarksFile)
222 {
223 KBookmarkManager *mgr(nullptr);
224 {
225 QReadLocker readLock(&s_pSelf()->lock);
226 mgr = lookupExisting(bookmarksFile);
227 if (mgr) {
228 return mgr;
229 }
230 }
231
232 QWriteLocker writeLock(&s_pSelf()->lock);
233 mgr = lookupExisting(bookmarksFile);
234 if (mgr) {
235 return mgr;
236 }
237
238 mgr = new KBookmarkManager(bookmarksFile);
239 s_pSelf()->append(mgr);
240 return mgr;
241 }
242
243 // principally used for filtered toolbars
createTempManager()244 KBookmarkManager *KBookmarkManager::createTempManager()
245 {
246 KBookmarkManager *mgr = new KBookmarkManager();
247 s_pSelf()->append(mgr);
248 return mgr;
249 }
250
createXbelTopLevelElement(QDomDocument & doc)251 static QDomElement createXbelTopLevelElement(QDomDocument &doc)
252 {
253 QDomElement topLevel = doc.createElement(QStringLiteral("xbel"));
254 topLevel.setAttribute(QStringLiteral("xmlns:mime"), QStringLiteral("http://www.freedesktop.org/standards/shared-mime-info"));
255 topLevel.setAttribute(QStringLiteral("xmlns:bookmark"), QStringLiteral("http://www.freedesktop.org/standards/desktop-bookmarks"));
256 topLevel.setAttribute(QStringLiteral("xmlns:kdepriv"), QStringLiteral("http://www.kde.org/kdepriv"));
257 doc.appendChild(topLevel);
258 doc.insertBefore(doc.createProcessingInstruction(QStringLiteral("xml"), Strings::piData()), topLevel);
259 return topLevel;
260 }
261
KBookmarkManager(const QString & bookmarksFile,const QString & dbusObjectName)262 KBookmarkManager::KBookmarkManager(const QString &bookmarksFile, const QString &dbusObjectName)
263 : d(new KBookmarkManagerPrivate(false, dbusObjectName))
264 {
265 if (dbusObjectName.isNull()) { // get dbusObjectName from file
266 if (QFile::exists(d->m_bookmarksFile)) {
267 parse(); // sets d->m_dbusObjectName
268 }
269 }
270
271 init(QLatin1String("/KBookmarkManager/") + d->m_dbusObjectName);
272
273 d->m_update = true;
274
275 Q_ASSERT(!bookmarksFile.isEmpty());
276 d->m_bookmarksFile = bookmarksFile;
277
278 if (!QFile::exists(d->m_bookmarksFile)) {
279 QDomElement topLevel = createXbelTopLevelElement(d->m_doc);
280 topLevel.setAttribute(QStringLiteral("dbusName"), dbusObjectName);
281 d->m_docIsLoaded = true;
282 }
283 }
284
KBookmarkManager(const QString & bookmarksFile)285 KBookmarkManager::KBookmarkManager(const QString &bookmarksFile)
286 : d(new KBookmarkManagerPrivate(false))
287 {
288 // use QFileSystemWatcher to monitor this bookmarks file
289 d->m_typeExternal = true;
290 d->m_update = true;
291
292 Q_ASSERT(!bookmarksFile.isEmpty());
293 d->m_bookmarksFile = bookmarksFile;
294
295 if (!QFile::exists(d->m_bookmarksFile)) {
296 createXbelTopLevelElement(d->m_doc);
297 } else {
298 parse();
299 }
300 d->m_docIsLoaded = true;
301
302 // start KDirWatch
303 d->m_dirWatch = new KDirWatch;
304 d->m_dirWatch->addFile(d->m_bookmarksFile);
305 QObject::connect(d->m_dirWatch, &KDirWatch::dirty, this, &KBookmarkManager::slotFileChanged);
306 QObject::connect(d->m_dirWatch, &KDirWatch::created, this, &KBookmarkManager::slotFileChanged);
307 QObject::connect(d->m_dirWatch, &KDirWatch::deleted, this, &KBookmarkManager::slotFileChanged);
308
309 // qCDebug(KBOOKMARKS_LOG) << "starting KDirWatch for" << d->m_bookmarksFile;
310 }
311
KBookmarkManager()312 KBookmarkManager::KBookmarkManager()
313 : d(new KBookmarkManagerPrivate(true))
314 {
315 init(QStringLiteral("/KBookmarkManager/generated"));
316 d->m_update = false; // TODO - make it read/write
317
318 createXbelTopLevelElement(d->m_doc);
319 }
320
init(const QString & dbusPath)321 void KBookmarkManager::init(const QString &dbusPath)
322 {
323 #ifndef KBOOKMARKS_NO_DBUS
324 // A KBookmarkManager without a dbus name is a temporary one, like those used by importers;
325 // no need to register them to dbus
326 if (dbusPath != QLatin1String("/KBookmarkManager/") && dbusPath != QLatin1String("/KBookmarkManager/generated")) {
327 new KBookmarkManagerAdaptor(this);
328 QDBusConnection::sessionBus().registerObject(dbusPath, this);
329
330 QDBusConnection::sessionBus().connect(QString(),
331 dbusPath,
332 Strings::bookmarkChangeNotifyInterface(),
333 QStringLiteral("bookmarksChanged"),
334 this,
335 SLOT(notifyChanged(QString, QDBusMessage)));
336 QDBusConnection::sessionBus()
337 .connect(QString(), dbusPath, Strings::bookmarkChangeNotifyInterface(), QStringLiteral("bookmarkConfigChanged"), this, SLOT(notifyConfigChanged()));
338 }
339 #endif
340 }
341
startKEditBookmarks(const QStringList & args)342 void KBookmarkManager::startKEditBookmarks(const QStringList &args)
343 {
344 bool success = QProcess::startDetached(QStringLiteral(KEDITBOOKMARKS_BINARY), args);
345
346 if (!success) {
347 QString err =
348 tr("Cannot launch keditbookmarks.\n\n"
349 "Most likely you do not have keditbookmarks currently installed");
350
351 if (d->m_dialogAllowed && qobject_cast<QApplication *>(qApp) && QThread::currentThread() == qApp->thread()) {
352 QMessageBox::warning(QApplication::activeWindow(), QApplication::applicationName(), err);
353 }
354
355 qCWarning(KBOOKMARKS_LOG) << QStringLiteral("Failed to start keditbookmarks");
356 Q_EMIT this->error(err);
357 }
358 }
359
slotFileChanged(const QString & path)360 void KBookmarkManager::slotFileChanged(const QString &path)
361 {
362 if (path == d->m_bookmarksFile) {
363 // qCDebug(KBOOKMARKS_LOG) << "file changed (KDirWatch) " << path ;
364 // Reparse
365 parse();
366 // Tell our GUI
367 // (emit where group is "" to directly mark the root menu as dirty)
368 Q_EMIT changed(QLatin1String(""), QString());
369 }
370 }
371
~KBookmarkManager()372 KBookmarkManager::~KBookmarkManager()
373 {
374 if (!s_pSelf.isDestroyed()) {
375 s_pSelf()->removeAll(this);
376 }
377 }
378
autoErrorHandlingEnabled() const379 bool KBookmarkManager::autoErrorHandlingEnabled() const
380 {
381 return d->m_dialogAllowed;
382 }
383
setAutoErrorHandlingEnabled(bool enable,QWidget * parent)384 void KBookmarkManager::setAutoErrorHandlingEnabled(bool enable, QWidget *parent)
385 {
386 d->m_dialogAllowed = enable;
387 d->m_dialogParent = parent;
388 }
389
setUpdate(bool update)390 void KBookmarkManager::setUpdate(bool update)
391 {
392 d->m_update = update;
393 }
394
internalDocument() const395 QDomDocument KBookmarkManager::internalDocument() const
396 {
397 if (!d->m_docIsLoaded) {
398 parse();
399 d->m_toolbarDoc.clear();
400 }
401 return d->m_doc;
402 }
403
parse() const404 void KBookmarkManager::parse() const
405 {
406 d->m_docIsLoaded = true;
407 // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::parse " << d->m_bookmarksFile;
408 QFile file(d->m_bookmarksFile);
409 if (!file.open(QIODevice::ReadOnly)) {
410 qCWarning(KBOOKMARKS_LOG) << "Can't open" << d->m_bookmarksFile;
411 d->m_doc = QDomDocument(QStringLiteral("xbel"));
412 createXbelTopLevelElement(d->m_doc);
413 return;
414 }
415 d->m_doc = QDomDocument(QStringLiteral("xbel"));
416 d->m_doc.setContent(&file);
417
418 if (d->m_doc.documentElement().isNull()) {
419 qCWarning(KBOOKMARKS_LOG) << "KBookmarkManager::parse : main tag is missing, creating default " << d->m_bookmarksFile;
420 QDomElement element = d->m_doc.createElement(QStringLiteral("xbel"));
421 d->m_doc.appendChild(element);
422 }
423
424 QDomElement docElem = d->m_doc.documentElement();
425
426 QString mainTag = docElem.tagName();
427 if (mainTag != QLatin1String("xbel")) {
428 qCWarning(KBOOKMARKS_LOG) << "KBookmarkManager::parse : unknown main tag " << mainTag;
429 }
430
431 if (d->m_dbusObjectName.isNull()) {
432 d->m_dbusObjectName = docElem.attribute(QStringLiteral("dbusName"));
433 } else if (docElem.attribute(QStringLiteral("dbusName")) != d->m_dbusObjectName) {
434 docElem.setAttribute(QStringLiteral("dbusName"), d->m_dbusObjectName);
435 save();
436 }
437
438 QDomNode n = d->m_doc.documentElement().previousSibling();
439 if (n.isProcessingInstruction()) {
440 QDomProcessingInstruction pi = n.toProcessingInstruction();
441 pi.parentNode().removeChild(pi);
442 }
443
444 QDomProcessingInstruction pi;
445 pi = d->m_doc.createProcessingInstruction(QStringLiteral("xml"), Strings::piData());
446 d->m_doc.insertBefore(pi, docElem);
447
448 file.close();
449
450 d->m_map.setNeedsUpdate();
451 }
452
save(bool toolbarCache) const453 bool KBookmarkManager::save(bool toolbarCache) const
454 {
455 return saveAs(d->m_bookmarksFile, toolbarCache);
456 }
457
saveAs(const QString & filename,bool toolbarCache) const458 bool KBookmarkManager::saveAs(const QString &filename, bool toolbarCache) const
459 {
460 // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::save " << filename;
461
462 // Save the bookmark toolbar folder for quick loading
463 // but only when it will actually make things quicker
464 const QString cacheFilename = filename + QLatin1String(".tbcache");
465 if (toolbarCache && !root().isToolbarGroup()) {
466 QSaveFile cacheFile(cacheFilename);
467 if (cacheFile.open(QIODevice::WriteOnly)) {
468 QString str;
469 QTextStream stream(&str, QIODevice::WriteOnly);
470 stream << root().findToolbar();
471 const QByteArray cstr = str.toUtf8();
472 cacheFile.write(cstr.data(), cstr.length());
473 cacheFile.commit();
474 }
475 } else { // remove any (now) stale cache
476 QFile::remove(cacheFilename);
477 }
478
479 // Create parent dirs
480 QFileInfo info(filename);
481 QDir().mkpath(info.absolutePath());
482
483 QSaveFile file(filename);
484 if (file.open(QIODevice::WriteOnly)) {
485 KBackup::simpleBackupFile(file.fileName(), QString(), QStringLiteral(".bak"));
486 QTextStream stream(&file);
487 stream.setCodec(QTextCodec::codecForName("UTF-8"));
488 stream << internalDocument().toString();
489 stream.flush();
490 if (file.commit()) {
491 return true;
492 }
493 }
494
495 static int hadSaveError = false;
496 if (!hadSaveError) {
497 QString err = tr("Unable to save bookmarks in %1. Reported error was: %2. "
498 "This error message will only be shown once. The cause "
499 "of the error needs to be fixed as quickly as possible, "
500 "which is most likely a full hard drive.")
501 .arg(filename, file.errorString());
502
503 if (d->m_dialogAllowed && qobject_cast<QApplication *>(qApp) && QThread::currentThread() == qApp->thread()) {
504 QMessageBox::critical(QApplication::activeWindow(), QApplication::applicationName(), err);
505 }
506
507 qCCritical(KBOOKMARKS_LOG)
508 << QStringLiteral("Unable to save bookmarks in %1. File reported the following error-code: %2.").arg(filename).arg(file.error());
509 Q_EMIT const_cast<KBookmarkManager *>(this)->error(err);
510 }
511 hadSaveError = true;
512 return false;
513 }
514
path() const515 QString KBookmarkManager::path() const
516 {
517 return d->m_bookmarksFile;
518 }
519
root() const520 KBookmarkGroup KBookmarkManager::root() const
521 {
522 return KBookmarkGroup(internalDocument().documentElement());
523 }
524
toolbar()525 KBookmarkGroup KBookmarkManager::toolbar()
526 {
527 // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::toolbar begin";
528 // Only try to read from a toolbar cache if the full document isn't loaded
529 if (!d->m_docIsLoaded) {
530 // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::toolbar trying cache";
531 const QString cacheFilename = d->m_bookmarksFile + QLatin1String(".tbcache");
532 QFileInfo bmInfo(d->m_bookmarksFile);
533 QFileInfo cacheInfo(cacheFilename);
534 if (d->m_toolbarDoc.isNull() && QFile::exists(cacheFilename) && bmInfo.lastModified() < cacheInfo.lastModified()) {
535 // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::toolbar reading file";
536 QFile file(cacheFilename);
537
538 if (file.open(QIODevice::ReadOnly)) {
539 d->m_toolbarDoc = QDomDocument(QStringLiteral("cache"));
540 d->m_toolbarDoc.setContent(&file);
541 // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::toolbar opened";
542 }
543 }
544 if (!d->m_toolbarDoc.isNull()) {
545 // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::toolbar returning element";
546 QDomElement elem = d->m_toolbarDoc.firstChild().toElement();
547 return KBookmarkGroup(elem);
548 }
549 }
550
551 // Fallback to the normal way if there is no cache or if the bookmark file
552 // is already loaded
553 QDomElement elem = root().findToolbar();
554 if (elem.isNull()) {
555 // Root is the bookmark toolbar if none has been set.
556 // Make it explicit to speed up invocations of findToolbar()
557 root().internalElement().setAttribute(QStringLiteral("toolbar"), QStringLiteral("yes"));
558 return root();
559 } else {
560 return KBookmarkGroup(elem);
561 }
562 }
563
findByAddress(const QString & address)564 KBookmark KBookmarkManager::findByAddress(const QString &address)
565 {
566 // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::findByAddress " << address;
567 KBookmark result = root();
568 // The address is something like /5/10/2+
569 const QStringList addresses = address.split(QRegularExpression(QStringLiteral("[/+]")), Qt::SkipEmptyParts);
570 // qCWarning(KBOOKMARKS_LOG) << addresses.join(",");
571 for (QStringList::const_iterator it = addresses.begin(); it != addresses.end();) {
572 bool append = ((*it) == QLatin1String("+"));
573 uint number = (*it).toUInt();
574 Q_ASSERT(result.isGroup());
575 KBookmarkGroup group = result.toGroup();
576 KBookmark bk = group.first();
577 KBookmark lbk = bk; // last non-null bookmark
578 for (uint i = 0; ((i < number) || append) && !bk.isNull(); ++i) {
579 lbk = bk;
580 bk = group.next(bk);
581 // qCWarning(KBOOKMARKS_LOG) << i;
582 }
583 it++;
584 // qCWarning(KBOOKMARKS_LOG) << "found section";
585 result = bk;
586 }
587 if (result.isNull()) {
588 qCWarning(KBOOKMARKS_LOG) << "KBookmarkManager::findByAddress: couldn't find item " << address;
589 }
590 // qCWarning(KBOOKMARKS_LOG) << "found " << result.address();
591 return result;
592 }
593
emitChanged()594 void KBookmarkManager::emitChanged()
595 {
596 emitChanged(root());
597 }
598
emitChanged(const KBookmarkGroup & group)599 void KBookmarkManager::emitChanged(const KBookmarkGroup &group)
600 {
601 (void)save(); // KDE5 TODO: emitChanged should return a bool? Maybe rename it to saveAndEmitChanged?
602
603 // Tell the other processes too
604 // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::emitChanged : broadcasting change " << group.address();
605
606 Q_EMIT bookmarksChanged(group.address());
607
608 // We do get our own broadcast, so no need for this anymore
609 // emit changed( group );
610 }
611
emitConfigChanged()612 void KBookmarkManager::emitConfigChanged()
613 {
614 Q_EMIT bookmarkConfigChanged();
615 }
616
notifyCompleteChange(const QString & caller)617 void KBookmarkManager::notifyCompleteChange(const QString &caller) // DBUS call
618 {
619 if (!d->m_update) {
620 return;
621 }
622
623 // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::notifyCompleteChange";
624 // The bk editor tells us we should reload everything
625 // Reparse
626 parse();
627 // Tell our GUI
628 // (emit where group is "" to directly mark the root menu as dirty)
629 Q_EMIT changed(QLatin1String(""), caller);
630 }
631
notifyConfigChanged()632 void KBookmarkManager::notifyConfigChanged() // DBUS call
633 {
634 // qCDebug(KBOOKMARKS_LOG) << "reloaded bookmark config!";
635 KBookmarkSettings::self()->readSettings();
636 parse(); // reload, and thusly recreate the menus
637 Q_EMIT configChanged();
638 }
639
640 #ifndef KBOOKMARKS_NO_DBUS
notifyChanged(const QString & groupAddress,const QDBusMessage & msg)641 void KBookmarkManager::notifyChanged(const QString &groupAddress, const QDBusMessage &msg) // DBUS call
642 {
643 // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::notifyChanged ( "<<groupAddress<<")";
644 if (!d->m_update) {
645 return;
646 }
647
648 // Reparse (the whole file, no other choice)
649 // if someone else notified us
650 if (msg.service() != QDBusConnection::sessionBus().baseService()) {
651 parse();
652 }
653
654 // qCDebug(KBOOKMARKS_LOG) << "KBookmarkManager::notifyChanged " << groupAddress;
655 // KBookmarkGroup group = findByAddress( groupAddress ).toGroup();
656 // Q_ASSERT(!group.isNull());
657 Q_EMIT changed(groupAddress, QString());
658 }
659 #endif
660
setEditorOptions(const QString & caption,bool browser)661 void KBookmarkManager::setEditorOptions(const QString &caption, bool browser)
662 {
663 d->m_editorCaption = caption;
664 d->m_browserEditor = browser;
665 }
666
slotEditBookmarks()667 void KBookmarkManager::slotEditBookmarks()
668 {
669 QStringList args;
670 if (!d->m_editorCaption.isEmpty()) {
671 args << QStringLiteral("--customcaption") << d->m_editorCaption;
672 }
673 if (!d->m_browserEditor) {
674 args << QStringLiteral("--nobrowser");
675 }
676 if (!d->m_dbusObjectName.isEmpty()) {
677 args << QStringLiteral("--dbusObjectName") << d->m_dbusObjectName;
678 }
679 args << d->m_bookmarksFile;
680 startKEditBookmarks(args);
681 }
682
slotEditBookmarksAtAddress(const QString & address)683 void KBookmarkManager::slotEditBookmarksAtAddress(const QString &address)
684 {
685 QStringList args;
686 if (!d->m_editorCaption.isEmpty()) {
687 args << QStringLiteral("--customcaption") << d->m_editorCaption;
688 }
689 if (!d->m_browserEditor) {
690 args << QStringLiteral("--nobrowser");
691 }
692 if (!d->m_dbusObjectName.isEmpty()) {
693 args << QStringLiteral("--dbusObjectName") << d->m_dbusObjectName;
694 }
695 args << QStringLiteral("--address") << address << d->m_bookmarksFile;
696 startKEditBookmarks(args);
697 }
698
699 ///////
updateAccessMetadata(const QString & url)700 bool KBookmarkManager::updateAccessMetadata(const QString &url)
701 {
702 d->m_map.update(this);
703 QList<KBookmark> list = d->m_map.find(url);
704 if (list.isEmpty()) {
705 return false;
706 }
707
708 for (QList<KBookmark>::iterator it = list.begin(); it != list.end(); ++it) {
709 (*it).updateAccessMetadata();
710 }
711
712 return true;
713 }
714
updateFavicon(const QString & url,const QString &)715 void KBookmarkManager::updateFavicon(const QString &url, const QString & /*faviconurl*/)
716 {
717 d->m_map.update(this);
718 QList<KBookmark> list = d->m_map.find(url);
719 for (QList<KBookmark>::iterator it = list.begin(); it != list.end(); ++it) {
720 // TODO - update favicon data based on faviconurl
721 // but only when the previously used icon
722 // isn't a manually set one.
723 }
724 }
725
userBookmarksManager()726 KBookmarkManager *KBookmarkManager::userBookmarksManager()
727 {
728 const QString bookmarksFile =
729 QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/konqueror/bookmarks.xml");
730 KBookmarkManager *bookmarkManager = KBookmarkManager::managerForFile(bookmarksFile, QStringLiteral("konqueror"));
731 QString caption = QGuiApplication::applicationDisplayName();
732 if (caption.isEmpty()) {
733 caption = QCoreApplication::applicationName();
734 }
735 bookmarkManager->setEditorOptions(caption, true);
736 return bookmarkManager;
737 }
738
739 KBookmarkSettings *KBookmarkSettings::s_self = nullptr;
740
readSettings()741 void KBookmarkSettings::readSettings()
742 {
743 KConfig config(QStringLiteral("kbookmarkrc"), KConfig::NoGlobals);
744 KConfigGroup cg(&config, "Bookmarks");
745
746 // add bookmark dialog usage - no reparse
747 s_self->m_advancedaddbookmark = cg.readEntry("AdvancedAddBookmarkDialog", false);
748
749 // this one alters the menu, therefore it needs a reparse
750 s_self->m_contextmenu = cg.readEntry("ContextMenuActions", true);
751 }
752
self()753 KBookmarkSettings *KBookmarkSettings::self()
754 {
755 if (!s_self) {
756 s_self = new KBookmarkSettings;
757 readSettings();
758 }
759 return s_self;
760 }
761
762 #include "moc_kbookmarkmanager.cpp"
763