feedlist.cpp
00001 /* 00002 This file is part of Akregator. 00003 00004 Copyright (C) 2004 Frank Osterfeld <frank.osterfeld at kdemail.net> 00005 00006 This program is free software; you can redistribute it and/or modify 00007 it under the terms of the GNU General Public License as published by 00008 the Free Software Foundation; either version 2 of the License, or 00009 (at your option) any later version. 00010 00011 This program is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 GNU General Public License for more details. 00015 00016 You should have received a copy of the GNU General Public License 00017 along with this program; if not, write to the Free Software 00018 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00019 00020 As a special exception, permission is given to link this program 00021 with any edition of TQt, and distribute the resulting executable, 00022 without including the source code for TQt in the source distribution. 00023 */ 00024 #include "feedlist.h" 00025 00026 #include <tqdatetime.h> 00027 #include <tqdom.h> 00028 #include <tqmap.h> 00029 #include <tqvaluelist.h> 00030 00031 #include <kdebug.h> 00032 #include <tdelocale.h> 00033 00034 #include "article.h" 00035 #include "feed.h" 00036 #include "folder.h" 00037 #include "treenode.h" 00038 #include "treenodevisitor.h" 00039 00040 namespace Akregator { 00041 00042 class FeedList::FeedListPrivate 00043 { 00044 public: 00045 00046 TQMap<TQString, TQValueList<Feed*> > urlMap; 00047 AddNodeVisitor* addNodeVisitor; 00048 RemoveNodeVisitor* removeNodeVisitor; 00049 }; 00050 00051 class FeedList::AddNodeVisitor : public TreeNodeVisitor 00052 { 00053 public: 00054 AddNodeVisitor(FeedList* list) : m_list(list) {} 00055 virtual ~AddNodeVisitor() {} 00056 00057 00058 virtual bool visitFeed(Feed* node) 00059 { 00060 m_list->idMap()->insert(node->id(), node); 00061 m_list->flatList()->append(node); 00062 return true; 00063 } 00064 00065 private: 00066 FeedList* m_list; 00067 }; 00068 00069 class FeedList::RemoveNodeVisitor : public TreeNodeVisitor 00070 { 00071 public: 00072 RemoveNodeVisitor(FeedList* list) : m_list(list) {} 00073 virtual ~RemoveNodeVisitor() {} 00074 00075 virtual bool visitFeed(Feed* node) 00076 { 00077 m_list->d->urlMap[node->xmlUrl()].remove(node); 00078 return true; 00079 } 00080 00081 private: 00082 FeedList* m_list; 00083 }; 00084 00085 FeedList::FeedList(TQObject *parent, const char *name) 00086 : NodeList(parent, name), d(new FeedListPrivate) 00087 { 00088 d->addNodeVisitor = new AddNodeVisitor(this); 00089 d->removeNodeVisitor = new RemoveNodeVisitor(this); 00090 00091 Folder* rootNode = new Folder(i18n("All Feeds")); 00092 rootNode->setId(1); 00093 setRootNode(rootNode); 00094 addNode(rootNode, true); 00095 } 00096 00097 void FeedList::addNode(TreeNode* node, bool preserveID) 00098 { 00099 NodeList::addNode(node, preserveID); 00100 d->addNodeVisitor->visit(node); 00101 } 00102 00103 void FeedList::removeNode(TreeNode* node) 00104 { 00105 NodeList::removeNode(node); 00106 d->removeNodeVisitor->visit(node); 00107 } 00108 00109 void FeedList::parseChildNodes(TQDomNode &node, Folder* parent) 00110 { 00111 TQDomElement e = node.toElement(); // try to convert the node to an element. 00112 00113 if( !e.isNull() ) 00114 { 00115 TQString title = e.hasAttribute("text") ? e.attribute("text") : e.attribute("title"); 00116 00117 if (e.hasAttribute("xmlUrl") || e.hasAttribute("xmlurl") || e.hasAttribute("xmlURL") ) 00118 { 00119 Feed* feed = Feed::fromOPML(e); 00120 if (feed) 00121 { 00122 if (!d->urlMap[feed->xmlUrl()].contains(feed)) 00123 d->urlMap[feed->xmlUrl()].append(feed); 00124 parent->appendChild(feed); 00125 } 00126 } 00127 else 00128 { 00129 Folder* fg = Folder::fromOPML(e); 00130 parent->appendChild(fg); 00131 00132 if (e.hasChildNodes()) 00133 { 00134 TQDomNode child = e.firstChild(); 00135 while(!child.isNull()) 00136 { 00137 parseChildNodes(child, fg); 00138 child = child.nextSibling(); 00139 } 00140 } 00141 } 00142 } 00143 } 00144 00145 bool FeedList::readFromXML(const TQDomDocument& doc) 00146 { 00147 TQDomElement root = doc.documentElement(); 00148 00149 kdDebug() << "loading OPML feed " << root.tagName().lower() << endl; 00150 00151 kdDebug() << "measuring startup time: START" << endl; 00152 TQTime spent; 00153 spent.start(); 00154 00155 if (root.tagName().lower() != "opml") 00156 { 00157 return false; 00158 } 00159 TQDomNode bodyNode = root.firstChild(); 00160 00161 while (!bodyNode.isNull() && bodyNode.toElement().tagName().lower() != "body") 00162 bodyNode = bodyNode.nextSibling(); 00163 00164 00165 if (bodyNode.isNull()) 00166 { 00167 kdDebug() << "Failed to acquire body node, markup broken?" << endl; 00168 return false; 00169 } 00170 00171 TQDomElement body = bodyNode.toElement(); 00172 00173 TQDomNode i = body.firstChild(); 00174 00175 while( !i.isNull() ) 00176 { 00177 parseChildNodes(i, rootNode()); 00178 i = i.nextSibling(); 00179 } 00180 00181 for (TreeNode* i = rootNode()->firstChild(); i && i != rootNode(); i = i->next() ) 00182 if (i->id() == 0) 00183 { 00184 uint id = generateID(); 00185 i->setId(id); 00186 idMap()->insert(id, i); 00187 } 00188 00189 kdDebug() << "measuring startup time: STOP, " << spent.elapsed() << "ms" << endl; 00190 kdDebug() << "Number of articles loaded: " << rootNode()->totalCount() << endl; 00191 return true; 00192 } 00193 00194 FeedList::~FeedList() 00195 { 00196 emit signalDestroyed(this); 00197 setRootNode(0); 00198 delete d->addNodeVisitor; 00199 delete d->removeNodeVisitor; 00200 delete d; 00201 d = 0; 00202 } 00203 00204 Feed* FeedList::findByURL(const TQString& feedURL) const 00205 { 00206 if (d->urlMap[feedURL].isEmpty()) 00207 return 0; 00208 else 00209 return *(d->urlMap[feedURL].begin()); 00210 } 00211 00212 Article FeedList::findArticle(const TQString& feedURL, const TQString& guid) const 00213 { 00214 Feed* feed = findByURL(feedURL); 00215 00216 return feed ? feed->findArticle(guid) : Article(); 00217 } 00218 00219 void FeedList::append(FeedList* list, Folder* parent, TreeNode* after) 00220 { 00221 if ( list == this ) 00222 return; 00223 00224 if ( !flatList()->contains(parent) ) 00225 parent = rootNode(); 00226 00227 TQValueList<TreeNode*> children = list->rootNode()->children(); 00228 00229 TQValueList<TreeNode*>::ConstIterator end( children.end() ); 00230 for (TQValueList<TreeNode*>::ConstIterator it = children.begin(); it != end; ++it) 00231 { 00232 list->rootNode()->removeChild(*it); 00233 parent->insertChild(*it, after); 00234 after = *it; 00235 } 00236 } 00237 00238 TQDomDocument FeedList::toXML() const 00239 { 00240 TQDomDocument doc; 00241 doc.appendChild( doc.createProcessingInstruction( "xml", "version=\"1.0\" encoding=\"UTF-8\"" ) ); 00242 00243 TQDomElement root = doc.createElement( "opml" ); 00244 root.setAttribute( "version", "1.0" ); 00245 doc.appendChild( root ); 00246 00247 TQDomElement head = doc.createElement( "head" ); 00248 root.appendChild( head ); 00249 00250 TQDomElement ti = doc.createElement( "text" ); 00251 head.appendChild( ti ); 00252 00253 TQDomText t = doc.createTextNode( title() ); 00254 ti.appendChild( t ); 00255 00256 TQDomElement body = doc.createElement( "body" ); 00257 root.appendChild( body ); 00258 00259 TQValueList<TreeNode*> children = rootNode()->children(); 00260 00261 TQValueList<TreeNode*>::ConstIterator end( children.end() ); 00262 00263 for (TQValueList<TreeNode*>::ConstIterator it = children.begin(); it != end; ++it) 00264 body.appendChild( (*it)->toOPML(body, doc) ); 00265 00266 return doc; 00267 } 00268 00269 } // namespace Akregator 00270 #include "feedlist.moc"