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