1 /***************************************************************************
2 qgsfeaturelistmodel.cpp
3 ---------------------
4 begin : February 2013
5 copyright : (C) 2013 by Matthias Kuhn
6 email : matthias at opengis dot ch
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 "qgsexception.h"
16 #include "qgsvectordataprovider.h"
17 #include "qgsfeaturelistmodel.h"
18 #include "qgsattributetablemodel.h"
19 #include "qgsvectorlayereditbuffer.h"
20 #include "qgsattributetablefiltermodel.h"
21 #include "qgsapplication.h"
22 #include "qgsvectorlayercache.h"
23
24 #include <QItemSelection>
25 #include <QSettings>
26
QgsFeatureListModel(QgsAttributeTableFilterModel * sourceModel,QObject * parent)27 QgsFeatureListModel::QgsFeatureListModel( QgsAttributeTableFilterModel *sourceModel, QObject *parent )
28 : QSortFilterProxyModel( parent )
29 {
30 setSourceModel( sourceModel );
31 }
32
setSourceModel(QgsAttributeTableFilterModel * sourceModel)33 void QgsFeatureListModel::setSourceModel( QgsAttributeTableFilterModel *sourceModel )
34 {
35 if ( mSourceLayer )
36 disconnect( mSourceLayer->conditionalStyles(), &QgsConditionalLayerStyles::changed, this, &QgsFeatureListModel::conditionalStylesChanged );
37
38 QSortFilterProxyModel::setSourceModel( sourceModel );
39 mExpressionContext = sourceModel->layer()->createExpressionContext();
40 mFilterModel = sourceModel;
41
42 mSourceLayer = sourceModel->layer();
43 connect( mSourceLayer->conditionalStyles(), &QgsConditionalLayerStyles::changed, this, &QgsFeatureListModel::conditionalStylesChanged );
44 }
45
layerCache()46 QgsVectorLayerCache *QgsFeatureListModel::layerCache()
47 {
48 return mFilterModel->layerCache();
49 }
50
idxToFid(const QModelIndex & index) const51 QgsFeatureId QgsFeatureListModel::idxToFid( const QModelIndex &index ) const
52 {
53 return mFilterModel->masterModel()->rowToId( mapToMaster( index ).row() );
54 }
55
fidToIdx(const QgsFeatureId fid) const56 QModelIndex QgsFeatureListModel::fidToIdx( const QgsFeatureId fid ) const
57 {
58 return mapFromMaster( mFilterModel->masterModel()->idToIndex( fid ) );
59 }
60
data(const QModelIndex & index,int role) const61 QVariant QgsFeatureListModel::data( const QModelIndex &index, int role ) const
62 {
63 if ( mInjectNull && index.row() == 0 )
64 {
65 if ( role == Qt::DisplayRole )
66 {
67 return QgsApplication::nullRepresentation();
68 }
69 else
70 {
71 return QVariant( QVariant::Invalid );
72 }
73 }
74
75 if ( role == Qt::DisplayRole || role == Qt::EditRole )
76 {
77 QgsFeature feat;
78
79 mFilterModel->layerCache()->featureAtId( idxToFid( index ), feat );
80
81 mExpressionContext.setFeature( feat );
82 return mDisplayExpression.evaluate( &mExpressionContext );
83 }
84
85 if ( role == FeatureInfoRole )
86 {
87 FeatureInfo featInfo;
88
89 QgsFeature feat;
90
91 mFilterModel->layerCache()->featureAtId( idxToFid( index ), feat );
92
93 QgsVectorLayerEditBuffer *editBuffer = mFilterModel->layer()->editBuffer();
94
95 if ( editBuffer )
96 {
97 if ( editBuffer->isFeatureAdded( feat.id() ) )
98 {
99 featInfo.isNew = true;
100 }
101 if ( editBuffer->isFeatureAttributesChanged( feat.id() ) )
102 {
103 featInfo.isEdited = true;
104 }
105 }
106
107 return QVariant::fromValue( featInfo );
108 }
109 else if ( role == FeatureRole )
110 {
111 QgsFeature feat;
112
113 mFilterModel->layerCache()->featureAtId( idxToFid( index ), feat );
114
115 return QVariant::fromValue( feat );
116 }
117 else if ( role == Qt::TextAlignmentRole )
118 {
119 return static_cast<Qt::Alignment::Int>( Qt::AlignLeft );
120 }
121
122 #if QT_VERSION < QT_VERSION_CHECK(5, 13, 0)
123 if ( role == Qt::BackgroundColorRole
124 || role == Qt::TextColorRole
125 #else
126 if ( role == Qt::BackgroundRole
127 || role == Qt::ForegroundRole
128 #endif
129 || role == Qt::DecorationRole
130 || role == Qt::FontRole )
131 {
132 QgsVectorLayer *layer = mFilterModel->layer();
133 QgsFeature feat;
134 const QgsFeatureId fid = idxToFid( index );
135 mFilterModel->layerCache()->featureAtId( fid, feat );
136 mExpressionContext.setFeature( feat );
137 QList<QgsConditionalStyle> styles;
138
139 if ( mRowStylesMap.contains( fid ) )
140 {
141 styles = mRowStylesMap.value( fid );
142 }
143 else
144 {
145 styles = QgsConditionalStyle::matchingConditionalStyles( layer->conditionalStyles()->rowStyles(), QVariant(), mExpressionContext );
146 mRowStylesMap.insert( fid, styles );
147 }
148
149 const QgsConditionalStyle rowstyle = QgsConditionalStyle::compressStyles( styles );
150
151 if ( mDisplayExpression.isField() )
152 {
153 const QString fieldName = *mDisplayExpression.referencedColumns().constBegin();
154 styles = layer->conditionalStyles()->fieldStyles( fieldName );
155 styles = QgsConditionalStyle::matchingConditionalStyles( styles, feat.attribute( fieldName ), mExpressionContext );
156 }
157
158 styles.insert( 0, rowstyle );
159
160 const QgsConditionalStyle style = QgsConditionalStyle::compressStyles( styles );
161
162 if ( style.isValid() )
163 {
164 #if QT_VERSION < QT_VERSION_CHECK(5, 13, 0)
165 if ( role == Qt::BackgroundColorRole && style.validBackgroundColor() )
166 #else
167 if ( role == Qt::BackgroundRole && style.validBackgroundColor() )
168 #endif
169 return style.backgroundColor().isValid() ? style.backgroundColor() : QVariant();
170 #if QT_VERSION < QT_VERSION_CHECK(5, 13, 0)
171 if ( role == Qt::TextColorRole && style.validTextColor() )
172 #else
173 if ( role == Qt::ForegroundRole && style.validTextColor() )
174 #endif
175 return style.textColor().isValid() ? style.textColor() : QVariant();
176 if ( role == Qt::DecorationRole )
177 return style.icon().isNull() ? QVariant() : style.icon();
178 if ( role == Qt::FontRole )
179 return style.font();
180 }
181
182 return QVariant();
183 }
184
185 return sourceModel()->data( mapToSource( index ), role );
186 }
187
flags(const QModelIndex & index) const188 Qt::ItemFlags QgsFeatureListModel::flags( const QModelIndex &index ) const
189 {
190 if ( mInjectNull && index.row() == 0 )
191 {
192 return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
193 }
194 else
195 {
196 return sourceModel()->flags( mapToSource( index ) ) & ~Qt::ItemIsEditable;
197 }
198 }
199
setInjectNull(bool injectNull)200 void QgsFeatureListModel::setInjectNull( bool injectNull )
201 {
202 if ( mInjectNull == injectNull )
203 return;
204
205 if ( injectNull )
206 setSortByDisplayExpression( false );
207
208 beginResetModel();
209 mInjectNull = injectNull;
210 endResetModel();
211 }
212
injectNull()213 bool QgsFeatureListModel::injectNull()
214 {
215 return mInjectNull;
216 }
217
masterModel()218 QgsAttributeTableModel *QgsFeatureListModel::masterModel()
219 {
220 return mFilterModel->masterModel();
221 }
222
setDisplayExpression(const QString & expression)223 bool QgsFeatureListModel::setDisplayExpression( const QString &expression )
224 {
225 QgsExpression exp = QgsExpression( expression );
226
227 exp.prepare( &mExpressionContext );
228
229 if ( exp.hasParserError() )
230 {
231 mParserErrorString = exp.parserErrorString();
232 return false;
233 }
234
235 mDisplayExpression = exp;
236
237 if ( mSortByDisplayExpression )
238 masterModel()->prefetchSortData( expression, 1 );
239
240 emit dataChanged( index( 0, 0 ), index( rowCount() - 1, 0 ) );
241
242 invalidate();
243 return true;
244 }
245
parserErrorString()246 QString QgsFeatureListModel::parserErrorString()
247 {
248 return mParserErrorString;
249 }
250
displayExpression() const251 QString QgsFeatureListModel::displayExpression() const
252 {
253 return mDisplayExpression.expression();
254 }
255
featureByIndex(const QModelIndex & index,QgsFeature & feat)256 bool QgsFeatureListModel::featureByIndex( const QModelIndex &index, QgsFeature &feat )
257 {
258 return mFilterModel->layerCache()->featureAtId( idxToFid( index ), feat );
259 }
260
onBeginRemoveRows(const QModelIndex & parent,int first,int last)261 void QgsFeatureListModel::onBeginRemoveRows( const QModelIndex &parent, int first, int last )
262 {
263 beginRemoveRows( parent, first, last );
264 }
265
onEndRemoveRows(const QModelIndex & parent,int first,int last)266 void QgsFeatureListModel::onEndRemoveRows( const QModelIndex &parent, int first, int last )
267 {
268 Q_UNUSED( parent )
269 Q_UNUSED( first )
270 Q_UNUSED( last )
271 endRemoveRows();
272 }
273
onBeginInsertRows(const QModelIndex & parent,int first,int last)274 void QgsFeatureListModel::onBeginInsertRows( const QModelIndex &parent, int first, int last )
275 {
276 beginInsertRows( parent, first, last );
277 }
278
onEndInsertRows(const QModelIndex & parent,int first,int last)279 void QgsFeatureListModel::onEndInsertRows( const QModelIndex &parent, int first, int last )
280 {
281 Q_UNUSED( parent )
282 Q_UNUSED( first )
283 Q_UNUSED( last )
284 endInsertRows();
285 }
286
conditionalStylesChanged()287 void QgsFeatureListModel::conditionalStylesChanged()
288 {
289 mRowStylesMap.clear();
290 emit dataChanged( index( 0, 0 ), index( rowCount() - 1, columnCount() - 1 ) );
291 }
292
sortByDisplayExpression() const293 bool QgsFeatureListModel::sortByDisplayExpression() const
294 {
295 return mSortByDisplayExpression;
296 }
297
setSortByDisplayExpression(bool sortByDisplayExpression,Qt::SortOrder order)298 void QgsFeatureListModel::setSortByDisplayExpression( bool sortByDisplayExpression, Qt::SortOrder order )
299 {
300 mSortByDisplayExpression = sortByDisplayExpression;
301
302 // If we are sorting by display expression, we do not support injected null
303 if ( mSortByDisplayExpression )
304 setInjectNull( false );
305
306 setSortRole( QgsAttributeTableModel::SortRole + 1 );
307 setDynamicSortFilter( mSortByDisplayExpression );
308 sort( 0, order );
309 }
310
mapToMaster(const QModelIndex & proxyIndex) const311 QModelIndex QgsFeatureListModel::mapToMaster( const QModelIndex &proxyIndex ) const
312 {
313 QModelIndex masterIndex;
314
315 if ( proxyIndex.isValid() )
316 {
317 if ( mSortByDisplayExpression )
318 {
319 masterIndex = mFilterModel->mapToMaster( mapToSource( proxyIndex ) );
320 }
321 else
322 {
323 const int offset = mInjectNull ? 1 : 0;
324
325 masterIndex = mFilterModel->mapToMaster( mFilterModel->index( proxyIndex.row() - offset, proxyIndex.column() ) );
326 }
327 }
328 return masterIndex;
329 }
330
mapFromMaster(const QModelIndex & masterIndex) const331 QModelIndex QgsFeatureListModel::mapFromMaster( const QModelIndex &masterIndex ) const
332 {
333 QModelIndex proxyIndex;
334
335 if ( masterIndex.isValid() )
336 {
337 if ( mSortByDisplayExpression )
338 {
339 proxyIndex = mapFromSource( mFilterModel->mapFromMaster( masterIndex ) );
340 }
341 else
342 {
343 const int offset = mInjectNull ? 1 : 0;
344
345 return createIndex( mFilterModel->mapFromMaster( masterIndex ).row() + offset, 0 );
346 }
347 }
348
349 return proxyIndex;
350 }
351
mapSelectionFromMaster(const QItemSelection & selection) const352 QItemSelection QgsFeatureListModel::mapSelectionFromMaster( const QItemSelection &selection ) const
353 {
354 return mapSelectionFromSource( mFilterModel->mapSelectionFromSource( selection ) );
355 }
356
mapSelectionToMaster(const QItemSelection & selection) const357 QItemSelection QgsFeatureListModel::mapSelectionToMaster( const QItemSelection &selection ) const
358 {
359 return mFilterModel->mapSelectionToSource( mapSelectionToSource( selection ) );
360 }
361
362 // Override some methods from QAbstractProxyModel, not that interesting
363
mapToSource(const QModelIndex & proxyIndex) const364 QModelIndex QgsFeatureListModel::mapToSource( const QModelIndex &proxyIndex ) const
365 {
366 QModelIndex sourceIndex;
367
368 if ( mSortByDisplayExpression )
369 {
370 sourceIndex = QSortFilterProxyModel::mapToSource( proxyIndex );
371 }
372 else
373 {
374 if ( !proxyIndex.isValid() )
375 return QModelIndex();
376
377 const int offset = mInjectNull ? 1 : 0;
378
379 sourceIndex = sourceModel()->index( proxyIndex.row() - offset, proxyIndex.column() );
380 }
381
382 return sourceIndex;
383 }
384
mapFromSource(const QModelIndex & sourceIndex) const385 QModelIndex QgsFeatureListModel::mapFromSource( const QModelIndex &sourceIndex ) const
386 {
387 QModelIndex proxyIndex;
388
389 if ( mSortByDisplayExpression )
390 {
391 proxyIndex = QSortFilterProxyModel::mapFromSource( sourceIndex );
392 }
393 else
394 {
395 if ( sourceIndex.isValid() )
396 proxyIndex = createIndex( sourceIndex.row(), 0 );
397 }
398
399 return proxyIndex;
400 }
401
parent(const QModelIndex & child) const402 QModelIndex QgsFeatureListModel::parent( const QModelIndex &child ) const
403 {
404 Q_UNUSED( child )
405 return QModelIndex();
406 }
407
columnCount(const QModelIndex & parent) const408 int QgsFeatureListModel::columnCount( const QModelIndex &parent ) const
409 {
410 Q_UNUSED( parent )
411 return 1;
412 }
413
rowCount(const QModelIndex & parent) const414 int QgsFeatureListModel::rowCount( const QModelIndex &parent ) const
415 {
416 Q_UNUSED( parent )
417
418 const int offset = mInjectNull ? 1 : 0;
419
420 return sourceModel()->rowCount() + offset;
421 }
422
fidToIndex(QgsFeatureId fid)423 QModelIndex QgsFeatureListModel::fidToIndex( QgsFeatureId fid )
424 {
425 return mapFromMaster( masterModel()->idToIndex( fid ) );
426 }
427
fidToIndexList(QgsFeatureId fid)428 QModelIndexList QgsFeatureListModel::fidToIndexList( QgsFeatureId fid )
429 {
430 return QModelIndexList() << fidToIndex( fid );
431 }
432