00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "articlemodel.h"
00025
00026 #include "article.h"
00027 #include "articlematcher.h"
00028 #include "akregatorconfig.h"
00029 #include "treenode.h"
00030 #include "feed.h"
00031
00032 #include <syndication/tools.h>
00033
00034 #include <QString>
00035 #include <QVector>
00036
00037 #include <KLocale>
00038 #include <KGlobal>
00039
00040 #include <cassert>
00041 #include <cmath>
00042
00043 using namespace Akregator;
00044
00045 class ArticleModel::Private {
00046 private:
00047 ArticleModel* const q;
00048 public:
00049 Private( TreeNode* node_, ArticleModel* qq );
00050 Akregator::TreeNode* node;
00051 QList<Akregator::Article> articles;
00052 QVector<QString> titleCache;
00053
00054 void nodeDestroyed();
00055 void articlesAdded( TreeNode*, const QList<Article>& );
00056 void articlesRemoved( TreeNode*, const QList<Article>& );
00057 void articlesUpdated( TreeNode*, const QList<Article>& );
00058
00059 };
00060
00061 ArticleModel::Private::Private( TreeNode* node_, ArticleModel* qq )
00062 : q( qq ), node( node_ )
00063 {
00064 Q_ASSERT( node );
00065 articles = node->articles();
00066 titleCache.resize( articles.count() );
00067 for ( int i = 0; i < articles.count(); ++i )
00068 titleCache[i] = Syndication::htmlToPlainText( articles[i].title() );
00069 connect( node, SIGNAL( destroyed() ) , q, SLOT( nodeDestroyed() ) );
00070 connect( node, SIGNAL( signalArticlesAdded( Akregator::TreeNode*, QList<Akregator::Article> ) ),
00071 q, SLOT( articlesAdded( Akregator::TreeNode*, QList<Akregator::Article> ) ) );
00072 connect( node, SIGNAL( signalArticlesRemoved( Akregator::TreeNode*, QList<Akregator::Article> ) ),
00073 q, SLOT( articlesRemoved( Akregator::TreeNode*, QList<Akregator::Article> ) ) );
00074 connect( node, SIGNAL( signalArticlesUpdated( Akregator::TreeNode*, QList<Akregator::Article> ) ),
00075 q, SLOT( articlesUpdated( Akregator::TreeNode*, QList<Akregator::Article> ) ) );
00076
00077 }
00078
00079 Akregator::ArticleModel::ArticleModel(TreeNode* node, QObject* parent) : QAbstractTableModel( parent ), d( new Private( node, this ) )
00080 {
00081 }
00082
00083 Akregator::ArticleModel::~ArticleModel()
00084 {
00085 delete d;
00086 }
00087
00088 int Akregator::ArticleModel::columnCount( const QModelIndex& parent ) const
00089 {
00090 return parent.isValid() ? 0 : ColumnCount;
00091 }
00092
00093 int Akregator::ArticleModel::rowCount( const QModelIndex& parent ) const
00094 {
00095 return parent.isValid() ? 0 : d->articles.count();
00096 }
00097
00098 QVariant Akregator::ArticleModel::headerData( int section, Qt::Orientation, int role ) const
00099 {
00100 if ( role != Qt::DisplayRole )
00101 return QVariant();
00102
00103 switch (section)
00104 {
00105 case ItemTitleColumn:
00106 return i18n("Title");
00107 case FeedTitleColumn:
00108 return i18n("Feed");
00109 case DateColumn:
00110 return i18n("Date");
00111 case AuthorColumn:
00112 return i18n("Author");
00113 case DescriptionColumn:
00114 return i18n("Description");
00115 case ContentColumn:
00116 return i18n("Content");
00117 }
00118
00119 return QVariant();
00120 }
00121
00122 QVariant Akregator::ArticleModel::data( const QModelIndex& index, int role ) const
00123 {
00124 if ( !index.isValid() || index.row() < 0 || index.row() >= d->articles.count() )
00125 return QVariant();
00126 const int row = index.row();
00127 const Akregator::Article& article( d->articles[row] );
00128
00129 if ( article.isNull() )
00130 return QVariant();
00131
00132 switch ( role )
00133 {
00134 case SortRole:
00135 if ( index.column() == DateColumn )
00136 return article.pubDate();
00137
00138 case Qt::DisplayRole:
00139 {
00140 switch ( index.column() )
00141 {
00142 case FeedTitleColumn:
00143 return article.feed() ? article.feed()->title() : QVariant();
00144 case DateColumn:
00145 return KGlobal::locale()->formatDateTime(article.pubDate(),
00146 KLocale::FancyShortDate );
00147 case ItemTitleColumn:
00148 return d->titleCache[row];
00149 case AuthorColumn:
00150 return article.authorShort();
00151 case DescriptionColumn:
00152 case ContentColumn:
00153 return article.description();
00154 }
00155 }
00156 case LinkRole:
00157 {
00158 return article.link();
00159 }
00160 case ItemIdRole:
00161 case GuidRole:
00162 {
00163 return article.guid();
00164 }
00165 case FeedIdRole:
00166 {
00167 return article.feed() ? article.feed()->xmlUrl() : QVariant();
00168 }
00169 case StatusRole:
00170 {
00171 return article.status();
00172 }
00173 case IsImportantRole:
00174 {
00175 return article.keep();
00176 }
00177 case IsDeletedRole:
00178 {
00179 return article.isDeleted();
00180 }
00181 }
00182
00183 return QVariant();
00184 }
00185
00186 void Akregator::ArticleModel::Private::nodeDestroyed()
00187 {
00188 node = 0;
00189 articles.clear();
00190 q->reset();
00191 }
00192
00193 void ArticleModel::Private::articlesAdded( TreeNode* node, const QList<Article>& list )
00194 {
00195 Q_UNUSED( node );
00196 if ( list.isEmpty() )
00197 return;
00198 const int first = static_cast<int>( articles.count() );
00199 q->beginInsertRows( QModelIndex(), first, first + list.size() - 1 );
00200
00201 const int oldSize = articles.size();
00202 articles << list;
00203 titleCache.resize( articles.count() );
00204 for ( int i = oldSize; i < articles.count(); ++i )
00205 titleCache[i] = Syndication::htmlToPlainText( articles[i].title() );
00206 q->endInsertRows();
00207 }
00208
00209 void ArticleModel::Private::articlesRemoved( TreeNode* node, const QList<Article>& list )
00210 {
00211 Q_UNUSED( node );
00212
00213 Q_FOREACH ( const Article& i, list )
00214 {
00215 const int row = articles.indexOf( i );
00216 assert( row != -1 );
00217 q->removeRow( row, QModelIndex() );
00218 }
00219 }
00220
00221
00222 void ArticleModel::Private::articlesUpdated( TreeNode* node, const QList<Article>& list )
00223 {
00224 Q_UNUSED( node );
00225 int rmin = 0;
00226 int rmax = 0;
00227
00228 if ( articles.count() > 0 )
00229 {
00230 rmin = articles.count() - 1;
00231
00232 Q_FOREACH ( const Article& i, list )
00233 {
00234 const int row = articles.indexOf( i );
00235
00236
00237 if ( row >= 0 )
00238 {
00239 titleCache[row] = Syndication::htmlToPlainText( articles[row].title() );
00240 rmin = std::min( row, rmin );
00241 rmax = std::max( row, rmax );
00242 }
00243 }
00244 }
00245 emit q->dataChanged( q->index( rmin, 0 ), q->index( rmax, ColumnCount-1 ) );
00246 }
00247
00248
00249 bool ArticleModel::rowMatches( int row, const boost::shared_ptr<const Akregator::Filters::AbstractMatcher>& matcher ) const
00250 {
00251 assert( matcher );
00252 return matcher->matches( article( row ) );
00253 }
00254
00255 Article ArticleModel::article( int row ) const
00256 {
00257 if ( row < 0 || row >= d->articles.count() )
00258 return Article();
00259 return d->articles[row];
00260 }
00261
00262 #include "articlemodel.moc"