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

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 "kbookmark.h"
0012 #include "kbookmarks_debug.h"
0013 #include <kbookmarkmanager.h>
0014 
0015 #include <KStringHandler>
0016 #include <kurlmimedata.h>
0017 
0018 #include <QCoreApplication>
0019 #include <QMimeDatabase>
0020 #include <QStack>
0021 #include <QDateTime>
0022 #include <QMimeData>
0023 
0024 namespace
0025 {
0026 namespace Strings
0027 {
0028 QString metaDataKDEOwner()
0029 {
0030     return QStringLiteral("http://www.kde.org");
0031 }
0032 QString metaDataFreedesktopOwner()
0033 {
0034     return QStringLiteral("http://freedesktop.org");
0035 }
0036 QString metaDataMimeOwner()
0037 {
0038     return QStringLiteral("http://www.freedesktop.org/standards/shared-mime-info");
0039 }
0040 
0041 QString xbelMimeType()
0042 {
0043     return QStringLiteral("application/x-xbel");
0044 }
0045 }
0046 }
0047 
0048 ////// utility functions
0049 
0050 static QDomNode cd(QDomNode node, const QString &name, bool create)
0051 {
0052     QDomNode subnode = node.namedItem(name);
0053     if (create && subnode.isNull()) {
0054         subnode = node.ownerDocument().createElement(name);
0055         node.appendChild(subnode);
0056     }
0057     return subnode;
0058 }
0059 
0060 static QDomNode cd_or_create(const QDomNode &node, const QString &name)
0061 {
0062     return cd(node, name, true);
0063 }
0064 
0065 static QDomText get_or_create_text(QDomNode node)
0066 {
0067     QDomNode subnode = node.firstChild();
0068     if (subnode.isNull()) {
0069         subnode = node.ownerDocument().createTextNode(QLatin1String(""));
0070         node.appendChild(subnode);
0071     }
0072     return subnode.toText();
0073 }
0074 
0075 static QDomNode findMetadata(const QString &forOwner, QDomNode &parent, bool create)
0076 {
0077     const bool forOwnerIsKDE = (forOwner == Strings::metaDataKDEOwner());
0078 
0079     QDomElement metadataElement;
0080     for (QDomNode _node = parent.firstChild(); !_node.isNull(); _node = _node.nextSibling()) {
0081         QDomElement elem = _node.toElement();
0082         if (!elem.isNull() && elem.tagName() == QLatin1String("metadata")) {
0083             const QString owner = elem.attribute(QStringLiteral("owner"));
0084             if (owner == forOwner) {
0085                 return elem;
0086             }
0087             if (owner.isEmpty() && forOwnerIsKDE) {
0088                 metadataElement = elem;
0089             }
0090         }
0091     }
0092     if (create && metadataElement.isNull()) {
0093         metadataElement = parent.ownerDocument().createElement(QStringLiteral("metadata"));
0094         parent.appendChild(metadataElement);
0095         metadataElement.setAttribute(QStringLiteral("owner"), forOwner);
0096 
0097     } else if (!metadataElement.isNull() && forOwnerIsKDE) {
0098         // i'm not sure if this is good, we shouldn't take over foreign metadata
0099         metadataElement.setAttribute(QStringLiteral("owner"), Strings::metaDataKDEOwner());
0100     }
0101     return metadataElement;
0102 }
0103 
0104 //////
0105 
0106 KBookmarkGroup::KBookmarkGroup()
0107     : KBookmark(QDomElement())
0108 {
0109 }
0110 
0111 KBookmarkGroup::KBookmarkGroup(const QDomElement &elem)
0112     : KBookmark(elem)
0113 {
0114 }
0115 
0116 bool KBookmarkGroup::isOpen() const
0117 {
0118     return element.attribute(QStringLiteral("folded")) == QLatin1String("no"); // default is: folded
0119 }
0120 
0121 KBookmark KBookmarkGroup::first() const
0122 {
0123     return KBookmark(nextKnownTag(element.firstChildElement(), true));
0124 }
0125 
0126 KBookmark KBookmarkGroup::previous(const KBookmark &current) const
0127 {
0128     return KBookmark(nextKnownTag(current.element.previousSiblingElement(), false));
0129 }
0130 
0131 KBookmark KBookmarkGroup::next(const KBookmark &current) const
0132 {
0133     return KBookmark(nextKnownTag(current.element.nextSiblingElement(), true));
0134 }
0135 
0136 int KBookmarkGroup::indexOf(const KBookmark &child) const
0137 {
0138     int counter = 0;
0139     for (KBookmark bk = first(); !bk.isNull(); bk = next(bk), ++counter) {
0140         if (bk.element == child.element) {
0141             return counter;
0142         }
0143     }
0144     return -1;
0145 }
0146 
0147 QDomElement KBookmarkGroup::nextKnownTag(const QDomElement &start, bool goNext) const
0148 {
0149     for (QDomElement elem = start; !elem.isNull();) {
0150         QString tag = elem.tagName();
0151         if (tag == QLatin1String("folder") || tag == QLatin1String("bookmark") || tag == QLatin1String("separator")) {
0152             return elem;
0153         }
0154         if (goNext) {
0155             elem = elem.nextSiblingElement();
0156         } else {
0157             elem = elem.previousSiblingElement();
0158         }
0159     }
0160     return QDomElement();
0161 }
0162 
0163 KBookmarkGroup KBookmarkGroup::createNewFolder(const QString &text)
0164 {
0165     if (isNull()) {
0166         return KBookmarkGroup();
0167     }
0168     QDomDocument doc = element.ownerDocument();
0169     QDomElement groupElem = doc.createElement(QStringLiteral("folder"));
0170     element.appendChild(groupElem);
0171     QDomElement textElem = doc.createElement(QStringLiteral("title"));
0172     groupElem.appendChild(textElem);
0173     textElem.appendChild(doc.createTextNode(text));
0174     return KBookmarkGroup(groupElem);
0175 }
0176 
0177 KBookmark KBookmarkGroup::createNewSeparator()
0178 {
0179     if (isNull()) {
0180         return KBookmark();
0181     }
0182     QDomDocument doc = element.ownerDocument();
0183     Q_ASSERT(!doc.isNull());
0184     QDomElement sepElem = doc.createElement(QStringLiteral("separator"));
0185     element.appendChild(sepElem);
0186     return KBookmark(sepElem);
0187 }
0188 
0189 bool KBookmarkGroup::moveBookmark(const KBookmark &item, const KBookmark &after)
0190 {
0191     QDomNode n;
0192     if (!after.isNull()) {
0193         n = element.insertAfter(item.element, after.element);
0194     } else { // first child
0195         if (element.firstChild().isNull()) { // Empty element -> set as real first child
0196             n = element.insertBefore(item.element, QDomElement());
0197         }
0198 
0199         // we have to skip everything up to the first valid child
0200         QDomElement firstChild = nextKnownTag(element.firstChild().toElement(), true);
0201         if (!firstChild.isNull()) {
0202             if (firstChild == item.element) { // item is already the first child, done
0203                 return true;
0204             }
0205             n = element.insertBefore(item.element, firstChild);
0206         } else {
0207             // No real first child -> append after the <title> etc.
0208             n = element.appendChild(item.element);
0209         }
0210     }
0211     return (!n.isNull());
0212 }
0213 
0214 KBookmark KBookmarkGroup::addBookmark(const KBookmark &bm)
0215 {
0216     element.appendChild(bm.internalElement());
0217     return bm;
0218 }
0219 
0220 KBookmark KBookmarkGroup::addBookmark(const QString &text, const QUrl &url, const QString &icon)
0221 {
0222     if (isNull()) {
0223         return KBookmark();
0224     }
0225     QDomDocument doc = element.ownerDocument();
0226     QDomElement elem = doc.createElement(QStringLiteral("bookmark"));
0227     elem.setAttribute(QStringLiteral("href"), url.toString(QUrl::FullyEncoded));
0228 
0229     QDomElement textElem = doc.createElement(QStringLiteral("title"));
0230     elem.appendChild(textElem);
0231     textElem.appendChild(doc.createTextNode(text));
0232 
0233     KBookmark newBookmark = addBookmark(KBookmark(elem));
0234 
0235     // as icons are moved to metadata, we have to use the KBookmark API for this
0236     newBookmark.setIcon(icon);
0237     return newBookmark;
0238 }
0239 
0240 void KBookmarkGroup::deleteBookmark(const KBookmark &bk)
0241 {
0242     element.removeChild(bk.element);
0243 }
0244 
0245 bool KBookmarkGroup::isToolbarGroup() const
0246 {
0247     return (element.attribute(QStringLiteral("toolbar")) == QLatin1String("yes"));
0248 }
0249 
0250 QDomElement KBookmarkGroup::findToolbar() const
0251 {
0252     if (element.attribute(QStringLiteral("toolbar")) == QLatin1String("yes")) {
0253         return element;
0254     }
0255     for (QDomElement e = element.firstChildElement(QStringLiteral("folder")); !e.isNull(); e = e.nextSiblingElement(QStringLiteral("folder"))) {
0256         QDomElement result = KBookmarkGroup(e).findToolbar();
0257         if (!result.isNull()) {
0258             return result;
0259         }
0260     }
0261     return QDomElement();
0262 }
0263 
0264 QList<QUrl> KBookmarkGroup::groupUrlList() const
0265 {
0266     QList<QUrl> urlList;
0267     for (KBookmark bm = first(); !bm.isNull(); bm = next(bm)) {
0268         if (bm.isSeparator() || bm.isGroup()) {
0269             continue;
0270         }
0271         urlList << bm.url();
0272     }
0273     return urlList;
0274 }
0275 
0276 //////
0277 
0278 KBookmark::KBookmark()
0279 {
0280 }
0281 
0282 KBookmark::KBookmark(const QDomElement &elem)
0283     : element(elem)
0284 {
0285 }
0286 
0287 bool KBookmark::isGroup() const
0288 {
0289     QString tag = element.tagName();
0290     return tag == QLatin1String("folder") //
0291         || tag == QLatin1String("xbel"); // don't forget the toplevel group
0292 }
0293 
0294 bool KBookmark::isSeparator() const
0295 {
0296     return (element.tagName() == QLatin1String("separator"));
0297 }
0298 
0299 bool KBookmark::isNull() const
0300 {
0301     return element.isNull();
0302 }
0303 
0304 bool KBookmark::hasParent() const
0305 {
0306     QDomElement parent = element.parentNode().toElement();
0307     return !parent.isNull();
0308 }
0309 
0310 QString KBookmark::text() const
0311 {
0312     return KStringHandler::csqueeze(fullText());
0313 }
0314 
0315 QString KBookmark::fullText() const
0316 {
0317     if (isSeparator()) {
0318         return QCoreApplication::translate("KBookmark", "--- separator ---", "Bookmark separator");
0319     }
0320 
0321     QString text = element.namedItem(QStringLiteral("title")).toElement().text();
0322     text.replace(QLatin1Char('\n'), QLatin1Char(' ')); // #140673
0323     return text;
0324 }
0325 
0326 void KBookmark::setFullText(const QString &fullText)
0327 {
0328     QDomNode titleNode = element.namedItem(QStringLiteral("title"));
0329     if (titleNode.isNull()) {
0330         titleNode = element.ownerDocument().createElement(QStringLiteral("title"));
0331         element.appendChild(titleNode);
0332     }
0333 
0334     if (titleNode.firstChild().isNull()) {
0335         QDomText domtext = titleNode.ownerDocument().createTextNode(QLatin1String(""));
0336         titleNode.appendChild(domtext);
0337     }
0338 
0339     QDomText domtext = titleNode.firstChild().toText();
0340     domtext.setData(fullText);
0341 }
0342 
0343 QUrl KBookmark::url() const
0344 {
0345     return QUrl(element.attribute(QStringLiteral("href")));
0346 }
0347 
0348 void KBookmark::setUrl(const QUrl &url)
0349 {
0350     element.setAttribute(QStringLiteral("href"), url.toString());
0351 }
0352 
0353 QString KBookmark::icon() const
0354 {
0355     QDomNode metaDataNode = metaData(Strings::metaDataFreedesktopOwner(), false);
0356     QDomElement iconElement = cd(metaDataNode, QStringLiteral("bookmark:icon"), false).toElement();
0357 
0358     QString icon = iconElement.attribute(QStringLiteral("name"));
0359 
0360     // migration code
0361     if (icon.isEmpty()) {
0362         icon = element.attribute(QStringLiteral("icon"));
0363     }
0364     if (icon == QLatin1String("www")) { // common icon for kde3 bookmarks
0365         return QStringLiteral("internet-web-browser");
0366     }
0367     // end migration code
0368 
0369     if (icon == QLatin1String("bookmark_folder")) {
0370         return QStringLiteral("folder-bookmarks");
0371     }
0372     if (icon.isEmpty()) {
0373         // Default icon depends on URL for bookmarks, and is default directory
0374         // icon for groups.
0375         if (isGroup()) {
0376             icon = QStringLiteral("folder-bookmarks");
0377         } else {
0378             if (isSeparator()) {
0379                 icon = QStringLiteral("edit-clear"); // whatever
0380             } else {
0381                 // get icon from mimeType
0382                 QMimeDatabase db;
0383                 QMimeType mime;
0384                 QString _mimeType = mimeType();
0385                 if (!_mimeType.isEmpty()) {
0386                     mime = db.mimeTypeForName(_mimeType);
0387                 } else {
0388                     mime = db.mimeTypeForUrl(url());
0389                 }
0390                 if (mime.isValid()) {
0391                     icon = mime.iconName();
0392                 }
0393             }
0394         }
0395     }
0396     return icon;
0397 }
0398 
0399 void KBookmark::setIcon(const QString &icon)
0400 {
0401     QDomNode metaDataNode = metaData(Strings::metaDataFreedesktopOwner(), true);
0402     QDomElement iconElement = cd_or_create(metaDataNode, QStringLiteral("bookmark:icon")).toElement();
0403     iconElement.setAttribute(QStringLiteral("name"), icon);
0404 
0405     // migration code
0406     if (!element.attribute(QStringLiteral("icon")).isEmpty()) {
0407         element.removeAttribute(QStringLiteral("icon"));
0408     }
0409 }
0410 
0411 QString KBookmark::description() const
0412 {
0413     if (isSeparator()) {
0414         return QString();
0415     }
0416 
0417     QString description = element.namedItem(QStringLiteral("desc")).toElement().text();
0418     description.replace(QLatin1Char('\n'), QLatin1Char(' ')); // #140673
0419     return description;
0420 }
0421 
0422 void KBookmark::setDescription(const QString &description)
0423 {
0424     QDomNode descNode = element.namedItem(QStringLiteral("desc"));
0425     if (descNode.isNull()) {
0426         descNode = element.ownerDocument().createElement(QStringLiteral("desc"));
0427         element.appendChild(descNode);
0428     }
0429 
0430     if (descNode.firstChild().isNull()) {
0431         QDomText domtext = descNode.ownerDocument().createTextNode(QString());
0432         descNode.appendChild(domtext);
0433     }
0434 
0435     QDomText domtext = descNode.firstChild().toText();
0436     domtext.setData(description);
0437 }
0438 
0439 QString KBookmark::mimeType() const
0440 {
0441     QDomNode metaDataNode = metaData(Strings::metaDataMimeOwner(), false);
0442     QDomElement mimeTypeElement = cd(metaDataNode, QStringLiteral("mime:mime-type"), false).toElement();
0443     return mimeTypeElement.attribute(QStringLiteral("type"));
0444 }
0445 
0446 void KBookmark::setMimeType(const QString &mimeType)
0447 {
0448     QDomNode metaDataNode = metaData(Strings::metaDataMimeOwner(), true);
0449     QDomElement iconElement = cd_or_create(metaDataNode, QStringLiteral("mime:mime-type")).toElement();
0450     iconElement.setAttribute(QStringLiteral("type"), mimeType);
0451 }
0452 
0453 bool KBookmark::showInToolbar() const
0454 {
0455     if (element.hasAttribute(QStringLiteral("showintoolbar"))) {
0456         bool show = element.attribute(QStringLiteral("showintoolbar")) == QLatin1String("yes");
0457         const_cast<QDomElement *>(&element)->removeAttribute(QStringLiteral("showintoolbar"));
0458         const_cast<KBookmark *>(this)->setShowInToolbar(show);
0459     }
0460     return metaDataItem(QStringLiteral("showintoolbar")) == QLatin1String("yes");
0461 }
0462 
0463 void KBookmark::setShowInToolbar(bool show)
0464 {
0465     setMetaDataItem(QStringLiteral("showintoolbar"), show ? QStringLiteral("yes") : QStringLiteral("no"));
0466 }
0467 
0468 KBookmarkGroup KBookmark::parentGroup() const
0469 {
0470     return KBookmarkGroup(element.parentNode().toElement());
0471 }
0472 
0473 KBookmarkGroup KBookmark::toGroup() const
0474 {
0475     Q_ASSERT(isGroup());
0476     return KBookmarkGroup(element);
0477 }
0478 
0479 QString KBookmark::address() const
0480 {
0481     if (element.tagName() == QLatin1String("xbel")) {
0482         return QLatin1String(""); // not QString() !
0483     } else {
0484         // Use keditbookmarks's DEBUG_ADDRESSES flag to debug this code :)
0485         if (element.parentNode().isNull()) {
0486             Q_ASSERT(false);
0487             return QStringLiteral("ERROR"); // Avoid an infinite loop
0488         }
0489         KBookmarkGroup group = parentGroup();
0490         QString parentAddress = group.address();
0491         int pos = group.indexOf(*this);
0492         Q_ASSERT(pos != -1);
0493         return parentAddress + QLatin1Char('/') + QString::number(pos);
0494     }
0495 }
0496 
0497 int KBookmark::positionInParent() const
0498 {
0499     return parentGroup().indexOf(*this);
0500 }
0501 
0502 QDomElement KBookmark::internalElement() const
0503 {
0504     return element;
0505 }
0506 
0507 KBookmark KBookmark::standaloneBookmark(const QString &text, const QUrl &url, const QString &icon)
0508 {
0509     QDomDocument doc(QStringLiteral("xbel"));
0510     QDomElement elem = doc.createElement(QStringLiteral("xbel"));
0511     doc.appendChild(elem);
0512     KBookmarkGroup grp(elem);
0513     grp.addBookmark(text, url, icon);
0514     return grp.first();
0515 }
0516 
0517 QString KBookmark::commonParent(const QString &first, const QString &second)
0518 {
0519     QString A = first;
0520     QString B = second;
0521     QString error(QStringLiteral("ERROR"));
0522     if (A == error || B == error) {
0523         return error;
0524     }
0525 
0526     A += QLatin1Char('/');
0527     B += QLatin1Char('/');
0528 
0529     int lastCommonSlash = 0;
0530     int lastPos = A.length() < B.length() ? A.length() : B.length();
0531     for (int i = 0; i < lastPos; ++i) {
0532         if (A[i] != B[i]) {
0533             return A.left(lastCommonSlash);
0534         }
0535         if (A[i] == QLatin1Char('/')) {
0536             lastCommonSlash = i;
0537         }
0538     }
0539     return A.left(lastCommonSlash);
0540 }
0541 
0542 void KBookmark::updateAccessMetadata()
0543 {
0544     // qCDebug(KBOOKMARKS_LOG) << "KBookmark::updateAccessMetadata " << address() << " " << url();
0545 
0546     const uint timet = QDateTime::currentDateTimeUtc().toSecsSinceEpoch();
0547     setMetaDataItem(QStringLiteral("time_added"), QString::number(timet), DontOverwriteMetaData);
0548     setMetaDataItem(QStringLiteral("time_visited"), QString::number(timet));
0549 
0550     QString countStr = metaDataItem(QStringLiteral("visit_count")); // TODO use spec'ed name
0551     bool ok;
0552     int currentCount = countStr.toInt(&ok);
0553     if (!ok) {
0554         currentCount = 0;
0555     }
0556     currentCount++;
0557     setMetaDataItem(QStringLiteral("visit_count"), QString::number(currentCount));
0558 
0559     // TODO - time_modified
0560 }
0561 
0562 QString KBookmark::parentAddress(const QString &address)
0563 {
0564     return address.left(address.lastIndexOf(QLatin1Char('/')));
0565 }
0566 
0567 uint KBookmark::positionInParent(const QString &address)
0568 {
0569 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0570     return QStringView(address).mid(address.lastIndexOf(QLatin1Char('/')) + 1).toInt();
0571 #else
0572     return address.midRef(address.lastIndexOf(QLatin1Char('/')) + 1).toInt();
0573 #endif
0574 }
0575 
0576 QString KBookmark::previousAddress(const QString &address)
0577 {
0578     uint pp = positionInParent(address);
0579     return pp > 0 ? parentAddress(address) + QLatin1Char('/') + QString::number(pp - 1) : QString();
0580 }
0581 
0582 QString KBookmark::nextAddress(const QString &address)
0583 {
0584     return parentAddress(address) + QLatin1Char('/') + QString::number(positionInParent(address) + 1);
0585 }
0586 
0587 QDomNode KBookmark::metaData(const QString &owner, bool create) const
0588 {
0589     QDomNode infoNode = cd(internalElement(), QStringLiteral("info"), create);
0590     if (infoNode.isNull()) {
0591         return QDomNode();
0592     }
0593     return findMetadata(owner, infoNode, create);
0594 }
0595 
0596 QString KBookmark::metaDataItem(const QString &key) const
0597 {
0598     QDomNode metaDataNode = metaData(Strings::metaDataKDEOwner(), false);
0599     for (QDomElement e = metaDataNode.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
0600         if (e.tagName() == key) {
0601             return e.text();
0602         }
0603     }
0604     return QString();
0605 }
0606 
0607 void KBookmark::setMetaDataItem(const QString &key, const QString &value, MetaDataOverwriteMode mode)
0608 {
0609     QDomNode metaDataNode = metaData(Strings::metaDataKDEOwner(), true);
0610     QDomNode item = cd_or_create(metaDataNode, key);
0611     QDomText text = get_or_create_text(item);
0612     if (mode == DontOverwriteMetaData && !text.data().isEmpty()) {
0613         return;
0614     }
0615 
0616     text.setData(value);
0617 }
0618 
0619 bool KBookmark::operator==(const KBookmark &rhs) const
0620 {
0621     return element == rhs.element;
0622 }
0623 
0624 ////
0625 
0626 KBookmarkGroupTraverser::~KBookmarkGroupTraverser()
0627 {
0628 }
0629 
0630 void KBookmarkGroupTraverser::traverse(const KBookmarkGroup &root)
0631 {
0632     QStack<KBookmarkGroup> stack;
0633     stack.push(root);
0634     KBookmark bk = root.first();
0635     for (;;) {
0636         if (bk.isNull()) {
0637             if (stack.count() == 1) { // only root is on the stack
0638                 return;
0639             }
0640             if (!stack.isEmpty()) {
0641                 visitLeave(stack.top());
0642                 bk = stack.pop();
0643             }
0644             bk = stack.top().next(bk);
0645         } else if (bk.isGroup()) {
0646             KBookmarkGroup gp = bk.toGroup();
0647             visitEnter(gp);
0648             bk = gp.first();
0649             stack.push(gp);
0650         } else {
0651             visit(bk);
0652             bk = stack.top().next(bk);
0653         }
0654     }
0655 }
0656 
0657 void KBookmarkGroupTraverser::visit(const KBookmark &)
0658 {
0659 }
0660 
0661 void KBookmarkGroupTraverser::visitEnter(const KBookmarkGroup &)
0662 {
0663 }
0664 
0665 void KBookmarkGroupTraverser::visitLeave(const KBookmarkGroup &)
0666 {
0667 }
0668 
0669 void KBookmark::populateMimeData(QMimeData *mimeData) const
0670 {
0671     KBookmark::List bookmarkList;
0672     bookmarkList.append(*this);
0673     bookmarkList.populateMimeData(mimeData);
0674 }
0675 
0676 KBookmark::List::List()
0677     : QList<KBookmark>()
0678 {
0679 }
0680 
0681 void KBookmark::List::populateMimeData(QMimeData *mimeData) const
0682 {
0683     QList<QUrl> urls;
0684 
0685     QDomDocument doc(QStringLiteral("xbel"));
0686     QDomElement elem = doc.createElement(QStringLiteral("xbel"));
0687     doc.appendChild(elem);
0688 
0689     for (const_iterator it = begin(), end = this->end(); it != end; ++it) {
0690         urls.append((*it).url());
0691         elem.appendChild((*it).internalElement().cloneNode(true /* deep */));
0692     }
0693 
0694     // This sets text/uri-list and text/plain into the mimedata
0695     mimeData->setUrls(urls);
0696 
0697     mimeData->setData(Strings::xbelMimeType(), doc.toByteArray());
0698 }
0699 
0700 bool KBookmark::List::canDecode(const QMimeData *mimeData)
0701 {
0702     return mimeData->hasFormat(Strings::xbelMimeType()) || mimeData->hasUrls();
0703 }
0704 
0705 QStringList KBookmark::List::mimeDataTypes()
0706 {
0707     return QStringList() << Strings::xbelMimeType() << KUrlMimeData::mimeDataTypes();
0708 }
0709 
0710 KBookmark::List KBookmark::List::fromMimeData(const QMimeData *mimeData, QDomDocument &doc)
0711 {
0712     KBookmark::List bookmarks;
0713     const QByteArray payload = mimeData->data(Strings::xbelMimeType());
0714     if (!payload.isEmpty()) {
0715         doc.setContent(payload);
0716         QDomElement elem = doc.documentElement();
0717         const QDomNodeList children = elem.childNodes();
0718         bookmarks.reserve(children.count());
0719         for (int childno = 0; childno < children.count(); childno++) {
0720             bookmarks.append(KBookmark(children.item(childno).toElement()));
0721         }
0722         return bookmarks;
0723     }
0724     const QList<QUrl> urls = KUrlMimeData::urlsFromMimeData(mimeData);
0725     bookmarks.reserve(urls.size());
0726     for (int i = 0; i < urls.size(); ++i) {
0727         const QUrl url = urls.at(i);
0728         bookmarks.append(KBookmark::standaloneBookmark(url.toDisplayString(), url, QString() /*TODO icon*/));
0729     }
0730     return bookmarks;
0731 }