QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
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 
37 QVariant 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 
77 Qt::ItemFlags QgsNewsFeedModel::flags( const QModelIndex &index ) const
78 {
79  Qt::ItemFlags flags = QAbstractItemModel::flags( index );
80  return flags;
81 }
82 
83 QModelIndex 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 
96 QModelIndex QgsNewsFeedModel::parent( const QModelIndex & ) const
97 {
98  //all items are top level for now
99  return QModelIndex();
100 }
101 
102 int QgsNewsFeedModel::rowCount( const QModelIndex &parent ) const
103 {
104  if ( !parent.isValid() )
105  {
106  return mEntries.count();
107  }
108  return 0;
109 }
110 
111 int QgsNewsFeedModel::columnCount( const QModelIndex & ) const
112 {
113  return 1;
114 }
115 
116 void QgsNewsFeedModel::onEntryAdded( const QgsNewsFeedParser::Entry &entry )
117 {
118  beginInsertRows( QModelIndex(), mEntries.count(), mEntries.count() );
119  mEntries.append( entry );
120  endInsertRows();
121 }
122 
123 void QgsNewsFeedModel::onEntryRemoved( const QgsNewsFeedParser::Entry &entry )
124 {
125  // find index of entry
126  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 
139 void QgsNewsFeedModel::onImageFetched( const int key, const QPixmap &pixmap )
140 {
141  // find index of entry
142  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 
169 bool 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 }
int rowCount(const QModelIndex &parent=QModelIndex()) const override
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
QgsNewsFeedModel(QgsNewsFeedParser *parser, QObject *parent=nullptr)
Constructor for QgsNewsFeedModel, with the specified parent object.
Optional entry image.
void entryDismissed(const QgsNewsFeedParser::Entry &entry)
Emitted whenever an entry is dismissed (as a result of a call to dismissEntry()). ...
Represents a single entry from a news feed.
Whether entry is sticky.
Qt::ItemFlags flags(const QModelIndex &index) const override
QModelIndex parent(const QModelIndex &index) const override
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
void entryAdded(const QgsNewsFeedParser::Entry &entry)
Emitted whenever a new entry is available from the feed (as a result of a call to fetch())...
Optional entry URL link.
Parser for published QGIS news feeds.
bool sticky
true if entry is "sticky" and should always be shown at the top
QString content
HTML content of news entry.
Optional entry image URL.
QString title
Entry title.
Entry unique key.
QString imageUrl
Optional URL for image associated with entry.
int columnCount(const QModelIndex &parent=QModelIndex()) const override
int key
Unique entry identifier.
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...
QgsNewsFeedProxyModel(QgsNewsFeedParser *parser, QObject *parent=nullptr)
Constructor for QgsNewsFeedProxyModel, with the specified parent object.
A model for published QGIS news feeds.
QUrl link
Optional URL link for entry.
QList< QgsNewsFeedParser::Entry > entries() const
Returns a list of existing entries in the feed.
QVariant data(const QModelIndex &index, int role) const override
QPixmap image
Optional image data.