00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "kbookmark.h"
00024 #include <QStack>
00025 #include <kdebug.h>
00026 #include <kmimetype.h>
00027 #include <kstringhandler.h>
00028 #include <kglobal.h>
00029 #include <klocale.h>
00030 #include <assert.h>
00031 #include <kbookmarkmanager.h>
00032
00033 #include <qdatetime.h>
00034 #include <qmimedata.h>
00035
00036 #define METADATA_KDE_OWNER "http://www.kde.org"
00037 #define METADATA_FREEDESKTOP_OWNER "http://freedesktop.org"
00038 #define METADATA_MIME_OWNER "http://www.freedesktop.org/standards/shared-mime-info"
00039
00041
00042 static QDomNode cd(QDomNode node, const QString &name, bool create)
00043 {
00044 QDomNode subnode = node.namedItem(name);
00045 if (create && subnode.isNull())
00046 {
00047 subnode = node.ownerDocument().createElement(name);
00048 node.appendChild(subnode);
00049 }
00050 return subnode;
00051 }
00052
00053 static QDomNode cd_or_create(QDomNode node, const QString &name)
00054 {
00055 return cd(node, name, true);
00056 }
00057
00058 static QDomText get_or_create_text(QDomNode node)
00059 {
00060 QDomNode subnode = node.firstChild();
00061 if (subnode.isNull())
00062 {
00063 subnode = node.ownerDocument().createTextNode("");
00064 node.appendChild(subnode);
00065 }
00066 return subnode.toText();
00067 }
00068
00069 static QDomNode findMetadata(const QString & forOwner, QDomNode& parent, bool create)
00070 {
00071 bool forOwnerIsKDE = forOwner == METADATA_KDE_OWNER;
00072
00073 QDomElement metadataElement;
00074 for ( QDomNode _node = parent.firstChild(); !_node.isNull(); _node = _node.nextSibling() ) {
00075 QDomElement elem = _node.toElement();
00076 if ( !elem.isNull() && elem.tagName() == "metadata" ) {
00077 const QString owner = elem.attribute( "owner" );
00078 if ( owner == forOwner )
00079 return elem;
00080 if ( owner.isEmpty() && forOwnerIsKDE )
00081 metadataElement = elem;
00082 }
00083 }
00084 if ( create && metadataElement.isNull() ) {
00085 metadataElement = parent.ownerDocument().createElement( "metadata" );
00086 parent.appendChild(metadataElement);
00087 metadataElement.setAttribute( "owner", forOwner );
00088
00089 } else if (!metadataElement.isNull() && forOwnerIsKDE) {
00090
00091 metadataElement.setAttribute( "owner", METADATA_KDE_OWNER );
00092 }
00093 return metadataElement;
00094 }
00095
00097
00098 KBookmarkGroup::KBookmarkGroup()
00099 : KBookmark( QDomElement() )
00100 {
00101 }
00102
00103 KBookmarkGroup::KBookmarkGroup( const QDomElement &elem )
00104 : KBookmark(elem)
00105 {
00106 }
00107
00108 bool KBookmarkGroup::isOpen() const
00109 {
00110 return element.attribute("folded") == "no";
00111 }
00112
00113 KBookmark KBookmarkGroup::first() const
00114 {
00115 return KBookmark( nextKnownTag( element.firstChildElement(), true ) );
00116 }
00117
00118 KBookmark KBookmarkGroup::previous( const KBookmark & current ) const
00119 {
00120 return KBookmark( nextKnownTag( current.element.previousSiblingElement(), false ) );
00121 }
00122
00123 KBookmark KBookmarkGroup::next( const KBookmark & current ) const
00124 {
00125 return KBookmark( nextKnownTag( current.element.nextSiblingElement(), true ) );
00126 }
00127
00128 int KBookmarkGroup::indexOf(const KBookmark& child) const
00129 {
00130 uint counter = 0;
00131 for ( KBookmark bk = first(); !bk.isNull(); bk = next(bk), ++counter ) {
00132 if ( bk.element == child.element )
00133 return counter;
00134 }
00135 return -1;
00136 }
00137
00138 QDomElement KBookmarkGroup::nextKnownTag( const QDomElement &start, bool goNext ) const
00139 {
00140 static const QString & bookmark = KGlobal::staticQString("bookmark");
00141 static const QString & folder = KGlobal::staticQString("folder");
00142 static const QString & separator = KGlobal::staticQString("separator");
00143
00144 for( QDomElement elem = start; !elem.isNull(); )
00145 {
00146 QString tag = elem.tagName();
00147 if (tag == folder || tag == bookmark || tag == separator)
00148 return elem;
00149 if (goNext)
00150 elem = elem.nextSiblingElement();
00151 else
00152 elem = elem.previousSiblingElement();
00153 }
00154 return QDomElement();
00155 }
00156
00157 KBookmarkGroup KBookmarkGroup::createNewFolder( const QString & text )
00158 {
00159 if (isNull())
00160 return KBookmarkGroup();
00161 QDomDocument doc = element.ownerDocument();
00162 QDomElement groupElem = doc.createElement( "folder" );
00163 element.appendChild( groupElem );
00164 QDomElement textElem = doc.createElement( "title" );
00165 groupElem.appendChild( textElem );
00166 textElem.appendChild( doc.createTextNode( text ) );
00167 return KBookmarkGroup(groupElem);
00168
00169 }
00170
00171 KBookmark KBookmarkGroup::createNewSeparator()
00172 {
00173 if (isNull())
00174 return KBookmark();
00175 QDomDocument doc = element.ownerDocument();
00176 Q_ASSERT(!doc.isNull());
00177 QDomElement sepElem = doc.createElement( "separator" );
00178 element.appendChild( sepElem );
00179 return KBookmark(sepElem);
00180 }
00181
00182 bool KBookmarkGroup::moveItem( const KBookmark & bookmark, const KBookmark & after )
00183 {
00184 return moveBookmark(bookmark, after);
00185 }
00186
00187 bool KBookmarkGroup::moveBookmark( const KBookmark & item, const KBookmark & after )
00188 {
00189 QDomNode n;
00190 if ( !after.isNull() )
00191 n = element.insertAfter( item.element, after.element );
00192 else
00193 {
00194 if ( element.firstChild().isNull() )
00195 n = element.insertBefore( item.element, QDomElement() );
00196
00197
00198 QDomElement firstChild = nextKnownTag(element.firstChild().toElement(), true);
00199 if ( !firstChild.isNull() )
00200 n = element.insertBefore( item.element, firstChild );
00201 else
00202 {
00203
00204 n = element.appendChild( item.element );
00205 }
00206 }
00207 return (!n.isNull());
00208 }
00209
00210 KBookmark KBookmarkGroup::addBookmark( const KBookmark &bm )
00211 {
00212 element.appendChild( bm.internalElement() );
00213 return bm;
00214 }
00215
00216 KBookmark KBookmarkGroup::addBookmark( const QString & text, const KUrl & url, const QString & icon )
00217 {
00218 if (isNull())
00219 return KBookmark();
00220 QDomDocument doc = element.ownerDocument();
00221 QDomElement elem = doc.createElement( "bookmark" );
00222 elem.setAttribute( "href", url.url() );
00223
00224 QDomElement textElem = doc.createElement( "title" );
00225 elem.appendChild( textElem );
00226 textElem.appendChild( doc.createTextNode( text ) );
00227
00228 KBookmark newBookmark = addBookmark( KBookmark( elem ) );
00229
00230
00231 newBookmark.setIcon(icon.isEmpty() ? KMimeType::iconNameForUrl( url ) : icon );
00232 return newBookmark;
00233 }
00234
00235 void KBookmarkGroup::deleteBookmark( const KBookmark &bk )
00236 {
00237 element.removeChild( bk.element );
00238 }
00239
00240 bool KBookmarkGroup::isToolbarGroup() const
00241 {
00242 return ( element.attribute("toolbar") == "yes" );
00243 }
00244
00245 QDomElement KBookmarkGroup::findToolbar() const
00246 {
00247 if ( element.attribute("toolbar") == "yes" )
00248 return element;
00249 for (QDomNode n = element.firstChild(); !n.isNull() ; n = n.nextSibling() )
00250 {
00251 QDomElement e = n.toElement();
00252
00253 if ( e.tagName() == "folder" )
00254 {
00255 if ( e.attribute("toolbar") == "yes" )
00256 return e;
00257 else
00258 {
00259 QDomElement result = KBookmarkGroup(e).findToolbar();
00260 if (!result.isNull())
00261 return result;
00262 }
00263 }
00264 }
00265 return QDomElement();
00266 }
00267
00268 QList<KUrl> KBookmarkGroup::groupUrlList() const
00269 {
00270 QList<KUrl> urlList;
00271 for ( KBookmark bm = first(); !bm.isNull(); bm = next(bm) )
00272 {
00273 if ( bm.isSeparator() || bm.isGroup() )
00274 continue;
00275 urlList << bm.url();
00276 }
00277 return urlList;
00278 }
00279
00281
00282 KBookmark::KBookmark()
00283 {
00284 }
00285
00286 KBookmark::KBookmark( const QDomElement &elem ) : element(elem)
00287 {
00288 }
00289
00290 bool KBookmark::isGroup() const
00291 {
00292 QString tag = element.tagName();
00293 return ( tag == "folder"
00294 || tag == "xbel" );
00295 }
00296
00297 bool KBookmark::isSeparator() const
00298 {
00299 return (element.tagName() == "separator");
00300 }
00301
00302 bool KBookmark::isNull() const
00303 {
00304 return element.isNull();
00305 }
00306
00307 bool KBookmark::hasParent() const
00308 {
00309 QDomElement parent = element.parentNode().toElement();
00310 return !parent.isNull();
00311 }
00312
00313 QString KBookmark::text() const
00314 {
00315 return KStringHandler::csqueeze( fullText() );
00316 }
00317
00318 QString KBookmark::fullText() const
00319 {
00320 if (isSeparator())
00321 return i18n("--- separator ---");
00322
00323 return element.namedItem("title").toElement().text();
00324 }
00325
00326 void KBookmark::setFullText(const QString &fullText)
00327 {
00328 QDomNode titleNode = element.namedItem("title");
00329 if (titleNode.isNull()) {
00330 titleNode = element.ownerDocument().createElement("title");
00331 element.appendChild(titleNode);
00332 }
00333
00334 if (titleNode.firstChild().isNull()) {
00335 QDomText domtext = titleNode.ownerDocument().createTextNode("");
00336 titleNode.appendChild(domtext);
00337 }
00338
00339 QDomText domtext = titleNode.firstChild().toText();
00340 domtext.setData(fullText);
00341 }
00342
00343 KUrl KBookmark::url() const
00344 {
00345 return KUrl(element.attribute("href").toAscii());
00346 }
00347
00348 void KBookmark::setUrl(const KUrl &url)
00349 {
00350 element.setAttribute("href", url.url());
00351 }
00352
00353 QString KBookmark::icon() const
00354 {
00355 QDomNode metaDataNode = metaData(METADATA_FREEDESKTOP_OWNER, false);
00356 QDomElement iconElement = cd(metaDataNode, "bookmark:icon", false).toElement();
00357
00358 QString icon = iconElement.attribute("name");
00359
00360
00361 if (icon.isEmpty())
00362 icon = element.attribute("icon");
00363
00364
00365 if (icon == "bookmark_folder") {
00366 return "folder-bookmarks";
00367 }
00368 if (icon.isEmpty()) {
00369
00370
00371 if (isGroup()) {
00372 icon = "folder-bookmarks";
00373 }
00374 else {
00375 if (isSeparator()) {
00376 icon = "edit-clear";
00377 } else {
00378
00379 QString _mimeType = mimeType();
00380 if (!_mimeType.isEmpty()) {
00381 KMimeType::Ptr mime = KMimeType::mimeType(_mimeType, KMimeType::ResolveAliases);
00382 if (mime) {
00383 return mime->iconName();
00384 }
00385 }
00386
00387 icon = KMimeType::iconNameForUrl(url());
00388 }
00389 }
00390 }
00391 return icon;
00392 }
00393
00394 void KBookmark::setIcon(const QString &icon)
00395 {
00396 QDomNode metaDataNode = metaData(METADATA_FREEDESKTOP_OWNER, true);
00397 QDomElement iconElement = cd_or_create(metaDataNode, "bookmark:icon").toElement();
00398 iconElement.setAttribute ( "name", icon );
00399
00400
00401 if(!element.attribute("icon").isEmpty())
00402 element.removeAttribute("icon");
00403 }
00404
00405 QString KBookmark::mimeType() const
00406 {
00407 QDomNode metaDataNode = metaData(METADATA_MIME_OWNER, false);
00408 QDomElement mimeTypeElement = cd(metaDataNode, "mime:mime-type", false).toElement();
00409 return mimeTypeElement.attribute("type");
00410 }
00411
00412 void KBookmark::setMimeType(const QString &mimeType)
00413 {
00414 QDomNode metaDataNode = metaData(METADATA_MIME_OWNER, true);
00415 QDomElement iconElement = cd_or_create(metaDataNode, "mime:mime-type").toElement();
00416 iconElement.setAttribute ( "type", mimeType );
00417 }
00418
00419 bool KBookmark::showInToolbar() const
00420 {
00421 if(element.hasAttribute("showintoolbar"))
00422 {
00423 bool show = element.attribute("showintoolbar") == "yes";
00424 const_cast<QDomElement *>(&element)->removeAttribute("showintoolbar");
00425 const_cast<KBookmark *>(this)->setShowInToolbar(show);
00426 }
00427 return metaDataItem("showintoolbar") == "yes";
00428 }
00429
00430
00431 void KBookmark::setShowInToolbar(bool show)
00432 {
00433 setMetaDataItem("showintoolbar", show ? "yes" : "no");
00434 }
00435
00436 KBookmarkGroup KBookmark::parentGroup() const
00437 {
00438 return KBookmarkGroup( element.parentNode().toElement() );
00439 }
00440
00441 KBookmarkGroup KBookmark::toGroup() const
00442 {
00443 Q_ASSERT( isGroup() );
00444 return KBookmarkGroup(element);
00445 }
00446
00447 QString KBookmark::address() const
00448 {
00449 if ( element.tagName() == "xbel" )
00450 return "";
00451 else
00452 {
00453
00454 if (element.parentNode().isNull())
00455 {
00456 Q_ASSERT(false);
00457 return "ERROR";
00458 }
00459 KBookmarkGroup group = parentGroup();
00460 QString parentAddress = group.address();
00461 int pos = group.indexOf(*this);
00462 Q_ASSERT(pos != -1);
00463 return parentAddress + '/' + QString::number(pos);
00464 }
00465 }
00466
00467 int KBookmark::positionInParent() const
00468 {
00469 return parentGroup().indexOf(*this);
00470 }
00471
00472 QDomElement KBookmark::internalElement() const
00473 {
00474 return element;
00475 }
00476
00477 KBookmark KBookmark::standaloneBookmark( const QString & text, const KUrl & url, const QString & icon )
00478 {
00479 QDomDocument doc("xbel");
00480 QDomElement elem = doc.createElement("xbel");
00481 doc.appendChild( elem );
00482 KBookmarkGroup grp( elem );
00483 grp.addBookmark( text, url, icon );
00484 return grp.first();
00485 }
00486
00487
00488 QString KBookmark::commonParent(const QString &first, const QString &second)
00489 {
00490 QString A = first;
00491 QString B = second;
00492 QString error("ERROR");
00493 if(A == error || B == error)
00494 return error;
00495
00496 A += '/';
00497 B += '/';
00498
00499 uint lastCommonSlash = 0;
00500 uint lastPos = A.length() < B.length() ? A.length() : B.length();
00501 for(uint i=0; i < lastPos; ++i)
00502 {
00503 if(A[i] != B[i])
00504 return A.left(lastCommonSlash);
00505 if(A[i] == '/')
00506 lastCommonSlash = i;
00507 }
00508 return A.left(lastCommonSlash);
00509 }
00510
00511 void KBookmark::updateAccessMetadata()
00512 {
00513 kDebug(7043) << "KBookmark::updateAccessMetadata " << address() << " " << url().prettyUrl();
00514
00515 const uint timet = QDateTime::currentDateTime().toTime_t();
00516 setMetaDataItem( "time_added", QString::number( timet ), DontOverwriteMetaData );
00517 setMetaDataItem( "time_visited", QString::number( timet ) );
00518
00519 QString countStr = metaDataItem( "visit_count" );
00520 bool ok;
00521 int currentCount = countStr.toInt(&ok);
00522 if (!ok)
00523 currentCount = 0;
00524 currentCount++;
00525 setMetaDataItem( "visit_count", QString::number( currentCount ) );
00526
00527
00528 }
00529
00530 QString KBookmark::parentAddress( const QString & address )
00531 {
00532 return address.left( address.lastIndexOf(QLatin1Char('/')) );
00533 }
00534
00535 uint KBookmark::positionInParent( const QString & address )
00536 {
00537 return address.mid( address.lastIndexOf(QLatin1Char('/')) + 1 ).toInt();
00538 }
00539
00540 QString KBookmark::previousAddress( const QString & address )
00541 {
00542 uint pp = positionInParent(address);
00543 return pp>0
00544 ? parentAddress(address) + QLatin1Char('/') + QString::number(pp-1)
00545 : QString();
00546 }
00547
00548 QString KBookmark::nextAddress( const QString & address )
00549 {
00550 return parentAddress(address) + QLatin1Char('/') +
00551 QString::number(positionInParent(address)+1);
00552 }
00553
00554 QDomNode KBookmark::metaData(const QString &owner, bool create) const
00555 {
00556 QDomNode infoNode = cd( internalElement(), "info", create);
00557 if (infoNode.isNull()) return QDomNode();
00558 return findMetadata(owner, infoNode , create);
00559 }
00560
00561 QString KBookmark::metaDataItem( const QString &key ) const
00562 {
00563 QDomNode metaDataNode = metaData(METADATA_KDE_OWNER, false);
00564 for ( QDomElement e = metaDataNode.firstChildElement(); !e.isNull(); e = e.nextSiblingElement() )
00565 {
00566 if ( e.tagName() == key ) {
00567 return e.text();
00568 }
00569 }
00570 return QString();
00571 }
00572
00573 void KBookmark::setMetaDataItem( const QString &key, const QString &value, MetaDataOverwriteMode mode )
00574 {
00575 QDomNode metaDataNode = metaData(METADATA_KDE_OWNER, true);
00576 QDomNode item = cd_or_create( metaDataNode, key );
00577 QDomText text = get_or_create_text( item );
00578 if ( mode == DontOverwriteMetaData && !text.data().isEmpty() ) {
00579 return;
00580 }
00581
00582 text.setData( value );
00583 }
00584
00585
00586 bool KBookmark::operator==(const KBookmark& rhs) const
00587 {
00588 return element == rhs.element;
00589 }
00590
00592
00593 KBookmarkGroupTraverser::~KBookmarkGroupTraverser()
00594 {
00595 }
00596
00597 void KBookmarkGroupTraverser::traverse(const KBookmarkGroup &root)
00598 {
00599 QStack<KBookmarkGroup> stack;
00600 stack.push(root);
00601 KBookmark bk = root.first();
00602 for(;;) {
00603 if(bk.isNull()) {
00604 if(stack.count() == 1)
00605 return;
00606 if(stack.count() > 0) {
00607 visitLeave(stack.top());
00608 bk = stack.pop();
00609 }
00610 bk = stack.top().next(bk);
00611 } else if(bk.isGroup()) {
00612 KBookmarkGroup gp = bk.toGroup();
00613 visitEnter(gp);
00614 bk = gp.first();
00615 stack.push(gp);
00616 } else {
00617 visit(bk);
00618 bk = stack.top().next(bk);
00619 }
00620 }
00621 }
00622
00623 void KBookmarkGroupTraverser::visit(const KBookmark &)
00624 {
00625 }
00626
00627 void KBookmarkGroupTraverser::visitEnter(const KBookmarkGroup &)
00628 {
00629 }
00630
00631 void KBookmarkGroupTraverser::visitLeave(const KBookmarkGroup &)
00632 {
00633 }
00634
00635 void KBookmark::populateMimeData( QMimeData* mimeData ) const
00636 {
00637 KBookmark::List bookmarkList;
00638 bookmarkList.append( *this );
00639 bookmarkList.populateMimeData( mimeData );
00640 }
00641
00642 KBookmark::List::List() : QList<KBookmark>()
00643 {
00644 }
00645
00646 void KBookmark::List::populateMimeData( QMimeData* mimeData ) const
00647 {
00648 KUrl::List urls;
00649
00650 QDomDocument doc( "xbel" );
00651 QDomElement elem = doc.createElement( "xbel" );
00652 doc.appendChild( elem );
00653
00654 for ( const_iterator it = begin(), end = this->end() ; it != end ; ++it ) {
00655 urls.append( (*it).url() );
00656 elem.appendChild( (*it).internalElement().cloneNode( true ) );
00657 }
00658
00659
00660 urls.populateMimeData( mimeData, KUrl::MetaDataMap() );
00661
00662 mimeData->setData( "application/x-xbel", doc.toByteArray() );
00663 }
00664
00665 bool KBookmark::List::canDecode( const QMimeData *mimeData )
00666 {
00667 return mimeData->hasFormat( "application/x-xbel" ) || KUrl::List::canDecode(mimeData);
00668 }
00669
00670 QStringList KBookmark::List::mimeDataTypes()
00671 {
00672 return QStringList()<<("application/x-xbel")<<KUrl::List::mimeDataTypes();
00673 }
00674
00675 KBookmark::List KBookmark::List::fromMimeData( const QMimeData *mimeData )
00676 {
00677 QDomDocument doc;
00678 kWarning(7043) << "Deprecated method called, with wrong lifetime of QDomDocument, will probably crash";
00679 return fromMimeData(mimeData, doc);
00680 }
00681
00682 KBookmark::List KBookmark::List::fromMimeData( const QMimeData *mimeData, QDomDocument& doc )
00683 {
00684 KBookmark::List bookmarks;
00685 QByteArray payload = mimeData->data( "application/x-xbel" );
00686 if ( !payload.isEmpty() ) {
00687 doc.setContent( payload );
00688 QDomElement elem = doc.documentElement();
00689 QDomNodeList children = elem.childNodes();
00690 for ( int childno = 0; childno < children.count(); childno++)
00691 {
00692 bookmarks.append( KBookmark( children.item(childno).toElement() ));
00693 }
00694 return bookmarks;
00695 }
00696 const KUrl::List urls = KUrl::List::fromMimeData( mimeData );
00697 if ( !urls.isEmpty() )
00698 {
00699 KUrl::List::ConstIterator uit = urls.begin();
00700 KUrl::List::ConstIterator uEnd = urls.end();
00701 for ( ; uit != uEnd ; ++uit )
00702 {
00703
00704 bookmarks.append( KBookmark::standaloneBookmark(
00705 (*uit).prettyUrl(), (*uit) ));
00706 }
00707 }
00708 return bookmarks;
00709 }
00710