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 <klocale.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 00056 00057 virtual bool visitFeed(Feed* node) 00058 { 00059 m_list->idMap()->insert(node->id(), node); 00060 m_list->flatList()->append(node); 00061 return true; 00062 } 00063 00064 private: 00065 FeedList* m_list; 00066 }; 00067 00068 class FeedList::RemoveNodeVisitor : public TreeNodeVisitor 00069 { 00070 public: 00071 RemoveNodeVisitor(FeedList* list) : m_list(list) {} 00072 00073 virtual bool visitFeed(Feed* node) 00074 { 00075 m_list->d->urlMap[node->xmlUrl()].remove(node); 00076 return true; 00077 } 00078 00079 private: 00080 FeedList* m_list; 00081 }; 00082 00083 FeedList::FeedList(TQObject *parent, const char *name) 00084 : NodeList(parent, name), d(new FeedListPrivate) 00085 { 00086 d->addNodeVisitor = new AddNodeVisitor(this); 00087 d->removeNodeVisitor = new RemoveNodeVisitor(this); 00088 00089 Folder* rootNode = new Folder(i18n("All Feeds")); 00090 rootNode->setId(1); 00091 setRootNode(rootNode); 00092 addNode(rootNode, true); 00093 } 00094 00095 void FeedList::addNode(TreeNode* node, bool preserveID) 00096 { 00097 NodeList::addNode(node, preserveID); 00098 d->addNodeVisitor->visit(node); 00099 } 00100 00101 void FeedList::removeNode(TreeNode* node) 00102 { 00103 NodeList::removeNode(node); 00104 d->removeNodeVisitor->visit(node); 00105 } 00106 00107 void FeedList::parseChildNodes(TQDomNode &node, Folder* parent) 00108 { 00109 TQDomElement e = node.toElement(); // try to convert the node to an element. 00110 00111 if( !e.isNull() ) 00112 { 00113 TQString title = e.hasAttribute("text") ? e.attribute("text") : e.attribute("title"); 00114 00115 if (e.hasAttribute("xmlUrl") || e.hasAttribute("xmlurl") || e.hasAttribute("xmlURL") ) 00116 { 00117 Feed* feed = Feed::fromOPML(e); 00118 if (feed) 00119 { 00120 if (!d->urlMap[feed->xmlUrl()].contains(feed)) 00121 d->urlMap[feed->xmlUrl()].append(feed); 00122 parent->appendChild(feed); 00123 } 00124 } 00125 else 00126 { 00127 Folder* fg = Folder::fromOPML(e); 00128 parent->appendChild(fg); 00129 00130 if (e.hasChildNodes()) 00131 { 00132 TQDomNode child = e.firstChild(); 00133 while(!child.isNull()) 00134 { 00135 parseChildNodes(child, fg); 00136 child = child.nextSibling(); 00137 } 00138 } 00139 } 00140 } 00141 } 00142 00143 bool FeedList::readFromXML(const TQDomDocument& doc) 00144 { 00145 TQDomElement root = doc.documentElement(); 00146 00147 kdDebug() << "loading OPML feed " << root.tagName().lower() << endl; 00148 00149 kdDebug() << "measuring startup time: START" << endl; 00150 TQTime spent; 00151 spent.start(); 00152 00153 if (root.tagName().lower() != "opml") 00154 { 00155 return false; 00156 } 00157 TQDomNode bodyNode = root.firstChild(); 00158 00159 while (!bodyNode.isNull() && bodyNode.toElement().tagName().lower() != "body") 00160 bodyNode = bodyNode.nextSibling(); 00161 00162 00163 if (bodyNode.isNull()) 00164 { 00165 kdDebug() << "Failed to acquire body node, markup broken?" << endl; 00166 return false; 00167 } 00168 00169 TQDomElement body = bodyNode.toElement(); 00170 00171 TQDomNode i = body.firstChild(); 00172 00173 while( !i.isNull() ) 00174 { 00175 parseChildNodes(i, rootNode()); 00176 i = i.nextSibling(); 00177 } 00178 00179 for (TreeNode* i = rootNode()->firstChild(); i && i != rootNode(); i = i->next() ) 00180 if (i->id() == 0) 00181 { 00182 uint id = generateID(); 00183 i->setId(id); 00184 idMap()->insert(id, i); 00185 } 00186 00187 kdDebug() << "measuring startup time: STOP, " << spent.elapsed() << "ms" << endl; 00188 kdDebug() << "Number of articles loaded: " << rootNode()->totalCount() << endl; 00189 return true; 00190 } 00191 00192 FeedList::~FeedList() 00193 { 00194 emit signalDestroyed(this); 00195 setRootNode(0); 00196 delete d->addNodeVisitor; 00197 delete d->removeNodeVisitor; 00198 delete d; 00199 d = 0; 00200 } 00201 00202 Feed* FeedList::findByURL(const TQString& feedURL) const 00203 { 00204 if (d->urlMap[feedURL].isEmpty()) 00205 return 0; 00206 else 00207 return *(d->urlMap[feedURL].begin()); 00208 } 00209 00210 Article FeedList::findArticle(const TQString& feedURL, const TQString& guid) const 00211 { 00212 Feed* feed = findByURL(feedURL); 00213 00214 return feed ? feed->findArticle(guid) : Article(); 00215 } 00216 00217 void FeedList::append(FeedList* list, Folder* parent, TreeNode* after) 00218 { 00219 if ( list == this ) 00220 return; 00221 00222 if ( !flatList()->contains(parent) ) 00223 parent = rootNode(); 00224 00225 TQValueList<TreeNode*> children = list->rootNode()->children(); 00226 00227 TQValueList<TreeNode*>::ConstIterator end( children.end() ); 00228 for (TQValueList<TreeNode*>::ConstIterator it = children.begin(); it != end; ++it) 00229 { 00230 list->rootNode()->removeChild(*it); 00231 parent->insertChild(*it, after); 00232 after = *it; 00233 } 00234 } 00235 00236 TQDomDocument FeedList::toXML() const 00237 { 00238 TQDomDocument doc; 00239 doc.appendChild( doc.createProcessingInstruction( "xml", "version=\"1.0\" encoding=\"UTF-8\"" ) ); 00240 00241 TQDomElement root = doc.createElement( "opml" ); 00242 root.setAttribute( "version", "1.0" ); 00243 doc.appendChild( root ); 00244 00245 TQDomElement head = doc.createElement( "head" ); 00246 root.appendChild( head ); 00247 00248 TQDomElement ti = doc.createElement( "text" ); 00249 head.appendChild( ti ); 00250 00251 TQDomText t = doc.createTextNode( title() ); 00252 ti.appendChild( t ); 00253 00254 TQDomElement body = doc.createElement( "body" ); 00255 root.appendChild( body ); 00256 00257 TQValueList<TreeNode*> children = rootNode()->children(); 00258 00259 TQValueList<TreeNode*>::ConstIterator end( children.end() ); 00260 00261 for (TQValueList<TreeNode*>::ConstIterator it = children.begin(); it != end; ++it) 00262 body.appendChild( (*it)->toOPML(body, doc) ); 00263 00264 return doc; 00265 } 00266 00267 } // namespace Akregator 00268 #include "feedlist.moc"