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