Index: ChangeLog
===================================================================
--- akregator/ChangeLog	(.../tags/KDE/3.5.1/kdepim/akregator)	(revision 519586)
+++ akregator/ChangeLog	(.../branches/KDE/3.5/kdepim/akregator)	(revision 519586)
@@ -2,6 +2,24 @@ Akregator ChangeLog
 ===================
 (c) 2004-2006 the Akregator authors.
 
+Changes after 1.2.1:
+-----------------------------
+
+Bug fixes:
+
+ 2006/03/09 Read more feed metadata for Atom feeds (title, link) (#123140) -fo
+ 2006/03/09 Use "refresh" cache mode as default, ignoring konq settings. 
+            ("refresh" == Use cache after verifying with server) 
+            This should make disabling the "Use browser cache" option unnecessary. -fo
+ 2006/02/21 Set "rel" attribute default to "alternate", as requested by Atom specification (#122409) -ew
+ 2006/02/21 Disable category and tag support (unless experimental GUI is enabled) in the metakit backend, 
+            so it supports about 1000 feeds (500 with tags enabled) instead of 340
+            now with the default 1024 open files limit on most systems. -fo
+ 2006/02/15 Combined View: Do not crash on "Next/Prev Unread Article" (#121999) -fo
+ 2006/01/31 RSS parser: ignore unknown or invalid version attribute value in the <rss> tag and
+            just assume RSS 2.0. The older formats are compatible to 2.0, so this should work.
+            (at least better than refusing to parse the feeds) (#118793) -fo
+
 Changes after 1.2:
 -----------------------------
 
Index: src/storagefactory.h
===================================================================
--- akregator/src/storagefactory.h	(.../tags/KDE/3.5.1/kdepim/akregator)	(revision 519586)
+++ akregator/src/storagefactory.h	(.../branches/KDE/3.5/kdepim/akregator)	(revision 519586)
@@ -50,6 +50,13 @@ class StorageFactory
     
     /** shows the plugin's configuration dialog */
     virtual void configure() = 0;
+
+    /**
+     * returns wether the backend allows multiple writers at the same time
+     * If not, Akregator must use a lock to ensure that only one process gains
+     * write access.
+     */
+    virtual bool allowsMultipleWriteAccess() const = 0;
     
     /** creates a storage object with given parameters
         @param params list of implementation-specific parameters
Index: src/articlelistview.cpp
===================================================================
--- akregator/src/articlelistview.cpp	(.../tags/KDE/3.5.1/kdepim/akregator)	(revision 519586)
+++ akregator/src/articlelistview.cpp	(.../branches/KDE/3.5/kdepim/akregator)	(revision 519586)
@@ -348,6 +348,9 @@ void ArticleListView::slotArticlesAdded(
 {
     setUpdatesEnabled(false);
     
+    bool statusActive = !(d->statusFilter.matchesAll());
+    bool textActive = !(d->textFilter.matchesAll());
+    
     for (QValueList<Article>::ConstIterator it = list.begin(); it != list.end(); ++it)
     {
         if (!d->articleMap.contains(*it))
@@ -355,7 +358,8 @@ void ArticleListView::slotArticlesAdded(
             if (!(*it).isNull() && !(*it).isDeleted())
             {
                 ArticleItem* ali = new ArticleItem(this, *it);
-                ali->setVisible( d->textFilter.matches( ali->article()) );
+                ali->setVisible( (!statusActive ||  d->statusFilter.matches( ali->article()))
+                        && (!textActive || d->textFilter.matches( ali->article())) );
                 d->articleMap.insert(*it, ali);
             }
         }
@@ -372,6 +376,9 @@ void ArticleListView::slotArticlesUpdate
     // is deleted, we will select the next item in the list
     bool singleSelected = selectedArticles().count() == 1;
     
+    bool statusActive = !(d->statusFilter.matchesAll());
+    bool textActive = !(d->textFilter.matchesAll());
+    
     QListViewItem* next = 0; // the item to select if a selected item is deleted
     
     for (QValueList<Article>::ConstIterator it = list.begin(); it != list.end(); ++it)
@@ -397,8 +404,11 @@ void ArticleListView::slotArticlesUpdate
             else
             {
                 ali->updateItem(*it);
-                // if the updated article matches the filters after the update, make visible. If it matched them before but not after update, they should stay visible (to not confuse users)
-                if (d->textFilter.matches( ali->article()) && d->statusFilter.matches(ali->article()))
+                // if the updated article matches the filters after the update,
+                // make visible. If it matched them before but not after update,
+                // they should stay visible (to not confuse users)
+                if ((!statusActive || d->statusFilter.matches(ali->article()))
+                      && (!textActive || d->textFilter.matches( ali->article())) )
                     ali->setVisible(true);
             }
         }
@@ -475,12 +485,44 @@ void ArticleListView::disconnectFromNode
 
 void ArticleListView::applyFilters()
 {
+    bool statusActive = !(d->statusFilter.matchesAll());
+    bool textActive = !(d->textFilter.matchesAll());
+    
     ArticleItem* ali = 0;
-    for (QListViewItemIterator it(this); it.current(); ++it)
+    
+    if (!statusActive && !textActive)
     {
-        ali = static_cast<ArticleItem*> (it.current());
-        ali->setVisible( d->statusFilter.matches( ali->article() ) && d->textFilter.matches( ali->article() ) );
+        for (QListViewItemIterator it(this); it.current(); ++it)
+        {
+            (static_cast<ArticleItem*> (it.current()))->setVisible(true);
+        }
+    }
+    else if (statusActive && !textActive)
+    {
+        for (QListViewItemIterator it(this); it.current(); ++it)
+        {
+            ali = static_cast<ArticleItem*> (it.current());
+            ali->setVisible( d->statusFilter.matches( ali->article()) );
+        }
+    }
+    else if (!statusActive && textActive)
+    {
+        for (QListViewItemIterator it(this); it.current(); ++it)
+        {
+            ali = static_cast<ArticleItem*> (it.current());
+            ali->setVisible( d->textFilter.matches( ali->article()) );
+        }
+    }
+    else // both true
+    {
+        for (QListViewItemIterator it(this); it.current(); ++it)
+        {
+            ali = static_cast<ArticleItem*> (it.current());
+            ali->setVisible( d->statusFilter.matches( ali->article()) 
+                    && d->textFilter.matches( ali->article()) );
+        }
     }
+
 }
 
 int ArticleListView::visibleArticles()
@@ -608,21 +650,26 @@ void ArticleListView::slotNextArticle()
 
 void ArticleListView::slotNextUnreadArticle()
 {
-    ArticleItem* start = 0;
+    ArticleItem* start = 0L;
     if (!currentItem() || selectedItems().isEmpty())
         start = dynamic_cast<ArticleItem*>(firstChild());
     else
         start = dynamic_cast<ArticleItem*>(currentItem()->itemBelow() ? currentItem()->itemBelow() : firstChild());
 
     ArticleItem* i = start;
-    ArticleItem* unread = 0;
+    ArticleItem* unread = 0L;
     
     do
     {
-        if (i && i->article().status() != Article::Read)
-            unread = i;
-        else 
-            i = dynamic_cast<ArticleItem*>(i && i->itemBelow() ? i->itemBelow() : firstChild());
+        if (i == 0L)
+            i = static_cast<ArticleItem*>(firstChild());
+        else
+        {
+            if (i->article().status() != Article::Read)
+                unread = i;
+            else 
+                i = static_cast<ArticleItem*>(i && i->itemBelow() ? i->itemBelow() : firstChild());
+        }
     }
     while (!unread && i != start);
 
@@ -638,23 +685,28 @@ void ArticleListView::slotNextUnreadArti
 
 void ArticleListView::slotPreviousUnreadArticle()
 {
-    ArticleItem* start = 0;
+    ArticleItem* start = 0L;
     if (!currentItem() || selectedItems().isEmpty())
         start = dynamic_cast<ArticleItem*>(firstChild());
     else
         start = dynamic_cast<ArticleItem*>(currentItem()->itemAbove() ? currentItem()->itemAbove() : firstChild());
 
     ArticleItem* i = start;
-    ArticleItem* unread = 0;
+    ArticleItem* unread = 0L;
     
     do
     {
-        if (i && i->article().status() != Article::Read)
-            unread = i;
-        else 
-            i = dynamic_cast<ArticleItem*>(i->itemAbove() ? i->itemAbove() : lastChild());
+        if (i == 0L)
+            i = static_cast<ArticleItem*>(lastChild());
+        else
+        {
+            if (i->article().status() != Article::Read)
+                unread = i;
+            else 
+                i = static_cast<ArticleItem*>(i->itemAbove() ? i->itemAbove() : lastChild());
+        }
     }
-    while ( !(unread != 0 || i == start) );
+    while ( !(unread != 0L || i == start) );
 
     if (unread)
     {
Index: src/librss/loader.cpp
===================================================================
--- akregator/src/librss/loader.cpp	(.../tags/KDE/3.5.1/kdepim/akregator)	(revision 519586)
+++ akregator/src/librss/loader.cpp	(.../branches/KDE/3.5/kdepim/akregator)	(revision 519586)
@@ -13,12 +13,14 @@
 
 #include <kio/job.h>
 #include <kprocess.h>
+#include <kstaticdeleter.h>
 #include <kurl.h>
 #include <kdebug.h>
 
 #include <qdom.h>
 #include <qbuffer.h>
 #include <qregexp.h>
+#include <qstring.h>
 #include <qstringlist.h>
 #include <qtimer.h>
 
@@ -32,8 +34,10 @@ DataRetriever::~DataRetriever()
 {
 }
 
-struct FileRetriever::Private
+class FileRetriever::Private
 {
+    public:
+        
    Private()
       : buffer(NULL),
         lastError(0), job(NULL)
@@ -48,8 +52,12 @@ struct FileRetriever::Private
    QBuffer *buffer;
    int lastError;
    KIO::Job *job;
+   static KStaticDeleter<QString> userAgentsd;
+   static QString* userAgent;
 };
 
+KStaticDeleter<QString> FileRetriever::Private::userAgentsd;
+QString* FileRetriever::Private::userAgent = 0L;
 FileRetriever::FileRetriever()
    : d(new Private)
 {
@@ -61,11 +69,19 @@ FileRetriever::~FileRetriever()
 }
 
 bool FileRetriever::m_useCache = true;
-QString FileRetriever::m_userAgent = 0;
+
+QString FileRetriever::userAgent()
+{
+    if (Private::userAgent == 0L)
+        FileRetriever::Private::userAgentsd.setObject(Private::userAgent, new QString);
+    return *Private::userAgent;
+}
 
 void FileRetriever::setUserAgent(const QString &ua)
 {
-    m_userAgent = ua;
+    if (Private::userAgent == 0L)
+        FileRetriever::Private::userAgentsd.setObject(Private::userAgent, new QString);
+    (*Private::userAgent) = ua;
 }
 
 void FileRetriever::setUseCache(bool enabled)
@@ -86,9 +102,12 @@ void FileRetriever::retrieveData(const K
    if (u.protocol()=="feed")
        u.setProtocol("http");
 
-   d->job = KIO::get(u, !m_useCache, false);
+   d->job = KIO::get(u, false, false);
+   d->job->addMetaData("cache", m_useCache ? "refresh" : "reload");
 
-   d->job->addMetaData("UserAgent", m_userAgent);
+   QString ua = userAgent();
+   if (!ua.isEmpty())
+      d->job->addMetaData("UserAgent", ua);
 
 
    QTimer::singleShot(1000*90, this, SLOT(slotTimeout()));
@@ -298,8 +317,6 @@ const KURL &Loader::discoveredFeedURL() 
    return d->discoveredFeedURL;
 }
 
-#include <kdebug.h>
-
 void Loader::slotRetrieverDone(const QByteArray &data, bool success)
 {
    d->lastError = d->retriever->errorCode();
Index: src/librss/loader.h
===================================================================
--- akregator/src/librss/loader.h	(.../tags/KDE/3.5.1/kdepim/akregator)	(revision 519586)
+++ akregator/src/librss/loader.h	(.../branches/KDE/3.5/kdepim/akregator)	(revision 519586)
@@ -121,6 +121,7 @@ namespace RSS
 
 			static void setUseCache(bool enabled);
 			static void setUserAgent(const QString &ua);
+                        static QString userAgent();
 
 		signals:
 			/**
@@ -143,7 +144,6 @@ namespace RSS
 
 		private:
 			static bool m_useCache;
-			static QString m_userAgent;
 
 			FileRetriever(const FileRetriever &other);
 			FileRetriever &operator=(const FileRetriever &other);
Index: src/librss/document.cpp
===================================================================
--- akregator/src/librss/document.cpp	(.../tags/KDE/3.5.1/kdepim/akregator)	(revision 519586)
+++ akregator/src/librss/document.cpp	(.../branches/KDE/3.5/kdepim/akregator)	(revision 519586)
@@ -72,6 +72,25 @@ Document::Document(const Document &other
     *this = other;
 }
 
+static QString extractLink(const QDomNode& node, Format format)
+{
+    if (format == AtomFeed)
+    {
+        QDomNode n;
+        for (n = node.firstChild(); !n.isNull(); n = n.nextSibling()) {
+            const QDomElement e = n.toElement();
+            if ( (e.tagName() == QString::fromLatin1("link")) 
+                  && (e.attribute(QString::fromLatin1("rel"), QString::fromLatin1("alternate")) == QString::fromLatin1("alternate")))
+            {   
+                return n.toElement().attribute(QString::fromLatin1("href"));
+            }
+        }
+    }
+
+    return extractNode(node, QString::fromLatin1("link"));
+    
+}
+
 Document::Document(const QDomDocument &doc) : d(new Private)
 {
     QString elemText;
@@ -110,7 +129,8 @@ Document::Document(const QDomDocument &d
             d->version = v0_93;
         else if (attr == QString::fromLatin1("0.94"))
             d->version = v0_94;
-        else if (attr.startsWith("2.0") || attr == QString::fromLatin1("2")) // http://www.breuls.org/rss puts 2.00 in version (BR #0000016)
+        else // otherwise, we just assume a RSS2 compatible feed. As rss2 is generally
+             // backward-compatible, this should work
             d->version = v2_0;
     }
     
@@ -147,10 +167,20 @@ Document::Document(const QDomDocument &d
 
     if (!(elemText = extractTitle(channelNode)).isNull())
         d->title = elemText;
-    if (!(elemText = extractNode(channelNode, QString::fromLatin1("description"))).isNull())
+    QString descriptionTagName = "description";
+    
+    if (d->format == AtomFeed)
+    {
+        if (d->version == vAtom_1_0)
+            descriptionTagName = "subtitle";
+        else
+            descriptionTagName = "tagline";
+    }
+    
+    if (!(elemText = extractNode(channelNode, descriptionTagName)).isNull())
         d->description = elemText;
-    if (!(elemText = extractNode(channelNode, QString::fromLatin1("link"))).isNull())
-        d->link = elemText;
+        
+    d->link = extractLink(channelNode, d->format);
 
     
     /* This is ugly but necessary since RSS 0.90 and 1.0 have a different parent
Index: src/librss/tools_p.cpp
===================================================================
--- akregator/src/librss/tools_p.cpp	(.../tags/KDE/3.5.1/kdepim/akregator)	(revision 519586)
+++ akregator/src/librss/tools_p.cpp	(.../branches/KDE/3.5/kdepim/akregator)	(revision 519586)
@@ -15,7 +15,9 @@
 #include <kcharsets.h>
 #include <qregexp.h>
 
-time_t RSS::parseISO8601Date(const QString &s)
+namespace RSS {
+
+time_t parseISO8601Date(const QString &s)
 {
     // do some sanity check: 26-12-2004T00:00+00:00 is parsed to epoch+1 in the KRFCDate, which is wrong. So let's check if the date begins with YYYY -fo
     if (s.stripWhiteSpace().left(4).toInt() < 1000)
@@ -28,7 +30,7 @@ time_t RSS::parseISO8601Date(const QStri
         return KRFCDate::parseDateISO8601(s + "T12:00:00");
 }
 
-QString RSS::childNodesAsXML(const QDomNode& parent)
+QString childNodesAsXML(const QDomNode& parent)
 {
 	QDomNodeList list = parent.childNodes();
 	QString str;
@@ -38,7 +40,81 @@ QString RSS::childNodesAsXML(const QDomN
 	return str.stripWhiteSpace();
 }
 
-QString RSS::extractNode(const QDomNode &parent, const QString &elemName, bool isInlined)
+static QString plainTextToHtml(const QString& plainText)
+{
+    QString str(plainText);
+    str.replace("&", "&amp;");
+    str.replace("\"", "&quot;");
+    str.replace("<", "&lt;");
+    //str.replace(">", "&gt;");
+    str.replace("\n", "<br/>");
+    return str;
+}
+
+enum ContentFormat { Text, HTML, XML, Binary };
+        
+static ContentFormat mapTypeToFormat(const QString& modep, const QString& typep,  const QString& src)
+{
+    QString mode = modep.isNull() ? "escaped" : modep;
+    QString type = typep;
+    
+    //"If neither the type attribute nor the src attribute is provided,
+    //Atom Processors MUST behave as though the type attribute were
+    //present with a value of "text""
+    if (type.isNull() && src.isEmpty())
+        type = QString::fromUtf8("text");
+
+    if (type == QString::fromUtf8("html")
+        || type == QString::fromUtf8("text/html"))
+        return HTML;
+    
+    if (type == QString::fromUtf8("text")
+        || (type.startsWith(QString::fromUtf8("text/"), false)
+        && !type.startsWith(QString::fromUtf8("text/xml"), false))
+       )
+        return Text;
+    
+    QStringList xmltypes;
+    xmltypes.append(QString::fromUtf8("xhtml"));
+    // XML media types as defined in RFC3023:
+    xmltypes.append(QString::fromUtf8("text/xml"));
+    xmltypes.append(QString::fromUtf8("application/xml"));
+    xmltypes.append(QString::fromUtf8("text/xml-external-parsed-entity"));
+    xmltypes.append(QString::fromUtf8("application/xml-external-parsed-entity"));
+    xmltypes.append(QString::fromUtf8("application/xml-dtd"));
+    
+    
+    if (xmltypes.contains(type)
+        || type.endsWith(QString::fromUtf8("+xml"), false)
+        || type.endsWith(QString::fromUtf8("/xml"), false))
+        return XML;
+    
+    return Binary;
+}
+
+static QString extractAtomContent(const QDomElement& e)
+{
+    ContentFormat format = mapTypeToFormat(e.attribute("mode"),
+                                           e.attribute("type"),
+                                           e.attribute("src"));
+    
+    switch (format)
+    {
+        case HTML:
+            return KCharsets::resolveEntities(e.text().simplifyWhiteSpace());
+        case Text:
+            return plainTextToHtml(e.text().stripWhiteSpace());
+        case XML:
+            return childNodesAsXML(e).simplifyWhiteSpace();
+        case Binary:
+        default:
+            return QString();
+    }
+    
+    return QString();
+}
+
+QString extractNode(const QDomNode &parent, const QString &elemName, bool isInlined)
 {
 	QDomNode node = parent.namedItem(elemName);
 	if (node.isNull())
@@ -46,31 +122,12 @@ QString RSS::extractNode(const QDomNode 
 
 	QDomElement e = node.toElement();
         QString result = e.text().stripWhiteSpace(); // let's assume plain text
-
-        bool doHTMLCheck = true;
  
         if (elemName == "content") // we have Atom here
         {
-            doHTMLCheck = false;
-            // the first line is always the Atom 0.3, the second Atom 1.0
-            if (( e.hasAttribute("mode") && e.attribute("mode") == "escaped" && e.attribute("type") == "text/html" )
-            || (!e.hasAttribute("mode") && e.attribute("type") == "html"))
-            {
-                result = KCharsets::resolveEntities(e.text().simplifyWhiteSpace()); // escaped html
-            }
-            else if (( e.hasAttribute("mode") && e.attribute("mode") == "escaped" && e.attribute("type") == "text/plain" )
-                       || (!e.hasAttribute("mode") && e.attribute("type") == "text"))
-            {
-                result = e.text().stripWhiteSpace(); // plain text
-            }
-            else if (( e.hasAttribute("mode") && e.attribute("mode") == "xml" )
-                       || (!e.hasAttribute("mode") && e.attribute("type") == "xhtml"))
-            {
-                result = childNodesAsXML(e); // embedded XHMTL
-            }
+            result = extractAtomContent(e);
         }        
-        
-        if (doHTMLCheck) // check for HTML; not necessary for Atom:content
+        else // check for HTML; not necessary for Atom:content
         {
             bool hasPre = result.contains("<pre>",false);
             bool hasHtml = hasPre || result.contains("<");	// FIXME: test if we have html, should be more clever -> regexp
@@ -83,7 +140,7 @@ QString RSS::extractNode(const QDomNode 
         return result.isEmpty() ? QString::null : result;
 }
 
-QString RSS::extractTitle(const QDomNode & parent)
+QString extractTitle(const QDomNode & parent)
 {
     QDomNode node = parent.namedItem(QString::fromLatin1("title"));
     if (node.isNull())
@@ -100,4 +157,6 @@ QString RSS::extractTitle(const QDomNode
     return result;
 }
 
+} // namespace RSS
+
 // vim:noet:ts=4
Index: src/librss/article.cpp
===================================================================
--- akregator/src/librss/article.cpp	(.../tags/KDE/3.5.1/kdepim/akregator)	(revision 519586)
+++ akregator/src/librss/article.cpp	(.../branches/KDE/3.5/kdepim/akregator)	(revision 519586)
@@ -77,7 +77,7 @@ Article::Article(const QDomNode &node, F
 		for (n = node.firstChild(); !n.isNull(); n = n.nextSibling()) {
 			const QDomElement e = n.toElement();
 			if ( (e.tagName()==QString::fromLatin1("link")) &&
-				(e.attribute(QString::fromLatin1("rel"))==QString::fromLatin1("alternate")))
+				(e.attribute(QString::fromLatin1("rel"), QString::fromLatin1("alternate")) == QString::fromLatin1("alternate")))
 				{   
 					d->link=n.toElement().attribute(QString::fromLatin1("href"));
 					break;
Index: src/articlefilter.h
===================================================================
--- akregator/src/articlefilter.h	(.../tags/KDE/3.5.1/kdepim/akregator)	(revision 519586)
+++ akregator/src/articlefilter.h	(.../branches/KDE/3.5/kdepim/akregator)	(revision 519586)
@@ -223,6 +223,12 @@ class ArticleMatcher : public AbstractMa
         ArticleMatcher(const ArticleMatcher& other);
         virtual ~ArticleMatcher();
 
+        /**
+         * returns whether the matcher matches all articles anyway (empty criteria list),
+         * so there is no need to call matches() at all.
+         */
+        virtual bool matchesAll() const;
+        
         ArticleMatcher& operator=(const ArticleMatcher& other);
         virtual ArticleMatcher* clone() const;
         virtual bool matches(const Article &article) const;
Index: src/mk4storage/storagefactorymk4impl.h
===================================================================
--- akregator/src/mk4storage/storagefactorymk4impl.h	(.../tags/KDE/3.5.1/kdepim/akregator)	(revision 519586)
+++ akregator/src/mk4storage/storagefactorymk4impl.h	(.../branches/KDE/3.5/kdepim/akregator)	(revision 519586)
@@ -43,6 +43,8 @@ class StorageFactoryMK4Impl : public Sto
     virtual void configure();
     virtual Storage* createStorage(const QStringList& params) const;
     virtual bool isConfigurable() const { return false; }
+    virtual bool allowsMultipleWriteAccess() const { return false; }
+    
 };
 
 }
Index: src/mk4storage/storagemk4impl.cpp
===================================================================
--- akregator/src/mk4storage/storagemk4impl.cpp	(.../tags/KDE/3.5.1/kdepim/akregator)	(revision 519586)
+++ akregator/src/mk4storage/storagemk4impl.cpp	(.../branches/KDE/3.5/kdepim/akregator)	(revision 519586)
@@ -59,11 +59,18 @@ class StorageMK4Impl::StorageMK4ImplPriv
         c4_IntProp punread, ptotalCount, plastFetch;
         QTimer* commitTimer;
         QString archivePath;
-
+        
+        bool taggingEnabled;
+        
         c4_Storage* feedListStorage;
         c4_View feedListView;
 };
 
+bool StorageMK4Impl::taggingEnabled() const
+{
+    return d->taggingEnabled;
+}
+
 void StorageMK4Impl::setArchivePath(const QString& archivePath)
 {
     if (archivePath.isNull()) // if isNull, reset to default
@@ -97,7 +104,24 @@ StorageMK4Impl::~StorageMK4Impl()
     delete d;
     d = 0;
 }
-void StorageMK4Impl::initialize(const QStringList&) {}
+void StorageMK4Impl::initialize(const QStringList& params)
+{
+    d->taggingEnabled = false;
+    
+    QStringList::ConstIterator it = params.begin();
+    QStringList::ConstIterator end = params.end();
+    
+    for ( ; it != end; ++it)
+    {
+        QStringList tokens = QStringList::split("=", *it);
+        if (tokens.count() == 2 && *(tokens.at(0)) == "taggingEnabled" 
+            && *(tokens.at(1)) == "true")
+        {
+            d->taggingEnabled = true;
+        }
+        
+    }
+}
 
 bool StorageMK4Impl::open(bool autoCommit)
 {
Index: src/mk4storage/storagemk4impl.h
===================================================================
--- akregator/src/mk4storage/storagemk4impl.h	(.../tags/KDE/3.5.1/kdepim/akregator)	(revision 519586)
+++ akregator/src/mk4storage/storagemk4impl.h	(.../branches/KDE/3.5/kdepim/akregator)	(revision 519586)
@@ -112,6 +112,8 @@ class StorageMK4Impl : public Storage
         /** deletes all feed storages in this archive */
         virtual void clear();
         
+        virtual bool taggingEnabled() const;
+        
     protected slots:
         virtual void slotCommit();
         
Index: src/mk4storage/akregator_mk4storage_plugin.desktop
===================================================================
--- akregator/src/mk4storage/akregator_mk4storage_plugin.desktop	(.../tags/KDE/3.5.1/kdepim/akregator)	(revision 519586)
+++ akregator/src/mk4storage/akregator_mk4storage_plugin.desktop	(.../branches/KDE/3.5/kdepim/akregator)	(revision 519586)
@@ -2,6 +2,7 @@
 Type=Service
 Name=Metakit storage backend
 Name[bg]=Приставка за архивиране Metakit
+Name[ca]=Dorsal de emmagatzematge Metakit
 Name[cs]=Metakit úložiště
 Name[da]=Metakit lagringsgrænseflade
 Name[de]=Metakit Archiv-Modul
@@ -10,8 +11,11 @@ Name[es]=Dorsal de almacenamiento Metaki
 Name[et]=Metakiti salvestamisrakendus
 Name[eu]=Metakit-en biltegiratze euskarria
 Name[fr]=Stockage avec Metakit
+Name[hu]=Metakit tároló
 Name[is]=Metakit geymslu bakendi
 Name[it]=Backend archiviazione metakit
+Name[ja]=メタキットストレージバックエンド
+Name[km]=កម្មវិធី​ខាង​ក្រោយ​សម្រាប់​រក្សាទុក (Metakit)
 Name[nb]=Metakit lagringsbakstykke
 Name[nds]=Metakit-Archivmoduul
 Name[nl]=Metakit-opslagbackend
@@ -20,6 +24,7 @@ Name[pl]=System przechowywania Metakit
 Name[pt]=Infra-estrutura de armazenamento Metakit
 Name[pt_BR]=Mecanismo de armazenamento Metakit
 Name[ru]=Движок Metakit
+Name[sl]=Shranjevanje Metakit
 Name[sr]=Систем за смештање Мета комплета
 Name[sr@Latn]=Sistem za smeštanje Meta kompleta
 Name[sv]=Metakit lagringsgränssnitt
@@ -29,6 +34,7 @@ X-KDE-Library=libakregator_mk4storage_pl
 Comment=Plugin for Akregator
 Comment[bg]=Приставка за Akregator
 Comment[br]=Lugent evit Akregator
+Comment[ca]=Endollable per a Akregator
 Comment[cs]=Modul pro Akregator
 Comment[de]=Modul für Akregator
 Comment[el]=Πρόσθετο για το Akregator
@@ -38,9 +44,11 @@ Comment[eu]=Akregator-en plugina
 Comment[fr]=Module pour Akregator
 Comment[ga]=Breiseán Akregator
 Comment[he]=תוסף עבור Akregator
+Comment[hu]=Akregator bővítőmodul
 Comment[is]=Íforrit fyrir Akregator
 Comment[it]=Plugin per Akregator
 Comment[ja]=Akregator 用プラグイン
+Comment[km]=កម្មវិធី​ជំនួយ Akregator
 Comment[lt]=Akregator skirtas priedas
 Comment[nb]=Programtillegg for Akregator
 Comment[nds]=Moduul för Akregator
Index: src/mk4storage/feedstoragemk4impl.cpp
===================================================================
--- akregator/src/mk4storage/feedstoragemk4impl.cpp	(.../tags/KDE/3.5.1/kdepim/akregator)	(revision 519586)
+++ akregator/src/mk4storage/feedstoragemk4impl.cpp	(.../branches/KDE/3.5/kdepim/akregator)	(revision 519586)
@@ -81,7 +81,8 @@ class FeedStorageMK4Impl::FeedStorageMK4
         c4_Storage* tagStorage;
         c4_View tagView;
         bool autoCommit;
-	    bool modified;
+        bool modified;
+        bool taggingEnabled;
         bool convert;
         QString oldArchivePath;
         c4_StringProp pguid, ptitle, pdescription, plink, pcommentsLink, ptag, pEnclosureType, pEnclosureUrl, pcatTerm, pcatScheme, pcatName;
@@ -133,7 +134,8 @@ FeedStorageMK4Impl::FeedStorageMK4Impl(c
     d->autoCommit = main->autoCommit();
     d->url = url;
     d->mainStorage = main;
-
+    d->taggingEnabled = main->taggingEnabled();
+    
     QString url2 = url;
 
     if (url.length() > 255)
@@ -146,6 +148,7 @@ FeedStorageMK4Impl::FeedStorageMK4Impl(c
     QString t2 = url2;
     QString filePath = main->archivePath() +"/"+ t.replace("/", "_").replace(":", "_");
     d->oldArchivePath = KGlobal::dirs()->saveLocation("data", "akregator/Archive/") + t2.replace("/", "_").replace(":", "_") + ".xml";
+
     d->convert = !QFile::exists(filePath + ".mk4") && QFile::exists(d->oldArchivePath);
     d->storage = new c4_Storage((filePath + ".mk4").local8Bit(), true);
 
@@ -154,22 +157,26 @@ FeedStorageMK4Impl::FeedStorageMK4Impl(c
     c4_View hash = d->storage->GetAs("archiveHash[_H:I,_R:I]");
     d->archiveView = d->archiveView.Hash(hash, 1); // hash on guid
 
+    d->tagStorage = 0;
 
-    d->tagStorage = new c4_Storage((filePath + ".mk4___TAGS").local8Bit(), true);
-    d->tagView = d->tagStorage->GetAs("tagIndex[tag:S,taggedArticles[guid:S]]");
-    hash = d->tagStorage->GetAs("archiveHash[_H:I,_R:I]");
-    d->tagView = d->tagView.Hash(hash, 1); // hash on tag
-
-    d->catStorage = new c4_Storage((filePath + ".mk4___CATEGORIES").local8Bit(), true);
-    d->catView = d->catStorage->GetAs("catIndex[catTerm:S,catScheme:S,catName:S,categorizedArticles[guid:S]]");
+    if (d->taggingEnabled)
+    {
+        d->tagStorage = new c4_Storage((filePath + ".mk4___TAGS").local8Bit(), true);
+        d->tagView = d->tagStorage->GetAs("tagIndex[tag:S,taggedArticles[guid:S]]");
+        hash = d->tagStorage->GetAs("archiveHash[_H:I,_R:I]");
+        d->tagView = d->tagView.Hash(hash, 1); // hash on tag
+    }
+    //d->catStorage = new c4_Storage((filePath + ".mk4___CATEGORIES").local8Bit(), true);
+    //d->catView = d->catStorage->GetAs("catIndex[catTerm:S,catScheme:S,catName:S,categorizedArticles[guid:S]]");
 }
 
 
 FeedStorageMK4Impl::~FeedStorageMK4Impl()
 {
     delete d->storage;
-    delete d->tagStorage;
-    delete d->catStorage;
+    if (d->taggingEnabled)
+        delete d->tagStorage;
+//    delete d->catStorage;
     delete d; d = 0;
 }
 
@@ -178,8 +185,9 @@ void FeedStorageMK4Impl::commit()
     if (d->modified)
     {
         d->storage->Commit();
-        d->tagStorage->Commit();
-        d->catStorage->Commit();
+        if (d->taggingEnabled)
+            d->tagStorage->Commit();
+//        d->catStorage->Commit();
     }
     d->modified = false;
 }
@@ -187,8 +195,9 @@ void FeedStorageMK4Impl::commit()
 void FeedStorageMK4Impl::rollback()
 {
     d->storage->Rollback();
-    d->tagStorage->Rollback();
-    d->catStorage->Rollback();
+    if (d->taggingEnabled)
+        d->tagStorage->Rollback();
+//    d->catStorage->Rollback();
 }
 
 void FeedStorageMK4Impl::close()
@@ -234,7 +243,7 @@ QStringList FeedStorageMK4Impl::articles
         for (int i = 0; i < size; i++) // fill with guids
             list += QString(d->pguid(d->archiveView.GetAt(i)));
     }
-    else
+    else if (d->taggingEnabled)
     {
         c4_Row tagrow;
         d->ptag(tagrow) = tag.utf8().data();
@@ -255,7 +264,7 @@ QStringList FeedStorageMK4Impl::articles
 QStringList FeedStorageMK4Impl::articles(const Category& cat)
 {
     QStringList list;
-
+    /*
     c4_Row catrow;
     d->pcatTerm(catrow) = cat.term.utf8().data();
     d->pcatScheme(catrow) = cat.scheme.utf8().data();
@@ -269,7 +278,7 @@ QStringList FeedStorageMK4Impl::articles
         for (int i = 0; i < size; i++)
             list += QString(d->pguid(catView.GetAt(i)));
     }
-
+    */
     return list;
 }
 
@@ -514,12 +523,14 @@ void FeedStorageMK4Impl::setGuidIsPermaL
     d->modified = true;
 }
 
-void FeedStorageMK4Impl::addCategory(const QString& guid, const Category& cat)
+void FeedStorageMK4Impl::addCategory(const QString& /*guid*/, const Category& /*cat*/)
 {
+    return;
+    /*
     int findidx = findArticle(guid);
     if (findidx == -1)
         return;
-
+    
     c4_Row row;
     row = d->archiveView.GetAt(findidx);
     c4_View catView = d->pcategories(row);
@@ -565,13 +576,15 @@ void FeedStorageMK4Impl::addCategory(con
 
         d->modified = true;
     } 
+    */
 }
 
-QValueList<Category> FeedStorageMK4Impl::categories(const QString& guid)
+QValueList<Category> FeedStorageMK4Impl::categories(const QString& /*guid*/)
 {
 
     QValueList<Category> list;
-        
+    return list;
+    /*  
     if (!guid.isNull()) // return categories for an article
     {
         int findidx = findArticle(guid);
@@ -610,11 +623,14 @@ QValueList<Category> FeedStorageMK4Impl:
         }
     }
     
-    return list;
+    return list;*/
 }
 
 void FeedStorageMK4Impl::addTag(const QString& guid, const QString& tag)
 {
+    if (!d->taggingEnabled)
+        return;
+    
     int findidx = findArticle(guid);
     if (findidx == -1)
         return;
@@ -656,6 +672,9 @@ void FeedStorageMK4Impl::addTag(const QS
 
 void FeedStorageMK4Impl::removeTag(const QString& guid, const QString& tag)
 {
+    if (!d->taggingEnabled)
+        return;
+    
     int findidx = findArticle(guid);
     if (findidx == -1)
         return;
@@ -700,6 +719,9 @@ QStringList FeedStorageMK4Impl::tags(con
 {
     QStringList list;
     
+    if (!d->taggingEnabled)
+        return list;
+    
     if (!guid.isNull()) // return tags for an articles
     {
         int findidx = findArticle(guid);
@@ -807,7 +829,8 @@ void FeedStorageMK4Impl::enclosure(const
 void FeedStorageMK4Impl::clear()
 {
     d->storage->RemoveAll();
-    d->tagStorage->RemoveAll();
+    if (d->taggingEnabled)
+        d->tagStorage->RemoveAll();
     setUnread(0);
     d->modified = true;
 }
Index: src/akregator_part.cpp
===================================================================
--- akregator/src/akregator_part.cpp	(.../tags/KDE/3.5.1/kdepim/akregator)	(revision 519586)
+++ akregator/src/akregator_part.cpp	(.../branches/KDE/3.5/kdepim/akregator)	(revision 519586)
@@ -56,6 +56,13 @@
 #include <qwidgetlist.h>
 #include <private/qucomextra_p.h>
 
+#include <cerrno>
+#include <sys/types.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
 #include "aboutdata.h"
 #include "actionmanagerimpl.h"
 #include "akregator_part.h"
@@ -68,6 +75,7 @@
 #include "frame.h"
 #include "article.h"
 #include "kernel.h"
+#include "kcursorsaver.h"
 #include "notificationmanager.h"
 #include "pageviewer.h"
 #include "plugin.h"
@@ -115,10 +123,9 @@ Part::Part( QWidget *parentWidget, const
        , m_standardListLoaded(false)
        , m_shuttingDown(false)
        , m_mergedPart(0)
-       , m_backedUpList(false)
        , m_view(0)
+       , m_backedUpList(false)
        , m_storage(0)
-       
 {
     // we need an instance
     setInstance( AkregatorFactory::instance() );
@@ -136,12 +143,30 @@ Part::Part( QWidget *parentWidget, const
 
     m_storage = 0;
     Backend::StorageFactory* factory = Backend::StorageFactoryRegistry::self()->getFactory(Settings::archiveBackend());
+   
+    QStringList storageParams;
+    
+    storageParams.append(QString("taggingEnabled=%1").arg(Settings::showTaggingGUI() ? "true" : "false"));
+    
     if (factory != 0)
-        m_storage = factory->createStorage(QStringList());
+    {
+        if (factory->allowsMultipleWriteAccess())
+        {
+            m_storage = factory->createStorage(storageParams);
+        } 
+        else
+        {
+            if (tryToLock(factory->name()))
+                m_storage = factory->createStorage(storageParams);
+            else 
+                m_storage = dummyFactory->createStorage(storageParams);
+        }
+    }
+    
 
     if (!m_storage) // Houston, we have a problem
     {
-        m_storage = Backend::StorageFactoryRegistry::self()->getFactory("dummy")->createStorage(QStringList());
+        m_storage = Backend::StorageFactoryRegistry::self()->getFactory("dummy")->createStorage(storageParams);
 
         KMessageBox::error(parentWidget, i18n("Unable to load storage backend plugin \"%1\". No feeds are archived.").arg(Settings::archiveBackend()), i18n("Plugin error") );
     }
@@ -495,28 +520,35 @@ bool Part::mergePart(KParts::Part* part)
 
 QWidget* Part::getMainWindow()
 {
-        // this is a dirty fix to get the main window used for the tray icon
-
-        QWidgetList *l = kapp->topLevelWidgets();
-        QWidgetListIt it( *l );
-        QWidget *wid;
+    // this is a dirty fix to get the main window used for the tray icon
+    
+    QWidgetList *l = kapp->topLevelWidgets();
+    QWidgetListIt it( *l );
+    QWidget *wid;
 
-        // check if there is an akregator main window
-        while ( (wid = it.current()) != 0 )
-        {
+    // check if there is an akregator main window
+    while ( (wid = it.current()) != 0 )
+    {
         ++it;
         //kdDebug() << "win name: " << wid->name() << endl;
         if (QString(wid->name()) == "akregator_mainwindow")
+        {
+            delete l;
             return wid;
         }
-        // if not, check for kontact main window
-        QWidgetListIt it2( *l );
-        while ( (wid = it2.current()) != 0 )
-        {
-            ++it2;
-            if (QString(wid->name()).startsWith("kontact-mainwindow"))
-                return wid;
+    }
+    // if not, check for kontact main window
+    QWidgetListIt it2( *l );
+    while ( (wid = it2.current()) != 0 )
+    {
+        ++it2;
+        if (QString(wid->name()).startsWith("kontact-mainwindow"))
+        {
+            delete l;
+            return wid;
         }
+    }
+    delete l;
     return 0;
 }
 
@@ -871,6 +903,117 @@ bool Part::copyFile(const QString& backu
     return false;
 }
 
+static QString getMyHostName()
+{
+    char hostNameC[256];
+    // null terminate this C string
+    hostNameC[255] = 0;
+    // set the string to 0 length if gethostname fails
+    if(gethostname(hostNameC, 255))
+        hostNameC[0] = 0;
+    return QString::fromLocal8Bit(hostNameC);
+}
+
+// taken from KMail
+bool Part::tryToLock(const QString& backendName)
+{
+// Check and create a lock file to prevent concurrent access to metakit archive
+    QString appName = kapp->instanceName();
+    if ( appName.isEmpty() )
+        appName = "akregator";
+
+    QString programName;
+    const KAboutData *about = kapp->aboutData();
+    if ( about )
+        programName = about->programName();
+    if ( programName.isEmpty() )
+        programName = i18n("Akregator");
+
+    QString lockLocation = locateLocal("data", "akregator/lock");
+    KSimpleConfig config(lockLocation);
+    int oldPid = config.readNumEntry("pid", -1);
+    const QString oldHostName = config.readEntry("hostname");
+    const QString oldAppName = config.readEntry( "appName", appName );
+    const QString oldProgramName = config.readEntry( "programName", programName );
+    const QString hostName = getMyHostName();
+    bool first_instance = false;
+    if ( oldPid == -1 )
+        first_instance = true;
+  // check if the lock file is stale by trying to see if
+  // the other pid is currently running.
+  // Not 100% correct but better safe than sorry
+    else if (hostName == oldHostName && oldPid != getpid()) {
+        if ( kill(oldPid, 0) == -1 )
+            first_instance = ( errno == ESRCH );
+    }
+
+    if ( !first_instance )
+    {
+        QString msg;
+        if ( oldHostName == hostName ) 
+        {
+            // this can only happen if the user is running this application on
+            // different displays on the same machine. All other cases will be
+            // taken care of by KUniqueApplication()
+            if ( oldAppName == appName )
+                msg = i18n("<qt>%1 already seems to be running on another display on "
+                        "this machine. <b>Running %2 more than once is not supported "
+                        "by the %3 backend and "
+                        "can cause the loss of archived articles and crashes at startup.</b> "
+                        "You should disable the archive for now "
+                        "unless you are sure that %2 is not already running.</qt>")
+                        .arg( programName, programName, backendName );
+              // QString::arg( st ) only replaces the first occurrence of %1
+              // with st while QString::arg( s1, s2 ) replacess all occurrences
+              // of %1 with s1 and all occurrences of %2 with s2. So don't
+              // even think about changing the above to .arg( programName ).
+            else
+                msg = i18n("<qt>%1 seems to be running on another display on this "
+                        "machine. <b>Running %1 and %2 at the same "
+                        "time is not supported by the %3 backend and can cause "
+                        "the loss of archived articles and crashes at startup.</b> "
+                        "You should disable the archive for now "
+                        "unless you are sure that %2 is not already running.</qt>")
+                        .arg( oldProgramName, programName, backendName );
+        }
+        else
+        {
+            if ( oldAppName == appName )
+                msg = i18n("<qt>%1 already seems to be running on %2. <b>Running %1 more "
+                        "than once is not supported by the %3 backend and can cause "
+                        "the loss of archived articles and crashes at startup.</b> "
+                        "You should disable the archive for now "
+                        "unless you are sure that it is "
+                        "not already running on %2.</qt>")
+                        .arg( programName, oldHostName, backendName );
+            else
+                msg = i18n("<qt>%1 seems to be running on %3. <b>Running %1 and %2 at the "
+                        "same time is not supported by the %4 backend and can cause "
+                        "the loss of archived articles and crashes at startup.</b> "
+                        "You should disable the archive for now "
+                        "unless you are sure that %1 is "
+                        "not running on %3.</qt>")
+                        .arg( oldProgramName, programName, oldHostName, backendName );
+        }
+
+        KCursorSaver idle( KBusyPtr::idle() );
+        if ( KMessageBox::No ==
+             KMessageBox::warningYesNo( 0, msg, QString::null,
+                                        i18n("Force Access"),
+                                        i18n("Disable Archive")) )
+                                        {
+                                            return false;
+                                        }
+    }
+
+    config.writeEntry("pid", getpid());
+    config.writeEntry("hostname", hostName);
+    config.writeEntry( "appName", appName );
+    config.writeEntry( "programName", programName );
+    config.sync();
+    return true;
+}
+
 
 } // namespace Akregator
 #include "akregator_part.moc"
Index: src/pageviewer.cpp
===================================================================
--- akregator/src/pageviewer.cpp	(.../tags/KDE/3.5.1/kdepim/akregator)	(revision 519586)
+++ akregator/src/pageviewer.cpp	(.../branches/KDE/3.5/kdepim/akregator)	(revision 519586)
@@ -41,6 +41,7 @@
 #include <klocale.h>
 #include <kpopupmenu.h>
 #include <kstandarddirs.h>
+#include <kstdaccel.h>
 #include <kparts/browserinterface.h>
 
 #include <qclipboard.h>
@@ -103,8 +104,13 @@ PageViewer::PageViewer(QWidget *parent, 
     s->init(Settings::self()->config());
     
     setXMLFile(locate("data", "akregator/pageviewer.rc"), true);
-    
-    d->backAction = new KToolBarPopupAction(i18n("Back"), "back", "Alt+Left", this, SLOT(slotBack()), actionCollection(), "pageviewer_back");
+
+    QPair< KGuiItem, KGuiItem > backForward = KStdGuiItem::backAndForward();
+
+    d->backAction = new KToolBarPopupAction(backForward.first, 
+                                KStdAccel::shortcut(KStdAccel::Back), this, 
+                                SLOT(slotBack()), actionCollection(), 
+                                "pageviewer_back");
 
     connect(d->backAction->popupMenu(), SIGNAL(aboutToShow()),
             this, SLOT(slotBackAboutToShow()));
@@ -112,7 +118,10 @@ PageViewer::PageViewer(QWidget *parent, 
             this, SLOT(slotPopupActivated(int)));
 
     
-    d->forwardAction = new KToolBarPopupAction(i18n("Forward"), "forward", "Alt+Right", this, SLOT(slotForward()), actionCollection(), "pageviewer_forward");
+    d->forwardAction = new KToolBarPopupAction(backForward.second, 
+                                KStdAccel::shortcut(KStdAccel::Forward),this, 
+                                SLOT(slotForward()), actionCollection(), 
+                                "pageviewer_forward");
 
     connect(d->forwardAction->popupMenu(), SIGNAL(aboutToShow()),
             this, SLOT(slotForwardAboutToShow()));
@@ -122,7 +131,7 @@ PageViewer::PageViewer(QWidget *parent, 
     d->reloadAction = new KAction(i18n("Reload"), "reload", 0,
                             this, SLOT(slotReload()),
                             actionCollection(), "pageviewer_reload");
-    d->stopAction = new KAction(i18n("Stop"), "stop", 0,
+    d->stopAction = new KAction(KStdGuiItem::guiItem(KStdGuiItem::Stop), 0,
                                  this, SLOT(slotStop()),
                                  actionCollection(), "pageviewer_stop");
  
Index: src/storagefactorydummyimpl.h
===================================================================
--- akregator/src/storagefactorydummyimpl.h	(.../tags/KDE/3.5.1/kdepim/akregator)	(revision 519586)
+++ akregator/src/storagefactorydummyimpl.h	(.../branches/KDE/3.5/kdepim/akregator)	(revision 519586)
@@ -42,6 +42,7 @@ class StorageFactoryDummyImpl : public S
     virtual QString name() const;
     virtual void configure();
     virtual bool isConfigurable() const { return false; }
+    virtual bool allowsMultipleWriteAccess() const { return true; }
     virtual Storage* createStorage(const QStringList& params) const;
 };
 
Index: src/kcursorsaver.h
===================================================================
--- akregator/src/kcursorsaver.h	(.../tags/KDE/3.5.1/kdepim/akregator)	(revision 0)
+++ akregator/src/kcursorsaver.h	(.../branches/KDE/3.5/kdepim/akregator)	(revision 519586)
@@ -0,0 +1,67 @@
+// taken from kmail/, moved to Akregator namespace to avoid clashes
+
+#ifndef kcursorsaver_h
+#define kcursorsaver_h
+
+#include <qcursor.h>
+#include <qapplication.h>
+
+namespace Akregator {
+
+/**
+ * @short sets a cursor and makes sure it's restored on destruction
+ * Create a KCursorSaver object when you want to set the cursor.
+ * As soon as it gets out of scope, it will restore the original
+ * cursor.
+ */
+class KCursorSaver : public Qt
+{
+public:
+    /// constructor taking QCursor shapes
+    KCursorSaver(Qt::CursorShape shape) {
+        QApplication::setOverrideCursor( QCursor(shape) );
+        inited = true;
+    }
+
+    /// copy constructor. The right side won't restore the cursor
+    KCursorSaver( const KCursorSaver &rhs ) {
+        *this = rhs;
+    }
+
+    /// restore the cursor
+    ~KCursorSaver() {
+        if (inited)
+            QApplication::restoreOverrideCursor();
+    }
+
+    /// call this to explitly restore the cursor
+    inline void restoreCursor(void) {
+        QApplication::restoreOverrideCursor();
+        inited = false;
+    }
+
+protected:
+    void operator=( const KCursorSaver &rhs ) {
+        inited = rhs.inited;
+        rhs.inited = false;
+    }
+
+private:
+    mutable bool inited;
+};
+
+/**
+ * convenience functions
+ */
+namespace KBusyPtr {
+    inline KCursorSaver idle() {
+        return KCursorSaver(QCursor::ArrowCursor);
+    }
+    inline KCursorSaver busy() {
+        return KCursorSaver(QCursor::WaitCursor);
+    }
+}
+
+} // namespace Akregator
+
+#endif /*kbusyptr_h_*/
Index: src/aboutdata.h
===================================================================
--- akregator/src/aboutdata.h	(.../tags/KDE/3.5.1/kdepim/akregator)	(revision 519586)
+++ akregator/src/aboutdata.h	(.../branches/KDE/3.5/kdepim/akregator)	(revision 519586)
@@ -28,7 +28,7 @@
 #include <kaboutdata.h>
 #include <kdepimmacros.h>
 
-#define AKREGATOR_VERSION "1.2.1"
+#define AKREGATOR_VERSION "1.2.2"
 
 namespace Akregator {
 /**
Index: src/akregator_part.h
===================================================================
--- akregator/src/akregator_part.h	(.../tags/KDE/3.5.1/kdepim/akregator)	(revision 519586)
+++ akregator/src/akregator_part.h	(.../branches/KDE/3.5/kdepim/akregator)	(revision 519586)
@@ -195,6 +195,8 @@ namespace Akregator
             /** creates an OPML file containing the initial feeds (KDE feeds) */
             static QDomDocument createDefaultFeedList();
 
+            bool tryToLock(const QString& backendName);
+                    
          private: // attributes
 
             class ApplyFiltersInterceptor;
Index: src/akregator_plugin.desktop
===================================================================
--- akregator/src/akregator_plugin.desktop	(.../tags/KDE/3.5.1/kdepim/akregator)	(revision 519586)
+++ akregator/src/akregator_plugin.desktop	(.../branches/KDE/3.5/kdepim/akregator)	(revision 519586)
@@ -4,6 +4,7 @@ X-KDE-ServiceType=Akregator/Plugin
 Comment=Plugin for Akregator
 Comment[bg]=Приставка за Akregator
 Comment[br]=Lugent evit Akregator
+Comment[ca]=Endollable per a Akregator
 Comment[cs]=Modul pro Akregator
 Comment[de]=Modul für Akregator
 Comment[el]=Πρόσθετο για το Akregator
@@ -13,9 +14,11 @@ Comment[eu]=Akregator-en plugina
 Comment[fr]=Module pour Akregator
 Comment[ga]=Breiseán Akregator
 Comment[he]=תוסף עבור Akregator
+Comment[hu]=Akregator bővítőmodul
 Comment[is]=Íforrit fyrir Akregator
 Comment[it]=Plugin per Akregator
 Comment[ja]=Akregator 用プラグイン
+Comment[km]=កម្មវិធី​ជំនួយ Akregator
 Comment[lt]=Akregator skirtas priedas
 Comment[nb]=Programtillegg for Akregator
 Comment[nds]=Moduul för Akregator
Index: src/akregator.desktop
===================================================================
--- akregator/src/akregator.desktop	(.../tags/KDE/3.5.1/kdepim/akregator)	(revision 519586)
+++ akregator/src/akregator.desktop	(.../branches/KDE/3.5/kdepim/akregator)	(revision 519586)
@@ -22,6 +22,7 @@ GenericName[hu]=RSS hírolvasó
 GenericName[is]=RSS fréttaforrit
 GenericName[it]=Lettore Fonti RSS
 GenericName[ja]=RSS ニュースリーダ
+GenericName[km]=កម្មវិធី​អាន​មតិព័ត៌មាន RSS
 GenericName[lt]=RSS kanalų skaityklė
 GenericName[nb]=Leser for RSS-kanal
 GenericName[nds]=Kieker för RSS-Mellenströöm
@@ -31,13 +32,14 @@ GenericName[pl]=Program do przeglądania
 GenericName[pt]=Leitor de Fontes RSS
 GenericName[pt_BR]=Leitor de Fontes de Notícias RSS
 GenericName[ru]=Чтение лент новостей
-GenericName[sl]=Bralnik dovodov RSS
+GenericName[sl]=Bralnik virov RSS
 GenericName[sr]=Читач RSS довода
 GenericName[sr@Latn]=Čitač RSS dovoda
-GenericName[sv]=Läsare av RSS-källor
+GenericName[sv]=Läsare av RSS-kanaler
 GenericName[ta]=RSS பீஃட் வாசிப்பான்
 GenericName[tr]=RSS Haber Kaynağı Okuyucu
 GenericName[uk]=Програма для читання подач RSS
+GenericName[uz]=RSS янгиликларни ўқувчи
 GenericName[zh_CN]=RSS 种子阅读器
 Comment=An RSS Aggregator for KDE
 Comment[bg]=Четец на новости във формат RSS
@@ -51,10 +53,12 @@ Comment[et]=KDE RSS-kanalite lugemisvahe
 Comment[eu]=KDE-ren RSS gehitzailea
 Comment[fi]=RSS-syötelukija
 Comment[fr]=Un lecteur de flux RSS pour KDE
+Comment[ga]=Comhbhailitheoir RSS le haghaidh KDE
 Comment[hu]=KDE-s hírolvasó RSS hírcsatornákhoz
 Comment[is]=RSS fréttaforrit fyrir KDE
 Comment[it]=Un concentratore KDE per RSS
-Comment[ja]=KDE 用 RSS アグレゲータ
+Comment[ja]=KDE 用 RSS アグリゲータ
+Comment[km]=កម្មវិធី​អាន RSS សម្រាប់ KDE
 Comment[nb]=En RSS-oppsamler for KDE
 Comment[nds]=Tosamensteller för RSS-Mellenströöm vun KDE
 Comment[nl]=Een RSS-agregator voor KDE
@@ -70,6 +74,7 @@ Comment[sv]=En RSS-samlare för KDE
 Comment[ta]=கேடியிக்கான ஒரு RSS சேர்ப்பான்
 Comment[tr]=Bir KDE RSS Okuyucusu
 Comment[uk]=Агрегатор RSS для KDE
+Comment[uz]=KDE учун RSS янгиликларни ўқувчи
 Comment[zh_CN]=KDE RSS 新闻收集器
 Terminal=false
 Categories=Qt;KDE;Network;
Index: src/articlefilter.cpp
===================================================================
--- akregator/src/articlefilter.cpp	(.../tags/KDE/3.5.1/kdepim/akregator)	(revision 519586)
+++ akregator/src/articlefilter.cpp	(.../branches/KDE/3.5/kdepim/akregator)	(revision 519586)
@@ -219,6 +219,11 @@ ArticleMatcher::~ArticleMatcher()
 {
 }
 
+bool ArticleMatcher::matchesAll() const
+{
+    return m_criteria.isEmpty();
+}
+
 ArticleMatcher* ArticleMatcher::clone() const
 {
     return new ArticleMatcher(*this);
Index: src/eventsrc
===================================================================
--- akregator/src/eventsrc	(.../tags/KDE/3.5.1/kdepim/akregator)	(revision 519586)
+++ akregator/src/eventsrc	(.../branches/KDE/3.5/kdepim/akregator)	(revision 519586)
@@ -23,6 +23,7 @@ Name[hu]=Hírforrás felvéve
 Name[is]=Straum bætt við
 Name[it]=Aggiunta fonte
 Name[ja]=フィード追加
+Name[km]=បាន​បន្ថែម​មតិព័ត៌មាន
 Name[lt]=Kanalas pridėtas
 Name[nb]=Kanal lagt til
 Name[nds]=Narichtenstroom toföögt
@@ -32,19 +33,20 @@ Name[pl]=Kanał dodany
 Name[pt]=Fonte adicionada
 Name[pt_BR]=Fonte de notícias adicionada
 Name[ru]=Лента новостей добавлена
-Name[sl]=Dovod je dodan
+Name[sl]=Vir je dodan
 Name[sr]=Довод је додат
 Name[sr@Latn]=Dovod je dodat
-Name[sv]=Källa tillagd
+Name[sv]=Kanal tillagd
 Name[ta]=பீஃட் சேர்க்கப்பட்டது
 Name[tr]=Haber kaynağı eklendi
 Name[uk]=Подачу додано
+Name[uz]=Янгиликлар тасмаси қўшилди
 Name[zh_CN]=添加了新闻源
 Comment=A new feed was remotely added to Akregator
 Comment[bg]=Отдалечено е добавена нова новина към Akregator
 Comment[ca]=S'ha afegit remotament un enllaç a Akregator
 Comment[cs]=Byl přidán nový kanál do Akregatoru
-Comment[da]=En ny kilde blev tilføjet eksternet til Akregator
+Comment[da]=En ny kilde blev tilføjet eksternt til Akregator
 Comment[de]=Eine neue Nachrichtenquelle wurde von extern zu Akregator hinzugefügt
 Comment[el]=Μια νέα εισροή προστέθηκε απομακρυσμένα στο Akregator
 Comment[es]=Se ha añadido remotamente un origen a Akregator
@@ -56,6 +58,7 @@ Comment[hu]=Egy hírforrást távolról 
 Comment[is]=Nýjum straum var bætt við Akregator
 Comment[it]=Una nuova fonte è stata aggiunta ad Akregator da remoto
 Comment[ja]=新規フィードがリモートで Akregator に追加されました
+Comment[km]=បាន​បន្ថែម​មតិព័ត៌មាន​ថ្មី​មួយ​ពី​ចម្ងាយ​ទៅ Akregator
 Comment[lt]=Naujas nutolęs naujienų kanalas buvo įdėtas į Akregator
 Comment[nb]=En ny kanal ble lagt til Akgregator utenfra 
 Comment[nds]=En nieg Stroom wöör Akregator vun buten toföögt
@@ -65,13 +68,14 @@ Comment[pl]=Nowy kanał został zdalnie 
 Comment[pt]=Foi adicionada remotamente uma nova fonte ao Akregator
 Comment[pt_BR]=Uma nova fonte de notícias foi adicionada remotamente ao Akregator
 Comment[ru]=Новая лента новостей добавлена в список Akregator
-Comment[sl]=Nov dovod je bil oddaljeno dodan k Akregatorju
+Comment[sl]=Nov vir je bil oddaljeno dodan v Akregator
 Comment[sr]=Нови довод је удаљено додат у Akregator
 Comment[sr@Latn]=Novi dovod je udaljeno dodat u Akregator
-Comment[sv]=En ny källa har lagts till i aKregator utifrån
+Comment[sv]=En ny kanal har lagts till i aKregator utifrån
 Comment[ta]=Akregatorக்கு ஒரு புதிய உள்ளீடு சேர்க்கப்பட்டது
 Comment[tr]=Akregator'a yeni bir haber kaynağı eklendi
 Comment[uk]=Нову подачу було віддалено додано до Akregator
+Comment[uz]=Akregator дастурига янги янгиликлар тасмаси қўшилди
 Comment[zh_CN]=新闻源远程添加进了 Akregator
 default_presentation=4
 
@@ -95,6 +99,7 @@ Name[hu]=Hírekk
 Name[is]=Nýjar greinar
 Name[it]=Nuovi articoli
 Name[ja]=新規記事
+Name[km]=អត្ថបទ​ថ្មី
 Name[lt]=Nauji straipsniai
 Name[nb]=Nye artikler
 Name[nds]=Niege Artikeln
@@ -126,11 +131,13 @@ Comment[et]=Tõmmati uued artiklid
 Comment[eu]=Artikulu berriak eskuratu dira
 Comment[fi]=Uutta postia saapunut
 Comment[fr]=Les nouveaux articles ont été relevés
+Comment[ga]=Fuarthas ailt nua
 Comment[he]=הורדו מאמרים נוספים
 Comment[hu]=Új hírek lettek letöltve
 Comment[is]=Nýjar greinar voru sóttar
 Comment[it]=I nuovi articoli sono stati recuperati
-Comment[ja]=新規記事が取得されました
+Comment[ja]=新規記事を取得しました
+Comment[km]=បាន​ប្រមូល​អត្ថបទ​ថ្មី
 Comment[lt]=Nauji straipsniai parsiųsti
 Comment[nb]=Nye artikler ble hentet
 Comment[nds]=Niege Artikeln wöörn haalt
Index: src/akregator_view.cpp
===================================================================
--- akregator/src/akregator_view.cpp	(.../tags/KDE/3.5.1/kdepim/akregator)	(revision 519586)
+++ akregator/src/akregator_view.cpp	(.../branches/KDE/3.5/kdepim/akregator)	(revision 519586)
@@ -990,6 +990,9 @@ void View::slotFeedModify()
 
 void View::slotNextUnreadArticle()
 {
+    if (m_viewMode == CombinedView)
+        m_listTabWidget->activeView()->slotNextUnreadFeed();
+    
     TreeNode* sel = m_listTabWidget->activeView()->selectedNode();
     if (sel && sel->unread() > 0)
         m_articleList->slotNextUnreadArticle();
@@ -999,6 +1002,9 @@ void View::slotNextUnreadArticle()
 
 void View::slotPrevUnreadArticle()
 {
+    if (m_viewMode == CombinedView)
+        m_listTabWidget->activeView()->slotPrevUnreadFeed();
+    
     TreeNode* sel = m_listTabWidget->activeView()->selectedNode();
     if (sel && sel->unread() > 0)
         m_articleList->slotPreviousUnreadArticle();
