QGIS API Documentation 3.99.0-Master (2fe06baccd8)
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 Qt::DecorationRole:
75 if ( entry.image.isNull() )
76 return QVariant();
77 return entry.image;
78 }
79 return QVariant();
80}
81
82Qt::ItemFlags QgsNewsFeedModel::flags( const QModelIndex &index ) const
83{
84 Qt::ItemFlags flags = QAbstractItemModel::flags( index );
85 return flags;
86}
87
88QModelIndex QgsNewsFeedModel::index( int row, int column, const QModelIndex &parent ) const
89{
90 if ( !hasIndex( row, column, parent ) )
91 return QModelIndex();
92
93 if ( !parent.isValid() )
94 {
95 return createIndex( row, column );
96 }
97
98 return QModelIndex();
99}
100
101QModelIndex QgsNewsFeedModel::parent( const QModelIndex & ) const
102{
103 //all items are top level for now
104 return QModelIndex();
105}
106
107int QgsNewsFeedModel::rowCount( const QModelIndex &parent ) const
108{
109 if ( !parent.isValid() )
110 {
111 return mEntries.count();
112 }
113 return 0;
114}
115
116int QgsNewsFeedModel::columnCount( const QModelIndex & ) const
117{
118 return 1;
119}
120
121void QgsNewsFeedModel::onEntryAdded( const QgsNewsFeedParser::Entry &entry )
122{
123 beginInsertRows( QModelIndex(), mEntries.count(), mEntries.count() );
124 mEntries.append( entry );
125 endInsertRows();
126}
127
128void QgsNewsFeedModel::onEntryUpdated( const QgsNewsFeedParser::Entry &entry )
129{
130 for ( int idx = 0; idx < mEntries.count(); idx++ )
131 {
132 if ( mEntries.at( idx ).key == entry.key )
133 {
134 mEntries[ idx ] = entry;
135 emit dataChanged( index( idx, 0 ), index( idx, 0 ) );
136 break;
137 }
138 }
139}
140
141void QgsNewsFeedModel::onEntryRemoved( const QgsNewsFeedParser::Entry &entry )
142{
143 // find index of entry
144 const auto findIter = std::find_if( mEntries.begin(), mEntries.end(), [entry]( const QgsNewsFeedParser::Entry & candidate )
145 {
146 return candidate.key == entry.key;
147 } );
148 if ( findIter == mEntries.end() )
149 return;
150
151 const int entryIndex = static_cast< int >( std::distance( mEntries.begin(), findIter ) );
152 beginRemoveRows( QModelIndex(), entryIndex, entryIndex );
153 mEntries.removeAt( entryIndex );
154 endRemoveRows();
155}
156
157void QgsNewsFeedModel::onImageFetched( const int key, const QPixmap &pixmap )
158{
159 // find index of entry
160 const auto findIter = std::find_if( mEntries.begin(), mEntries.end(), [key]( const QgsNewsFeedParser::Entry & candidate )
161 {
162 return candidate.key == key;
163 } );
164 if ( findIter == mEntries.end() )
165 return;
166
167 const int entryIndex = static_cast< int >( std::distance( mEntries.begin(), findIter ) );
168 mEntries[ entryIndex ].image = pixmap;
169 emit dataChanged( index( entryIndex, 0, QModelIndex() ), index( entryIndex, 0, QModelIndex() ) );
170}
171
172
173//
174// QgsNewsFeedProxyModel
175//
176
178 : QSortFilterProxyModel( parent )
179{
180 mModel = new QgsNewsFeedModel( parser, this );
181 setSortCaseSensitivity( Qt::CaseInsensitive );
182 setSourceModel( mModel );
183 setDynamicSortFilter( true );
184 sort( 0 );
185}
186
187bool QgsNewsFeedProxyModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
188{
189 const bool leftSticky = sourceModel()->data( left, static_cast< int >( QgsNewsFeedModel::CustomRole::Sticky ) ).toBool();
190 const bool rightSticky = sourceModel()->data( right, static_cast< int >( QgsNewsFeedModel::CustomRole::Sticky ) ).toBool();
191
192 // sticky items come first
193 if ( leftSticky && !rightSticky )
194 return true;
195 if ( rightSticky && !leftSticky )
196 return false;
197
198 // else sort by descending key
199 const int leftKey = sourceModel()->data( left, static_cast< int >( QgsNewsFeedModel::CustomRole::Key ) ).toInt();
200 const int rightKey = sourceModel()->data( right, static_cast< int >( QgsNewsFeedModel::CustomRole::Key ) ).toInt();
201 return rightKey < leftKey;
202}
A model for published QGIS news feeds.
@ ImageUrl
Optional entry image URL.
@ 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...
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
QgsNewsFeedProxyModel(QgsNewsFeedParser *parser, QObject *parent=nullptr)
Constructor for QgsNewsFeedProxyModel, with the specified parent object.