summaryrefslogtreecommitdiffstats
path: root/kio/bookmarks/kbookmark.cc
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitce4a32fe52ef09d8f5ff1dd22c001110902b60a2 (patch)
tree5ac38a06f3dde268dc7927dc155896926aaf7012 /kio/bookmarks/kbookmark.cc
downloadtdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.tar.gz
tdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdelibs@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kio/bookmarks/kbookmark.cc')
-rw-r--r--kio/bookmarks/kbookmark.cc535
1 files changed, 535 insertions, 0 deletions
diff --git a/kio/bookmarks/kbookmark.cc b/kio/bookmarks/kbookmark.cc
new file mode 100644
index 000000000..59cf10db3
--- /dev/null
+++ b/kio/bookmarks/kbookmark.cc
@@ -0,0 +1,535 @@
+// -*- c-basic-offset:4; indent-tabs-mode:nil -*-
+// vim: set ts=4 sts=4 sw=4 et:
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Faure <[email protected]>
+ Copyright (C) 2003 Alexander Kellett <[email protected]>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kbookmark.h"
+#include <qvaluestack.h>
+#include <kdebug.h>
+#include <kmimetype.h>
+#include <kstringhandler.h>
+#include <kinputdialog.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <assert.h>
+#include <kapplication.h>
+#include <dcopclient.h>
+#include <kbookmarkmanager.h>
+
+KBookmarkGroup::KBookmarkGroup()
+ : KBookmark( QDomElement() )
+{
+}
+
+KBookmarkGroup::KBookmarkGroup( QDomElement elem )
+ : KBookmark(elem)
+{
+}
+
+QString KBookmarkGroup::groupAddress() const
+{
+ if (m_address.isEmpty())
+ m_address = address();
+ return m_address;
+}
+
+bool KBookmarkGroup::isOpen() const
+{
+ return element.attribute("folded") == "no"; // default is: folded
+}
+
+// Returns first element node equal to or after node n
+static QDomElement firstElement(QDomNode n)
+{
+ while(!n.isNull() && !n.isElement())
+ n = n.nextSibling();
+ return n.toElement();
+}
+
+// Returns first element node equal to or before node n
+static QDomElement lastElement(QDomNode n)
+{
+ while(!n.isNull() && !n.isElement())
+ n = n.previousSibling();
+ return n.toElement();
+}
+
+KBookmark KBookmarkGroup::first() const
+{
+ return KBookmark( nextKnownTag( firstElement(element.firstChild()), true ) );
+}
+
+KBookmark KBookmarkGroup::previous( const KBookmark & current ) const
+{
+ return KBookmark( nextKnownTag( lastElement(current.element.previousSibling()), false ) );
+}
+
+KBookmark KBookmarkGroup::next( const KBookmark & current ) const
+{
+ return KBookmark( nextKnownTag( firstElement(current.element.nextSibling()), true ) );
+}
+
+// KDE4: Change QDomElement to QDomNode so that we can get rid of
+// firstElement() and lastElement()
+QDomElement KBookmarkGroup::nextKnownTag( QDomElement start, bool goNext ) const
+{
+ static const QString & bookmark = KGlobal::staticQString("bookmark");
+ static const QString & folder = KGlobal::staticQString("folder");
+ static const QString & separator = KGlobal::staticQString("separator");
+
+ for( QDomNode n = start; !n.isNull(); )
+ {
+ QDomElement elem = n.toElement();
+ QString tag = elem.tagName();
+ if (tag == folder || tag == bookmark || tag == separator)
+ return elem;
+ if (goNext)
+ n = n.nextSibling();
+ else
+ n = n.previousSibling();
+ }
+ return QDomElement();
+}
+
+KBookmarkGroup KBookmarkGroup::createNewFolder( KBookmarkManager* mgr, const QString & text, bool emitSignal )
+{
+ QString txt( text );
+ if ( text.isEmpty() )
+ {
+ bool ok;
+ QString caption = parentGroup().fullText().isEmpty() ?
+ i18n( "Create New Bookmark Folder" ) :
+ i18n( "Create New Bookmark Folder in %1" )
+ .arg( parentGroup().text() );
+ txt = KInputDialog::getText( caption, i18n( "New folder:" ),
+ QString::null, &ok );
+ if ( !ok )
+ return KBookmarkGroup();
+ }
+
+ Q_ASSERT(!element.isNull());
+ QDomDocument doc = element.ownerDocument();
+ QDomElement groupElem = doc.createElement( "folder" );
+ element.appendChild( groupElem );
+ QDomElement textElem = doc.createElement( "title" );
+ groupElem.appendChild( textElem );
+ textElem.appendChild( doc.createTextNode( txt ) );
+
+ KBookmarkGroup grp(groupElem);
+
+ if (emitSignal)
+ emit mgr->notifier().createdNewFolder(
+ mgr->path(), grp.fullText(),
+ grp.address() );
+
+ return grp;
+
+}
+
+KBookmark KBookmarkGroup::createNewSeparator()
+{
+ Q_ASSERT(!element.isNull());
+ QDomDocument doc = element.ownerDocument();
+ Q_ASSERT(!doc.isNull());
+ QDomElement sepElem = doc.createElement( "separator" );
+ element.appendChild( sepElem );
+ return KBookmark(sepElem);
+}
+
+bool KBookmarkGroup::moveItem( const KBookmark & item, const KBookmark & after )
+{
+ QDomNode n;
+ if ( !after.isNull() )
+ n = element.insertAfter( item.element, after.element );
+ else // first child
+ {
+ if ( element.firstChild().isNull() ) // Empty element -> set as real first child
+ n = element.insertBefore( item.element, QDomElement() );
+
+ // we have to skip everything up to the first valid child
+ QDomElement firstChild = nextKnownTag(element.firstChild().toElement(), true);
+ if ( !firstChild.isNull() )
+ n = element.insertBefore( item.element, firstChild );
+ else
+ {
+ // No real first child -> append after the <title> etc.
+ n = element.appendChild( item.element );
+ }
+ }
+ return (!n.isNull());
+}
+
+KBookmark KBookmarkGroup::addBookmark( KBookmarkManager* mgr, const KBookmark &bm, bool emitSignal )
+{
+ element.appendChild( bm.internalElement() );
+
+ if (emitSignal) {
+ if ( bm.hasMetaData() ) {
+ mgr->notifyCompleteChange( "" );
+ } else {
+ emit mgr->notifier().addedBookmark(
+ mgr->path(), bm.url().url(),
+ bm.fullText(), bm.address(), bm.icon() );
+ }
+ }
+
+ return bm;
+}
+
+KBookmark KBookmarkGroup::addBookmark( KBookmarkManager* mgr, const QString & text, const KURL & url, const QString & icon, bool emitSignal )
+{
+ //kdDebug(7043) << "KBookmarkGroup::addBookmark " << text << " into " << m_address << endl;
+ QDomDocument doc = element.ownerDocument();
+ QDomElement elem = doc.createElement( "bookmark" );
+ elem.setAttribute( "href", url.url( 0, 106 ) ); // write utf8 URL (106 is mib enum for utf8)
+ QString _icon = icon;
+ if ( _icon.isEmpty() )
+ _icon = KMimeType::iconForURL( url );
+ elem.setAttribute( "icon", _icon );
+
+ QDomElement textElem = doc.createElement( "title" );
+ elem.appendChild( textElem );
+ textElem.appendChild( doc.createTextNode( text ) );
+
+ return addBookmark( mgr, KBookmark( elem ), emitSignal );
+}
+
+void KBookmarkGroup::deleteBookmark( KBookmark bk )
+{
+ element.removeChild( bk.element );
+}
+
+bool KBookmarkGroup::isToolbarGroup() const
+{
+ return ( element.attribute("toolbar") == "yes" );
+}
+
+QDomElement KBookmarkGroup::findToolbar() const
+{
+ if ( element.attribute("toolbar") == "yes" )
+ return element;
+ for (QDomNode n = element.firstChild(); !n.isNull() ; n = n.nextSibling() )
+ {
+ QDomElement e = n.toElement();
+ // Search among the "folder" children only
+ if ( e.tagName() == "folder" )
+ {
+ if ( e.attribute("toolbar") == "yes" )
+ return e;
+ else
+ {
+ QDomElement result = KBookmarkGroup(e).findToolbar();
+ if (!result.isNull())
+ return result;
+ }
+ }
+ }
+ return QDomElement();
+}
+
+QValueList<KURL> KBookmarkGroup::groupUrlList() const
+{
+ QValueList<KURL> urlList;
+ for ( KBookmark bm = first(); !bm.isNull(); bm = next(bm) )
+ {
+ if ( bm.isSeparator() || bm.isGroup() )
+ continue;
+ urlList << bm.url();
+ }
+ return urlList;
+}
+
+//////
+
+bool KBookmark::isGroup() const
+{
+ QString tag = element.tagName();
+ return ( tag == "folder"
+ || tag == "xbel" ); // don't forget the toplevel group
+}
+
+bool KBookmark::isSeparator() const
+{
+ return (element.tagName() == "separator");
+}
+
+bool KBookmark::hasParent() const
+{
+ QDomElement parent = element.parentNode().toElement();
+ return !parent.isNull();
+}
+
+QString KBookmark::text() const
+{
+ return KStringHandler::csqueeze( fullText() );
+}
+
+QString KBookmark::fullText() const
+{
+ if (isSeparator())
+ return i18n("--- separator ---");
+
+ return element.namedItem("title").toElement().text();
+}
+
+KURL KBookmark::url() const
+{
+ return KURL(element.attribute("href"), 106); // Decode it from utf8 (106 is mib enum for utf8)
+}
+
+QString KBookmark::icon() const
+{
+ QString icon = element.attribute("icon");
+ if ( icon.isEmpty() )
+ // Default icon depends on URL for bookmarks, and is default directory
+ // icon for groups.
+ if ( isGroup() )
+ icon = "bookmark_folder";
+ else
+ if ( isSeparator() )
+ icon = "eraser"; // whatever
+ else
+ icon = KMimeType::iconForURL( url() );
+ return icon;
+}
+
+KBookmarkGroup KBookmark::parentGroup() const
+{
+ return KBookmarkGroup( element.parentNode().toElement() );
+}
+
+KBookmarkGroup KBookmark::toGroup() const
+{
+ Q_ASSERT( isGroup() );
+ return KBookmarkGroup(element);
+}
+
+QString KBookmark::address() const
+{
+ if ( element.tagName() == "xbel" )
+ return ""; // not QString::null !
+ else
+ {
+ // Use keditbookmarks's DEBUG_ADDRESSES flag to debug this code :)
+ if (!hasParent())
+ {
+ Q_ASSERT(hasParent());
+ return "ERROR"; // Avoid an infinite loop
+ }
+ KBookmarkGroup group = parentGroup();
+ QString parentAddress = group.address();
+ uint counter = 0;
+ // Implementation note: we don't use QDomNode's childNode list because we
+ // would have to skip "TEXT", which KBookmarkGroup already does for us.
+ for ( KBookmark bk = group.first() ; !bk.isNull() ; bk = group.next(bk), ++counter )
+ {
+ if ( bk.element == element )
+ return parentAddress + "/" + QString::number(counter);
+ }
+ kdWarning() << "KBookmark::address : this can't happen! " << parentAddress << endl;
+ return "ERROR";
+ }
+}
+
+KBookmark KBookmark::standaloneBookmark( const QString & text, const KURL & url, const QString & icon )
+{
+ QDomDocument doc("xbel");
+ QDomElement elem = doc.createElement("xbel");
+ doc.appendChild( elem );
+ KBookmarkGroup grp( elem );
+ grp.addBookmark( 0L, text, url, icon, false );
+ return grp.first();
+}
+
+// For some strange reason QString("").left(0) returns QString::null;
+// That breaks commonParent()
+QString KBookmark::left(const QString & str, uint len)
+{
+ //kdDebug()<<"********"<<QString("").left(0).isNull()<<endl;
+ if(len == 0)
+ return QString("");
+ else
+ return str.left(len);
+}
+
+QString KBookmark::commonParent(QString A, QString B)
+{
+ QString error("ERROR");
+ if(A == error || B == error)
+ return error;
+
+ A += "/";
+ B += "/";
+
+ uint lastCommonSlash = 0;
+ uint lastPos = A.length() < B.length() ? A.length() : B.length();
+ for(uint i=0; i < lastPos; ++i)
+ {
+ if(A[i] != B[i])
+ return left(A, lastCommonSlash);
+ if(A[i] == '/')
+ lastCommonSlash = i;
+ }
+ return left(A, lastCommonSlash);
+}
+
+static QDomNode cd_or_create(QDomNode node, QString name)
+{
+ QDomNode subnode = node.namedItem(name);
+ if (subnode.isNull())
+ {
+ subnode = node.ownerDocument().createElement(name);
+ node.appendChild(subnode);
+ }
+ return subnode;
+}
+
+static QDomText get_or_create_text(QDomNode node)
+{
+ QDomNode subnode = node.firstChild();
+ if (subnode.isNull())
+ {
+ subnode = node.ownerDocument().createTextNode("");
+ node.appendChild(subnode);
+ }
+ return subnode.toText();
+}
+
+// Look for a metadata with owner="http://www.kde.org" or without any owner (for compatibility)
+static QDomNode findOrCreateMetadata( QDomNode& parent )
+{
+ static const char kdeOwner[] = "http://www.kde.org";
+ QDomElement metadataElement;
+ for ( QDomNode _node = parent.firstChild(); !_node.isNull(); _node = _node.nextSibling() ) {
+ QDomElement elem = _node.toElement();
+ if ( !elem.isNull() && elem.tagName() == "metadata" ) {
+ const QString owner = elem.attribute( "owner" );
+ if ( owner == kdeOwner )
+ return elem;
+ if ( owner.isEmpty() )
+ metadataElement = elem;
+ }
+ }
+ if ( metadataElement.isNull() ) {
+ metadataElement = parent.ownerDocument().createElement( "metadata" );
+ parent.appendChild(metadataElement);
+ }
+ metadataElement.setAttribute( "owner", kdeOwner );
+ return metadataElement;
+}
+
+bool KBookmark::hasMetaData() const
+{
+ // ### NOTE: this code creates <info> and <metadata>, despite its name and the const.
+ // It doesn't matter much in practice since it's only called for newly-created bookmarks,
+ // which will get metadata soon after anyway.
+ QDomNode n = cd_or_create( internalElement(), "info" );
+ return findOrCreateMetadata( n ).hasChildNodes();
+}
+
+void KBookmark::updateAccessMetadata()
+{
+ kdDebug(7043) << "KBookmark::updateAccessMetadata " << address() << " " << url().prettyURL() << endl;
+
+ const uint timet = QDateTime::currentDateTime().toTime_t();
+ setMetaDataItem( "time_added", QString::number( timet ), DontOverwriteMetaData );
+ setMetaDataItem( "time_visited", QString::number( timet ) );
+
+ QString countStr = metaDataItem( "visit_count" ); // TODO use spec'ed name
+ bool ok;
+ int currentCount = countStr.toInt(&ok);
+ if (!ok)
+ currentCount = 0;
+ currentCount++;
+ setMetaDataItem( "visit_count", QString::number( currentCount ) );
+
+ // TODO - for 4.0 - time_modified
+}
+
+QString KBookmark::metaDataItem( const QString &key ) const
+{
+ QDomNode infoNode = cd_or_create( internalElement(), "info" );
+ infoNode = findOrCreateMetadata( infoNode );
+ for ( QDomNode n = infoNode.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( !n.isElement() ) {
+ continue;
+ }
+ const QDomElement e = n.toElement();
+ if ( e.tagName() == key ) {
+ return e.text();
+ }
+ }
+ return QString::null;
+}
+
+void KBookmark::setMetaDataItem( const QString &key, const QString &value, MetaDataOverwriteMode mode )
+{
+ QDomNode infoNode = cd_or_create( internalElement(), "info" );
+ infoNode = findOrCreateMetadata( infoNode );
+
+ QDomNode item = cd_or_create( infoNode, key );
+ QDomText text = get_or_create_text( item );
+ if ( mode == DontOverwriteMetaData && !text.data().isEmpty() ) {
+ return;
+ }
+
+ text.setData( value );
+}
+
+void KBookmarkGroupTraverser::traverse(const KBookmarkGroup &root)
+{
+ // non-recursive bookmark iterator
+ QValueStack<KBookmarkGroup> stack;
+ stack.push(root);
+ KBookmark bk = stack.top().first();
+ for (;;) {
+ if (bk.isNull())
+ {
+ if (stack.isEmpty())
+ return;
+ if (stack.count() > 1)
+ visitLeave(stack.top());
+ bk = stack.pop();
+ bk = stack.top().next(bk);
+ if (bk.isNull())
+ continue;
+ }
+
+ if (bk.isGroup())
+ {
+ KBookmarkGroup gp = bk.toGroup();
+ visitEnter(gp);
+ if (!gp.first().isNull())
+ {
+ stack.push(gp);
+ bk = gp.first();
+ continue;
+ }
+ // empty group
+ visitLeave(gp);
+ }
+ else
+ visit(bk);
+
+ bk = stack.top().next(bk);
+ }
+
+ // never reached
+}
+