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