QGIS API Documentation 4.1.0-Master (376402f9aeb)
Loading...
Searching...
No Matches
qgsnewsfeedmodel.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsnewsfeedmodel.cpp
3 -------------------
4 begin : July 2019
5 copyright : (C) 2019 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15#include "qgsnewsfeedmodel.h"
16
18
19#include <QPainter>
20
21#include "moc_qgsnewsfeedmodel.cpp"
22
23//
24// QgsNewsFeedModel
25//
26
28 : QAbstractItemModel( parent )
29 , mParser( parser )
30{
31 Q_ASSERT( mParser );
32 const QList< QgsNewsFeedParser::Entry > initialEntries = mParser->entries();
33 for ( const QgsNewsFeedParser::Entry &e : initialEntries )
34 onEntryAdded( e );
35
36 connect( mParser, &QgsNewsFeedParser::entryAdded, this, &QgsNewsFeedModel::onEntryAdded );
37 connect( mParser, &QgsNewsFeedParser::entryDismissed, this, &QgsNewsFeedModel::onEntryRemoved );
38 connect( mParser, &QgsNewsFeedParser::entryUpdated, this, &QgsNewsFeedModel::onEntryUpdated );
39 connect( mParser, &QgsNewsFeedParser::imageFetched, this, &QgsNewsFeedModel::onImageFetched );
40}
41
42QVariant QgsNewsFeedModel::data( const QModelIndex &index, int role ) const
43{
44 if ( index.row() < 0 || index.row() >= rowCount( QModelIndex() ) )
45 return QVariant();
46
47 const QgsNewsFeedParser::Entry &entry = mEntries.at( index.row() );
48
49 switch ( role )
50 {
51 case Qt::DisplayRole:
52 case static_cast< int >( CustomRole::Content ):
53 return entry.content;
54
55 case Qt::ToolTipRole:
56 case static_cast< int >( CustomRole::Title ):
57 return entry.title;
58
59 case static_cast< int >( CustomRole::Key ):
60 return entry.key;
61
62 case static_cast< int >( CustomRole::ImageUrl ):
63 return entry.imageUrl;
64
65 case static_cast< int >( CustomRole::Image ):
66 return entry.image;
67
68 case static_cast< int >( CustomRole::Link ):
69 return entry.link;
70
71 case static_cast< int >( CustomRole::Sticky ):
72 return entry.sticky;
73
74 case static_cast< int >( CustomRole::Published ):
75 return entry.published;
76
77 case Qt::DecorationRole:
78 if ( entry.image.isNull() )
79 return QVariant();
80 return entry.image;
81 }
82 return QVariant();
83}
84
85QHash<int, QByteArray> QgsNewsFeedModel::roleNames() const
86{
87 QHash<int, QByteArray> roles;
88 roles[static_cast< int >( CustomRole::Key )] = "Key";
89 roles[static_cast< int >( CustomRole::Title )] = "Title";
90 roles[static_cast< int >( CustomRole::Content )] = "Content";
91 roles[static_cast< int >( CustomRole::ImageUrl )] = "ImageUrl";
92 roles[static_cast< int >( CustomRole::Link )] = "Link";
93 roles[static_cast< int >( CustomRole::Sticky )] = "Sticky";
94 roles[static_cast< int >( CustomRole::Published )] = "Published";
95 return roles;
96}
97
98Qt::ItemFlags QgsNewsFeedModel::flags( const QModelIndex &index ) const
99{
100 Qt::ItemFlags flags = QAbstractItemModel::flags( index );
101 return flags;
102}
103
104QModelIndex QgsNewsFeedModel::index( int row, int column, const QModelIndex &parent ) const
105{
106 if ( !hasIndex( row, column, parent ) )
107 return QModelIndex();
108
109 if ( !parent.isValid() )
110 {
111 return createIndex( row, column );
112 }
113
114 return QModelIndex();
115}
116
117QModelIndex QgsNewsFeedModel::parent( const QModelIndex & ) const
118{
119 //all items are top level for now
120 return QModelIndex();
121}
122
123int QgsNewsFeedModel::rowCount( const QModelIndex &parent ) const
124{
125 if ( !parent.isValid() )
126 {
127 return mEntries.count();
128 }
129 return 0;
130}
131
132int QgsNewsFeedModel::columnCount( const QModelIndex & ) const
133{
134 return 1;
135}
136
137void QgsNewsFeedModel::onEntryAdded( const QgsNewsFeedParser::Entry &entry )
138{
139 beginInsertRows( QModelIndex(), mEntries.count(), mEntries.count() );
140 mEntries.append( entry );
141 endInsertRows();
142}
143
144void QgsNewsFeedModel::onEntryUpdated( const QgsNewsFeedParser::Entry &entry )
145{
146 for ( int idx = 0; idx < mEntries.count(); idx++ )
147 {
148 if ( mEntries.at( idx ).key == entry.key )
149 {
150 mEntries[idx] = entry;
151 emit dataChanged( index( idx, 0 ), index( idx, 0 ) );
152 break;
153 }
154 }
155}
156
157void QgsNewsFeedModel::onEntryRemoved( const QgsNewsFeedParser::Entry &entry )
158{
159 // find index of entry
160 const auto findIter = std::find_if( mEntries.begin(), mEntries.end(), [entry]( const QgsNewsFeedParser::Entry &candidate ) { return candidate.key == entry.key; } );
161 if ( findIter == mEntries.end() )
162 return;
163
164 const int entryIndex = static_cast< int >( std::distance( mEntries.begin(), findIter ) );
165 beginRemoveRows( QModelIndex(), entryIndex, entryIndex );
166 mEntries.removeAt( entryIndex );
167 endRemoveRows();
168}
169
170void QgsNewsFeedModel::onImageFetched( const int key, const QPixmap &pixmap )
171{
172 // find index of entry
173 const auto findIter = std::find_if( mEntries.begin(), mEntries.end(), [key]( const QgsNewsFeedParser::Entry &candidate ) { return candidate.key == key; } );
174 if ( findIter == mEntries.end() )
175 return;
176
177 const int entryIndex = static_cast< int >( std::distance( mEntries.begin(), findIter ) );
178 mEntries[entryIndex].image = pixmap;
179 emit dataChanged( index( entryIndex, 0, QModelIndex() ), index( entryIndex, 0, QModelIndex() ) );
180}
181
182
183//
184// QgsNewsFeedProxyModel
185//
186
188 : QSortFilterProxyModel( parent )
189{
190 mModel = new QgsNewsFeedModel( parser, this );
191 setSortCaseSensitivity( Qt::CaseInsensitive );
192 setSourceModel( mModel );
193 setDynamicSortFilter( true );
194 sort( 0 );
195}
196
197bool QgsNewsFeedProxyModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
198{
199 const bool leftSticky = sourceModel()->data( left, static_cast< int >( QgsNewsFeedModel::CustomRole::Sticky ) ).toBool();
200 const bool rightSticky = sourceModel()->data( right, static_cast< int >( QgsNewsFeedModel::CustomRole::Sticky ) ).toBool();
201
202 // sticky items come first
203 if ( leftSticky && !rightSticky )
204 return true;
205 if ( rightSticky && !leftSticky )
206 return false;
207
208 // else sort by descending publication date
209 const QDateTime leftPublished = sourceModel()->data( left, static_cast< int >( QgsNewsFeedModel::CustomRole::Published ) ).toDateTime();
210 const QDateTime rightPublished = sourceModel()->data( right, static_cast< int >( QgsNewsFeedModel::CustomRole::Published ) ).toDateTime();
211 return rightPublished < leftPublished;
212}
A model for published QGIS news feeds.
@ ImageUrl
Optional entry image URL.
@ Link
Optional entry URL link.
@ Published
Entry publication date.
@ Image
Optional entry image.
@ Sticky
Whether entry is sticky.
int columnCount(const QModelIndex &parent=QModelIndex()) const override
QVariant data(const QModelIndex &index, int role) const override
Qt::ItemFlags flags(const QModelIndex &index) const override
QgsNewsFeedModel(QgsNewsFeedParser *parser, QObject *parent=nullptr)
Constructor for QgsNewsFeedModel, with the specified parent object.
QHash< int, QByteArray > roleNames() const override
QModelIndex parent(const QModelIndex &index) const override
int rowCount(const QModelIndex &parent=QModelIndex()) const override
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
Represents a single entry from a news feed.
QString content
HTML content of news entry.
QDateTime published
Entry publication date.
bool sticky
true if entry is "sticky" and should always be shown at the top
QUrl link
Optional URL link for entry.
QString imageUrl
Optional URL for image associated with entry.
QPixmap image
Optional image data.
int key
Unique entry identifier.
QString title
Entry title.
Parser for published QGIS news feeds.
void entryDismissed(const QgsNewsFeedParser::Entry &entry)
Emitted whenever an entry is dismissed (as a result of a call to dismissEntry()).
void entryUpdated(const QgsNewsFeedParser::Entry &entry)
Emitted whenever an existing entry is available from the feed (as a result of a call to fetch()).
void entryAdded(const QgsNewsFeedParser::Entry &entry)
Emitted whenever a new entry is available from the feed (as a result of a call to fetch()).
void imageFetched(int key, const QPixmap &pixmap)
Emitted when the image attached to the entry with the specified key has been fetched and is now avail...
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
QgsNewsFeedProxyModel(QgsNewsFeedParser *parser, QObject *parent=nullptr)
Constructor for QgsNewsFeedProxyModel, with the specified parent object.