QGIS API Documentation 3.41.0-Master (45a0abf3bec)
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#include "moc_qgsnewsfeedmodel.cpp"
18#include <QPainter>
19
20//
21// QgsNewsFeedModel
22//
23
25 : QAbstractItemModel( parent )
26 , mParser( parser )
27{
28 Q_ASSERT( mParser );
29 const QList< QgsNewsFeedParser::Entry > initialEntries = mParser->entries();
30 for ( const QgsNewsFeedParser::Entry &e : initialEntries )
31 onEntryAdded( e );
32
33 connect( mParser, &QgsNewsFeedParser::entryAdded, this, &QgsNewsFeedModel::onEntryAdded );
34 connect( mParser, &QgsNewsFeedParser::entryDismissed, this, &QgsNewsFeedModel::onEntryRemoved );
35 connect( mParser, &QgsNewsFeedParser::entryUpdated, this, &QgsNewsFeedModel::onEntryUpdated );
36 connect( mParser, &QgsNewsFeedParser::imageFetched, this, &QgsNewsFeedModel::onImageFetched );
37}
38
39QVariant QgsNewsFeedModel::data( const QModelIndex &index, int role ) const
40{
41 if ( index.row() < 0 || index.row() >= rowCount( QModelIndex() ) )
42 return QVariant();
43
44 const QgsNewsFeedParser::Entry &entry = mEntries.at( index.row() );
45
46 switch ( role )
47 {
48 case Qt::DisplayRole:
49 case static_cast< int >( CustomRole::Content ):
50 return entry.content;
51
52 case Qt::ToolTipRole:
53 case static_cast< int >( CustomRole::Title ):
54 return entry.title;
55
56 case static_cast< int >( CustomRole::Key ):
57 return entry.key;
58
59 case static_cast< int >( CustomRole::ImageUrl ):
60 return entry.imageUrl;
61
62 case static_cast< int >( CustomRole::Image ):
63 return entry.image;
64
65 case static_cast< int >( CustomRole::Link ):
66 return entry.link;
67
68 case static_cast< int >( CustomRole::Sticky ):
69 return entry.sticky;
70
71 case Qt::DecorationRole:
72 if ( entry.image.isNull() )
73 return QVariant();
74 return entry.image;
75 }
76 return QVariant();
77}
78
79Qt::ItemFlags QgsNewsFeedModel::flags( const QModelIndex &index ) const
80{
81 Qt::ItemFlags flags = QAbstractItemModel::flags( index );
82 return flags;
83}
84
85QModelIndex QgsNewsFeedModel::index( int row, int column, const QModelIndex &parent ) const
86{
87 if ( !hasIndex( row, column, parent ) )
88 return QModelIndex();
89
90 if ( !parent.isValid() )
91 {
92 return createIndex( row, column );
93 }
94
95 return QModelIndex();
96}
97
98QModelIndex QgsNewsFeedModel::parent( const QModelIndex & ) const
99{
100 //all items are top level for now
101 return QModelIndex();
102}
103
104int QgsNewsFeedModel::rowCount( const QModelIndex &parent ) const
105{
106 if ( !parent.isValid() )
107 {
108 return mEntries.count();
109 }
110 return 0;
111}
112
113int QgsNewsFeedModel::columnCount( const QModelIndex & ) const
114{
115 return 1;
116}
117
118void QgsNewsFeedModel::onEntryAdded( const QgsNewsFeedParser::Entry &entry )
119{
120 beginInsertRows( QModelIndex(), mEntries.count(), mEntries.count() );
121 mEntries.append( entry );
122 endInsertRows();
123}
124
125void QgsNewsFeedModel::onEntryUpdated( const QgsNewsFeedParser::Entry &entry )
126{
127 for ( int idx = 0; idx < mEntries.count(); idx++ )
128 {
129 if ( mEntries.at( idx ).key == entry.key )
130 {
131 mEntries[ idx ] = entry;
132 emit dataChanged( index( idx, 0 ), index( idx, 0 ) );
133 break;
134 }
135 }
136}
137
138void QgsNewsFeedModel::onEntryRemoved( const QgsNewsFeedParser::Entry &entry )
139{
140 // find index of entry
141 const auto findIter = std::find_if( mEntries.begin(), mEntries.end(), [entry]( const QgsNewsFeedParser::Entry & candidate )
142 {
143 return candidate.key == entry.key;
144 } );
145 if ( findIter == mEntries.end() )
146 return;
147
148 const int entryIndex = static_cast< int >( std::distance( mEntries.begin(), findIter ) );
149 beginRemoveRows( QModelIndex(), entryIndex, entryIndex );
150 mEntries.removeAt( entryIndex );
151 endRemoveRows();
152}
153
154void QgsNewsFeedModel::onImageFetched( const int key, const QPixmap &pixmap )
155{
156 // find index of entry
157 const auto findIter = std::find_if( mEntries.begin(), mEntries.end(), [key]( const QgsNewsFeedParser::Entry & candidate )
158 {
159 return candidate.key == key;
160 } );
161 if ( findIter == mEntries.end() )
162 return;
163
164 const int entryIndex = static_cast< int >( std::distance( mEntries.begin(), findIter ) );
165 mEntries[ entryIndex ].image = pixmap;
166 emit dataChanged( index( entryIndex, 0, QModelIndex() ), index( entryIndex, 0, QModelIndex() ) );
167}
168
169
170//
171// QgsNewsFeedProxyModel
172//
173
175 : QSortFilterProxyModel( parent )
176{
177 mModel = new QgsNewsFeedModel( parser, this );
178 setSortCaseSensitivity( Qt::CaseInsensitive );
179 setSourceModel( mModel );
180 setDynamicSortFilter( true );
181 sort( 0 );
182}
183
184bool QgsNewsFeedProxyModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
185{
186 const bool leftSticky = sourceModel()->data( left, static_cast< int >( QgsNewsFeedModel::CustomRole::Sticky ) ).toBool();
187 const bool rightSticky = sourceModel()->data( right, static_cast< int >( QgsNewsFeedModel::CustomRole::Sticky ) ).toBool();
188
189 // sticky items come first
190 if ( leftSticky && !rightSticky )
191 return true;
192 if ( rightSticky && !leftSticky )
193 return false;
194
195 // else sort by descending key
196 const int leftKey = sourceModel()->data( left, static_cast< int >( QgsNewsFeedModel::CustomRole::Key ) ).toInt();
197 const int rightKey = sourceModel()->data( right, static_cast< int >( QgsNewsFeedModel::CustomRole::Key ) ).toInt();
198 return rightKey < leftKey;
199}
A model for published QGIS news feeds.
@ ImageUrl
Optional entry image URL.
@ Key
Entry unique key.
@ Link
Optional entry URL link.
@ 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.
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.
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...
QList< QgsNewsFeedParser::Entry > entries() const
Returns a list of existing entries in the feed.
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
QgsNewsFeedProxyModel(QgsNewsFeedParser *parser, QObject *parent=nullptr)
Constructor for QgsNewsFeedProxyModel, with the specified parent object.