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 "kbookmark.h"
12 #include "kbookmarks_debug.h"
13 #include <kbookmarkmanager.h>
14 
15 #include <KStringHandler>
16 #include <kurlmimedata.h>
17 
18 #include <QCoreApplication>
19 #include <QMimeDatabase>
20 #include <QStack>
21 #include <QDateTime>
22 #include <QMimeData>
23 
24 namespace
25 {
26 namespace Strings
27 {
metaDataKDEOwner()28 QString metaDataKDEOwner()
29 {
30     return QStringLiteral("http://www.kde.org");
31 }
metaDataFreedesktopOwner()32 QString metaDataFreedesktopOwner()
33 {
34     return QStringLiteral("http://freedesktop.org");
35 }
metaDataMimeOwner()36 QString metaDataMimeOwner()
37 {
38     return QStringLiteral("http://www.freedesktop.org/standards/shared-mime-info");
39 }
40 
xbelMimeType()41 QString xbelMimeType()
42 {
43     return QStringLiteral("application/x-xbel");
44 }
45 }
46 }
47 
48 ////// utility functions
49 
cd(QDomNode node,const QString & name,bool create)50 static QDomNode cd(QDomNode node, const QString &name, bool create)
51 {
52     QDomNode subnode = node.namedItem(name);
53     if (create && subnode.isNull()) {
54         subnode = node.ownerDocument().createElement(name);
55         node.appendChild(subnode);
56     }
57     return subnode;
58 }
59 
cd_or_create(const QDomNode & node,const QString & name)60 static QDomNode cd_or_create(const QDomNode &node, const QString &name)
61 {
62     return cd(node, name, true);
63 }
64 
get_or_create_text(QDomNode node)65 static QDomText get_or_create_text(QDomNode node)
66 {
67     QDomNode subnode = node.firstChild();
68     if (subnode.isNull()) {
69         subnode = node.ownerDocument().createTextNode(QLatin1String(""));
70         node.appendChild(subnode);
71     }
72     return subnode.toText();
73 }
74 
findMetadata(const QString & forOwner,QDomNode & parent,bool create)75 static QDomNode findMetadata(const QString &forOwner, QDomNode &parent, bool create)
76 {
77     const bool forOwnerIsKDE = (forOwner == Strings::metaDataKDEOwner());
78 
79     QDomElement metadataElement;
80     for (QDomNode _node = parent.firstChild(); !_node.isNull(); _node = _node.nextSibling()) {
81         QDomElement elem = _node.toElement();
82         if (!elem.isNull() && elem.tagName() == QLatin1String("metadata")) {
83             const QString owner = elem.attribute(QStringLiteral("owner"));
84             if (owner == forOwner) {
85                 return elem;
86             }
87             if (owner.isEmpty() && forOwnerIsKDE) {
88                 metadataElement = elem;
89             }
90         }
91     }
92     if (create && metadataElement.isNull()) {
93         metadataElement = parent.ownerDocument().createElement(QStringLiteral("metadata"));
94         parent.appendChild(metadataElement);
95         metadataElement.setAttribute(QStringLiteral("owner"), forOwner);
96 
97     } else if (!metadataElement.isNull() && forOwnerIsKDE) {
98         // i'm not sure if this is good, we shouldn't take over foreign metadata
99         metadataElement.setAttribute(QStringLiteral("owner"), Strings::metaDataKDEOwner());
100     }
101     return metadataElement;
102 }
103 
104 //////
105 
KBookmarkGroup()106 KBookmarkGroup::KBookmarkGroup()
107     : KBookmark(QDomElement())
108 {
109 }
110 
KBookmarkGroup(const QDomElement & elem)111 KBookmarkGroup::KBookmarkGroup(const QDomElement &elem)
112     : KBookmark(elem)
113 {
114 }
115 
isOpen() const116 bool KBookmarkGroup::isOpen() const
117 {
118     return element.attribute(QStringLiteral("folded")) == QLatin1String("no"); // default is: folded
119 }
120 
first() const121 KBookmark KBookmarkGroup::first() const
122 {
123     return KBookmark(nextKnownTag(element.firstChildElement(), true));
124 }
125 
previous(const KBookmark & current) const126 KBookmark KBookmarkGroup::previous(const KBookmark &current) const
127 {
128     return KBookmark(nextKnownTag(current.element.previousSiblingElement(), false));
129 }
130 
next(const KBookmark & current) const131 KBookmark KBookmarkGroup::next(const KBookmark &current) const
132 {
133     return KBookmark(nextKnownTag(current.element.nextSiblingElement(), true));
134 }
135 
indexOf(const KBookmark & child) const136 int KBookmarkGroup::indexOf(const KBookmark &child) const
137 {
138     int counter = 0;
139     for (KBookmark bk = first(); !bk.isNull(); bk = next(bk), ++counter) {
140         if (bk.element == child.element) {
141             return counter;
142         }
143     }
144     return -1;
145 }
146 
nextKnownTag(const QDomElement & start,bool goNext) const147 QDomElement KBookmarkGroup::nextKnownTag(const QDomElement &start, bool goNext) const
148 {
149     for (QDomElement elem = start; !elem.isNull();) {
150         QString tag = elem.tagName();
151         if (tag == QLatin1String("folder") || tag == QLatin1String("bookmark") || tag == QLatin1String("separator")) {
152             return elem;
153         }
154         if (goNext) {
155             elem = elem.nextSiblingElement();
156         } else {
157             elem = elem.previousSiblingElement();
158         }
159     }
160     return QDomElement();
161 }
162 
createNewFolder(const QString & text)163 KBookmarkGroup KBookmarkGroup::createNewFolder(const QString &text)
164 {
165     if (isNull()) {
166         return KBookmarkGroup();
167     }
168     QDomDocument doc = element.ownerDocument();
169     QDomElement groupElem = doc.createElement(QStringLiteral("folder"));
170     element.appendChild(groupElem);
171     QDomElement textElem = doc.createElement(QStringLiteral("title"));
172     groupElem.appendChild(textElem);
173     textElem.appendChild(doc.createTextNode(text));
174     return KBookmarkGroup(groupElem);
175 }
176 
createNewSeparator()177 KBookmark KBookmarkGroup::createNewSeparator()
178 {
179     if (isNull()) {
180         return KBookmark();
181     }
182     QDomDocument doc = element.ownerDocument();
183     Q_ASSERT(!doc.isNull());
184     QDomElement sepElem = doc.createElement(QStringLiteral("separator"));
185     element.appendChild(sepElem);
186     return KBookmark(sepElem);
187 }
188 
moveBookmark(const KBookmark & item,const KBookmark & after)189 bool KBookmarkGroup::moveBookmark(const KBookmark &item, const KBookmark &after)
190 {
191     QDomNode n;
192     if (!after.isNull()) {
193         n = element.insertAfter(item.element, after.element);
194     } else { // first child
195         if (element.firstChild().isNull()) { // Empty element -> set as real first child
196             n = element.insertBefore(item.element, QDomElement());
197         }
198 
199         // we have to skip everything up to the first valid child
200         QDomElement firstChild = nextKnownTag(element.firstChild().toElement(), true);
201         if (!firstChild.isNull()) {
202             if (firstChild == item.element) { // item is already the first child, done
203                 return true;
204             }
205             n = element.insertBefore(item.element, firstChild);
206         } else {
207             // No real first child -> append after the <title> etc.
208             n = element.appendChild(item.element);
209         }
210     }
211     return (!n.isNull());
212 }
213 
addBookmark(const KBookmark & bm)214 KBookmark KBookmarkGroup::addBookmark(const KBookmark &bm)
215 {
216     element.appendChild(bm.internalElement());
217     return bm;
218 }
219 
addBookmark(const QString & text,const QUrl & url,const QString & icon)220 KBookmark KBookmarkGroup::addBookmark(const QString &text, const QUrl &url, const QString &icon)
221 {
222     if (isNull()) {
223         return KBookmark();
224     }
225     QDomDocument doc = element.ownerDocument();
226     QDomElement elem = doc.createElement(QStringLiteral("bookmark"));
227     elem.setAttribute(QStringLiteral("href"), url.toString(QUrl::FullyEncoded));
228 
229     QDomElement textElem = doc.createElement(QStringLiteral("title"));
230     elem.appendChild(textElem);
231     textElem.appendChild(doc.createTextNode(text));
232 
233     KBookmark newBookmark = addBookmark(KBookmark(elem));
234 
235     // as icons are moved to metadata, we have to use the KBookmark API for this
236     newBookmark.setIcon(icon);
237     return newBookmark;
238 }
239 
deleteBookmark(const KBookmark & bk)240 void KBookmarkGroup::deleteBookmark(const KBookmark &bk)
241 {
242     element.removeChild(bk.element);
243 }
244 
isToolbarGroup() const245 bool KBookmarkGroup::isToolbarGroup() const
246 {
247     return (element.attribute(QStringLiteral("toolbar")) == QLatin1String("yes"));
248 }
249 
findToolbar() const250 QDomElement KBookmarkGroup::findToolbar() const
251 {
252     if (element.attribute(QStringLiteral("toolbar")) == QLatin1String("yes")) {
253         return element;
254     }
255     for (QDomElement e = element.firstChildElement(QStringLiteral("folder")); !e.isNull(); e = e.nextSiblingElement(QStringLiteral("folder"))) {
256         QDomElement result = KBookmarkGroup(e).findToolbar();
257         if (!result.isNull()) {
258             return result;
259         }
260     }
261     return QDomElement();
262 }
263 
groupUrlList() const264 QList<QUrl> KBookmarkGroup::groupUrlList() const
265 {
266     QList<QUrl> urlList;
267     for (KBookmark bm = first(); !bm.isNull(); bm = next(bm)) {
268         if (bm.isSeparator() || bm.isGroup()) {
269             continue;
270         }
271         urlList << bm.url();
272     }
273     return urlList;
274 }
275 
276 //////
277 
KBookmark()278 KBookmark::KBookmark()
279 {
280 }
281 
KBookmark(const QDomElement & elem)282 KBookmark::KBookmark(const QDomElement &elem)
283     : element(elem)
284 {
285 }
286 
isGroup() const287 bool KBookmark::isGroup() const
288 {
289     QString tag = element.tagName();
290     return tag == QLatin1String("folder") //
291         || tag == QLatin1String("xbel"); // don't forget the toplevel group
292 }
293 
isSeparator() const294 bool KBookmark::isSeparator() const
295 {
296     return (element.tagName() == QLatin1String("separator"));
297 }
298 
isNull() const299 bool KBookmark::isNull() const
300 {
301     return element.isNull();
302 }
303 
hasParent() const304 bool KBookmark::hasParent() const
305 {
306     QDomElement parent = element.parentNode().toElement();
307     return !parent.isNull();
308 }
309 
text() const310 QString KBookmark::text() const
311 {
312     return KStringHandler::csqueeze(fullText());
313 }
314 
fullText() const315 QString KBookmark::fullText() const
316 {
317     if (isSeparator()) {
318         return QCoreApplication::translate("KBookmark", "--- separator ---", "Bookmark separator");
319     }
320 
321     QString text = element.namedItem(QStringLiteral("title")).toElement().text();
322     text.replace(QLatin1Char('\n'), QLatin1Char(' ')); // #140673
323     return text;
324 }
325 
setFullText(const QString & fullText)326 void KBookmark::setFullText(const QString &fullText)
327 {
328     QDomNode titleNode = element.namedItem(QStringLiteral("title"));
329     if (titleNode.isNull()) {
330         titleNode = element.ownerDocument().createElement(QStringLiteral("title"));
331         element.appendChild(titleNode);
332     }
333 
334     if (titleNode.firstChild().isNull()) {
335         QDomText domtext = titleNode.ownerDocument().createTextNode(QLatin1String(""));
336         titleNode.appendChild(domtext);
337     }
338 
339     QDomText domtext = titleNode.firstChild().toText();
340     domtext.setData(fullText);
341 }
342 
url() const343 QUrl KBookmark::url() const
344 {
345     return QUrl(element.attribute(QStringLiteral("href")));
346 }
347 
setUrl(const QUrl & url)348 void KBookmark::setUrl(const QUrl &url)
349 {
350     element.setAttribute(QStringLiteral("href"), url.toString());
351 }
352 
icon() const353 QString KBookmark::icon() const
354 {
355     QDomNode metaDataNode = metaData(Strings::metaDataFreedesktopOwner(), false);
356     QDomElement iconElement = cd(metaDataNode, QStringLiteral("bookmark:icon"), false).toElement();
357 
358     QString icon = iconElement.attribute(QStringLiteral("name"));
359 
360     // migration code
361     if (icon.isEmpty()) {
362         icon = element.attribute(QStringLiteral("icon"));
363     }
364     if (icon == QLatin1String("www")) { // common icon for kde3 bookmarks
365         return QStringLiteral("internet-web-browser");
366     }
367     // end migration code
368 
369     if (icon == QLatin1String("bookmark_folder")) {
370         return QStringLiteral("folder-bookmarks");
371     }
372     if (icon.isEmpty()) {
373         // Default icon depends on URL for bookmarks, and is default directory
374         // icon for groups.
375         if (isGroup()) {
376             icon = QStringLiteral("folder-bookmarks");
377         } else {
378             if (isSeparator()) {
379                 icon = QStringLiteral("edit-clear"); // whatever
380             } else {
381                 // get icon from mimeType
382                 QMimeDatabase db;
383                 QMimeType mime;
384                 QString _mimeType = mimeType();
385                 if (!_mimeType.isEmpty()) {
386                     mime = db.mimeTypeForName(_mimeType);
387                 } else {
388                     mime = db.mimeTypeForUrl(url());
389                 }
390                 if (mime.isValid()) {
391                     icon = mime.iconName();
392                 }
393             }
394         }
395     }
396     return icon;
397 }
398 
setIcon(const QString & icon)399 void KBookmark::setIcon(const QString &icon)
400 {
401     QDomNode metaDataNode = metaData(Strings::metaDataFreedesktopOwner(), true);
402     QDomElement iconElement = cd_or_create(metaDataNode, QStringLiteral("bookmark:icon")).toElement();
403     iconElement.setAttribute(QStringLiteral("name"), icon);
404 
405     // migration code
406     if (!element.attribute(QStringLiteral("icon")).isEmpty()) {
407         element.removeAttribute(QStringLiteral("icon"));
408     }
409 }
410 
description() const411 QString KBookmark::description() const
412 {
413     if (isSeparator()) {
414         return QString();
415     }
416 
417     QString description = element.namedItem(QStringLiteral("desc")).toElement().text();
418     description.replace(QLatin1Char('\n'), QLatin1Char(' ')); // #140673
419     return description;
420 }
421 
setDescription(const QString & description)422 void KBookmark::setDescription(const QString &description)
423 {
424     QDomNode descNode = element.namedItem(QStringLiteral("desc"));
425     if (descNode.isNull()) {
426         descNode = element.ownerDocument().createElement(QStringLiteral("desc"));
427         element.appendChild(descNode);
428     }
429 
430     if (descNode.firstChild().isNull()) {
431         QDomText domtext = descNode.ownerDocument().createTextNode(QString());
432         descNode.appendChild(domtext);
433     }
434 
435     QDomText domtext = descNode.firstChild().toText();
436     domtext.setData(description);
437 }
438 
mimeType() const439 QString KBookmark::mimeType() const
440 {
441     QDomNode metaDataNode = metaData(Strings::metaDataMimeOwner(), false);
442     QDomElement mimeTypeElement = cd(metaDataNode, QStringLiteral("mime:mime-type"), false).toElement();
443     return mimeTypeElement.attribute(QStringLiteral("type"));
444 }
445 
setMimeType(const QString & mimeType)446 void KBookmark::setMimeType(const QString &mimeType)
447 {
448     QDomNode metaDataNode = metaData(Strings::metaDataMimeOwner(), true);
449     QDomElement iconElement = cd_or_create(metaDataNode, QStringLiteral("mime:mime-type")).toElement();
450     iconElement.setAttribute(QStringLiteral("type"), mimeType);
451 }
452 
showInToolbar() const453 bool KBookmark::showInToolbar() const
454 {
455     if (element.hasAttribute(QStringLiteral("showintoolbar"))) {
456         bool show = element.attribute(QStringLiteral("showintoolbar")) == QLatin1String("yes");
457         const_cast<QDomElement *>(&element)->removeAttribute(QStringLiteral("showintoolbar"));
458         const_cast<KBookmark *>(this)->setShowInToolbar(show);
459     }
460     return metaDataItem(QStringLiteral("showintoolbar")) == QLatin1String("yes");
461 }
462 
setShowInToolbar(bool show)463 void KBookmark::setShowInToolbar(bool show)
464 {
465     setMetaDataItem(QStringLiteral("showintoolbar"), show ? QStringLiteral("yes") : QStringLiteral("no"));
466 }
467 
parentGroup() const468 KBookmarkGroup KBookmark::parentGroup() const
469 {
470     return KBookmarkGroup(element.parentNode().toElement());
471 }
472 
toGroup() const473 KBookmarkGroup KBookmark::toGroup() const
474 {
475     Q_ASSERT(isGroup());
476     return KBookmarkGroup(element);
477 }
478 
address() const479 QString KBookmark::address() const
480 {
481     if (element.tagName() == QLatin1String("xbel")) {
482         return QLatin1String(""); // not QString() !
483     } else {
484         // Use keditbookmarks's DEBUG_ADDRESSES flag to debug this code :)
485         if (element.parentNode().isNull()) {
486             Q_ASSERT(false);
487             return QStringLiteral("ERROR"); // Avoid an infinite loop
488         }
489         KBookmarkGroup group = parentGroup();
490         QString parentAddress = group.address();
491         int pos = group.indexOf(*this);
492         Q_ASSERT(pos != -1);
493         return parentAddress + QLatin1Char('/') + QString::number(pos);
494     }
495 }
496 
positionInParent() const497 int KBookmark::positionInParent() const
498 {
499     return parentGroup().indexOf(*this);
500 }
501 
internalElement() const502 QDomElement KBookmark::internalElement() const
503 {
504     return element;
505 }
506 
standaloneBookmark(const QString & text,const QUrl & url,const QString & icon)507 KBookmark KBookmark::standaloneBookmark(const QString &text, const QUrl &url, const QString &icon)
508 {
509     QDomDocument doc(QStringLiteral("xbel"));
510     QDomElement elem = doc.createElement(QStringLiteral("xbel"));
511     doc.appendChild(elem);
512     KBookmarkGroup grp(elem);
513     grp.addBookmark(text, url, icon);
514     return grp.first();
515 }
516 
commonParent(const QString & first,const QString & second)517 QString KBookmark::commonParent(const QString &first, const QString &second)
518 {
519     QString A = first;
520     QString B = second;
521     QString error(QStringLiteral("ERROR"));
522     if (A == error || B == error) {
523         return error;
524     }
525 
526     A += QLatin1Char('/');
527     B += QLatin1Char('/');
528 
529     int lastCommonSlash = 0;
530     int lastPos = A.length() < B.length() ? A.length() : B.length();
531     for (int i = 0; i < lastPos; ++i) {
532         if (A[i] != B[i]) {
533             return A.left(lastCommonSlash);
534         }
535         if (A[i] == QLatin1Char('/')) {
536             lastCommonSlash = i;
537         }
538     }
539     return A.left(lastCommonSlash);
540 }
541 
updateAccessMetadata()542 void KBookmark::updateAccessMetadata()
543 {
544     // qCDebug(KBOOKMARKS_LOG) << "KBookmark::updateAccessMetadata " << address() << " " << url();
545 
546     const uint timet = QDateTime::currentDateTimeUtc().toSecsSinceEpoch();
547     setMetaDataItem(QStringLiteral("time_added"), QString::number(timet), DontOverwriteMetaData);
548     setMetaDataItem(QStringLiteral("time_visited"), QString::number(timet));
549 
550     QString countStr = metaDataItem(QStringLiteral("visit_count")); // TODO use spec'ed name
551     bool ok;
552     int currentCount = countStr.toInt(&ok);
553     if (!ok) {
554         currentCount = 0;
555     }
556     currentCount++;
557     setMetaDataItem(QStringLiteral("visit_count"), QString::number(currentCount));
558 
559     // TODO - time_modified
560 }
561 
parentAddress(const QString & address)562 QString KBookmark::parentAddress(const QString &address)
563 {
564     return address.left(address.lastIndexOf(QLatin1Char('/')));
565 }
566 
positionInParent(const QString & address)567 uint KBookmark::positionInParent(const QString &address)
568 {
569 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
570     return QStringView(address).mid(address.lastIndexOf(QLatin1Char('/')) + 1).toInt();
571 #else
572     return address.midRef(address.lastIndexOf(QLatin1Char('/')) + 1).toInt();
573 #endif
574 }
575 
previousAddress(const QString & address)576 QString KBookmark::previousAddress(const QString &address)
577 {
578     uint pp = positionInParent(address);
579     return pp > 0 ? parentAddress(address) + QLatin1Char('/') + QString::number(pp - 1) : QString();
580 }
581 
nextAddress(const QString & address)582 QString KBookmark::nextAddress(const QString &address)
583 {
584     return parentAddress(address) + QLatin1Char('/') + QString::number(positionInParent(address) + 1);
585 }
586 
metaData(const QString & owner,bool create) const587 QDomNode KBookmark::metaData(const QString &owner, bool create) const
588 {
589     QDomNode infoNode = cd(internalElement(), QStringLiteral("info"), create);
590     if (infoNode.isNull()) {
591         return QDomNode();
592     }
593     return findMetadata(owner, infoNode, create);
594 }
595 
metaDataItem(const QString & key) const596 QString KBookmark::metaDataItem(const QString &key) const
597 {
598     QDomNode metaDataNode = metaData(Strings::metaDataKDEOwner(), false);
599     for (QDomElement e = metaDataNode.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
600         if (e.tagName() == key) {
601             return e.text();
602         }
603     }
604     return QString();
605 }
606 
setMetaDataItem(const QString & key,const QString & value,MetaDataOverwriteMode mode)607 void KBookmark::setMetaDataItem(const QString &key, const QString &value, MetaDataOverwriteMode mode)
608 {
609     QDomNode metaDataNode = metaData(Strings::metaDataKDEOwner(), true);
610     QDomNode item = cd_or_create(metaDataNode, key);
611     QDomText text = get_or_create_text(item);
612     if (mode == DontOverwriteMetaData && !text.data().isEmpty()) {
613         return;
614     }
615 
616     text.setData(value);
617 }
618 
operator ==(const KBookmark & rhs) const619 bool KBookmark::operator==(const KBookmark &rhs) const
620 {
621     return element == rhs.element;
622 }
623 
624 ////
625 
~KBookmarkGroupTraverser()626 KBookmarkGroupTraverser::~KBookmarkGroupTraverser()
627 {
628 }
629 
traverse(const KBookmarkGroup & root)630 void KBookmarkGroupTraverser::traverse(const KBookmarkGroup &root)
631 {
632     QStack<KBookmarkGroup> stack;
633     stack.push(root);
634     KBookmark bk = root.first();
635     for (;;) {
636         if (bk.isNull()) {
637             if (stack.count() == 1) { // only root is on the stack
638                 return;
639             }
640             if (!stack.isEmpty()) {
641                 visitLeave(stack.top());
642                 bk = stack.pop();
643             }
644             bk = stack.top().next(bk);
645         } else if (bk.isGroup()) {
646             KBookmarkGroup gp = bk.toGroup();
647             visitEnter(gp);
648             bk = gp.first();
649             stack.push(gp);
650         } else {
651             visit(bk);
652             bk = stack.top().next(bk);
653         }
654     }
655 }
656 
visit(const KBookmark &)657 void KBookmarkGroupTraverser::visit(const KBookmark &)
658 {
659 }
660 
visitEnter(const KBookmarkGroup &)661 void KBookmarkGroupTraverser::visitEnter(const KBookmarkGroup &)
662 {
663 }
664 
visitLeave(const KBookmarkGroup &)665 void KBookmarkGroupTraverser::visitLeave(const KBookmarkGroup &)
666 {
667 }
668 
populateMimeData(QMimeData * mimeData) const669 void KBookmark::populateMimeData(QMimeData *mimeData) const
670 {
671     KBookmark::List bookmarkList;
672     bookmarkList.append(*this);
673     bookmarkList.populateMimeData(mimeData);
674 }
675 
List()676 KBookmark::List::List()
677     : QList<KBookmark>()
678 {
679 }
680 
populateMimeData(QMimeData * mimeData) const681 void KBookmark::List::populateMimeData(QMimeData *mimeData) const
682 {
683     QList<QUrl> urls;
684 
685     QDomDocument doc(QStringLiteral("xbel"));
686     QDomElement elem = doc.createElement(QStringLiteral("xbel"));
687     doc.appendChild(elem);
688 
689     for (const_iterator it = begin(), end = this->end(); it != end; ++it) {
690         urls.append((*it).url());
691         elem.appendChild((*it).internalElement().cloneNode(true /* deep */));
692     }
693 
694     // This sets text/uri-list and text/plain into the mimedata
695     mimeData->setUrls(urls);
696 
697     mimeData->setData(Strings::xbelMimeType(), doc.toByteArray());
698 }
699 
canDecode(const QMimeData * mimeData)700 bool KBookmark::List::canDecode(const QMimeData *mimeData)
701 {
702     return mimeData->hasFormat(Strings::xbelMimeType()) || mimeData->hasUrls();
703 }
704 
mimeDataTypes()705 QStringList KBookmark::List::mimeDataTypes()
706 {
707     return QStringList() << Strings::xbelMimeType() << KUrlMimeData::mimeDataTypes();
708 }
709 
fromMimeData(const QMimeData * mimeData,QDomDocument & doc)710 KBookmark::List KBookmark::List::fromMimeData(const QMimeData *mimeData, QDomDocument &doc)
711 {
712     KBookmark::List bookmarks;
713     const QByteArray payload = mimeData->data(Strings::xbelMimeType());
714     if (!payload.isEmpty()) {
715         doc.setContent(payload);
716         QDomElement elem = doc.documentElement();
717         const QDomNodeList children = elem.childNodes();
718         bookmarks.reserve(children.count());
719         for (int childno = 0; childno < children.count(); childno++) {
720             bookmarks.append(KBookmark(children.item(childno).toElement()));
721         }
722         return bookmarks;
723     }
724     const QList<QUrl> urls = KUrlMimeData::urlsFromMimeData(mimeData);
725     bookmarks.reserve(urls.size());
726     for (int i = 0; i < urls.size(); ++i) {
727         const QUrl url = urls.at(i);
728         bookmarks.append(KBookmark::standaloneBookmark(url.toDisplayString(), url, QString() /*TODO icon*/));
729     }
730     return bookmarks;
731 }
732