1 /***************************************************************************
2    qgsfieldmodel.cpp
3     --------------------------------------
4    Date                 : 01.04.2014
5    Copyright            : (C) 2014 Denis Rouzaud
6    Email                : denis.rouzaud@gmail.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 
16 #include <QFont>
17 #include <QIcon>
18 
19 #include "qgsfieldmodel.h"
20 #include "qgsmaplayermodel.h"
21 #include "qgsmaplayerproxymodel.h"
22 #include "qgslogger.h"
23 #include "qgsapplication.h"
24 #include "qgsvectorlayer.h"
25 #include "qgsvectorlayerjoinbuffer.h"
26 
QgsFieldModel(QObject * parent)27 QgsFieldModel::QgsFieldModel( QObject *parent )
28   : QAbstractItemModel( parent )
29 {
30 }
31 
indexFromName(const QString & fieldName)32 QModelIndex QgsFieldModel::indexFromName( const QString &fieldName )
33 {
34   QString fldName( fieldName ); // we may need a copy
35 
36   // only non-empty names should be used here, as by default all fields
37   // have no aliases set and calling key() fill return just first value
38   // from the aliases map
39   if ( mLayer && !fldName.isEmpty() )
40   {
41     // the name could be an alias
42     // it would be better to have "display name" directly in QgsFields
43     // rather than having to consult layer in various places in code!
44     const QString fieldNameWithAlias = mLayer->attributeAliases().key( fldName );
45     if ( !fieldNameWithAlias.isNull() )
46       fldName = fieldNameWithAlias;
47   }
48 
49   if ( mAllowEmpty && fieldName.isEmpty() )
50     return index( 0, 0 );
51 
52   int r = mFields.lookupField( fldName );
53   if ( r >= 0 )
54   {
55     if ( mAllowEmpty )
56       r++;
57 
58     QModelIndex idx = index( r, 0 );
59     if ( idx.isValid() )
60     {
61       return idx;
62     }
63   }
64 
65   if ( mAllowExpression )
66   {
67     const int exprIdx = mExpression.indexOf( fldName );
68     if ( exprIdx != -1 )
69     {
70       return index( ( mAllowEmpty ? 1 : 0 ) + mFields.count() + exprIdx, 0 );
71     }
72   }
73 
74   return QModelIndex();
75 }
76 
isField(const QString & expression) const77 bool QgsFieldModel::isField( const QString &expression ) const
78 {
79   const int index = mFields.indexFromName( expression );
80   return index >= 0;
81 }
82 
setLayer(QgsVectorLayer * layer)83 void QgsFieldModel::setLayer( QgsVectorLayer *layer )
84 {
85   if ( mLayer )
86   {
87     disconnect( mLayer, &QgsVectorLayer::updatedFields, this, &QgsFieldModel::updateModel );
88     disconnect( mLayer, &QObject::destroyed, this, &QgsFieldModel::layerDeleted );
89   }
90 
91   mLayer = layer;
92 
93   if ( mLayer )
94   {
95     connect( mLayer, &QgsVectorLayer::updatedFields, this, &QgsFieldModel::updateModel );
96     connect( mLayer, &QObject::destroyed, this, &QgsFieldModel::layerDeleted );
97   }
98 
99   updateModel();
100 }
101 
layerDeleted()102 void QgsFieldModel::layerDeleted()
103 {
104   mLayer = nullptr;
105   updateModel();
106 }
107 
updateModel()108 void QgsFieldModel::updateModel()
109 {
110   const int offset = mAllowEmpty ? 1 : 0;
111   if ( mLayer )
112   {
113     const QgsFields newFields = mLayer->fields();
114     if ( mFields.toList() != newFields.toList() )
115     {
116       // Try to handle two special cases: addition of a new field and removal of a field.
117       // It would be better to listen directly to attributeAdded/attributeDeleted
118       // so we would not have to check for addition/removal here.
119 
120       if ( mFields.count() == newFields.count() - 1 )
121       {
122         QgsFields tmpNewFields = newFields;
123         tmpNewFields.remove( tmpNewFields.count() - 1 );
124         if ( mFields.toList() == tmpNewFields.toList() )
125         {
126           // the only change is a new field at the end
127           beginInsertRows( QModelIndex(), mFields.count() + offset, mFields.count() + offset );
128           mFields = newFields;
129           endInsertRows();
130           return;
131         }
132       }
133 
134       if ( mFields.count() == newFields.count() + 1 )
135       {
136         QgsFields tmpOldFields = mFields;
137         tmpOldFields.remove( tmpOldFields.count() - 1 );
138         if ( tmpOldFields.toList() == newFields.toList() )
139         {
140           // the only change is a field removed at the end
141           beginRemoveRows( QModelIndex(), mFields.count() - 1 + offset, mFields.count() - 1 + offset );
142           mFields = newFields;
143           endRemoveRows();
144           return;
145         }
146 
147         for ( int i = 0; i < newFields.count(); ++i )
148         {
149           if ( mFields.at( i ) != newFields.at( i ) )
150           {
151             QgsFields tmpOldFields = mFields;
152             tmpOldFields.remove( i );
153             if ( tmpOldFields.toList() != newFields.toList() )
154               break; // the change is more complex - go with general case
155 
156             // the only change is a field removed at index i
157             beginRemoveRows( QModelIndex(), i + offset, i + offset );
158             mFields = newFields;
159             endRemoveRows();
160             return;
161           }
162         }
163       }
164 
165       // general case with reset - not good - resets selections
166       beginResetModel();
167       mFields = mLayer->fields();
168       endResetModel();
169     }
170     else
171       emit dataChanged( index( 0 + offset, 0 ), index( rowCount(), 0 ) );
172   }
173   else
174   {
175     beginResetModel();
176     mFields = QgsFields();
177     endResetModel();
178   }
179 }
180 
setAllowExpression(bool allowExpression)181 void QgsFieldModel::setAllowExpression( bool allowExpression )
182 {
183   if ( allowExpression == mAllowExpression )
184     return;
185 
186   mAllowExpression = allowExpression;
187 
188   if ( !mAllowExpression )
189   {
190     const int start = mFields.count();
191     const int end = start + mExpression.count() - 1;
192     beginRemoveRows( QModelIndex(), start, end );
193     mExpression = QList<QString>();
194     endRemoveRows();
195   }
196 }
197 
setAllowEmptyFieldName(bool allowEmpty)198 void QgsFieldModel::setAllowEmptyFieldName( bool allowEmpty )
199 {
200   if ( allowEmpty == mAllowEmpty )
201     return;
202 
203   if ( allowEmpty )
204   {
205     beginInsertRows( QModelIndex(), 0, 0 );
206     mAllowEmpty = true;
207     endInsertRows();
208   }
209   else
210   {
211     beginRemoveRows( QModelIndex(), 0, 0 );
212     mAllowEmpty = false;
213     endRemoveRows();
214   }
215 }
216 
217 
setExpression(const QString & expression)218 void QgsFieldModel::setExpression( const QString &expression )
219 {
220   if ( !mAllowExpression )
221     return;
222 
223   const QModelIndex idx = indexFromName( expression );
224   if ( idx.isValid() )
225     return;
226 
227   beginResetModel();
228   mExpression = QList<QString>();
229   if ( !expression.isEmpty() )
230     mExpression << expression;
231   endResetModel();
232 }
233 
removeExpression()234 void QgsFieldModel::removeExpression()
235 {
236   beginResetModel();
237   mExpression = QList<QString>();
238   endResetModel();
239 }
240 
index(int row,int column,const QModelIndex & parent) const241 QModelIndex QgsFieldModel::index( int row, int column, const QModelIndex &parent ) const
242 {
243   if ( hasIndex( row, column, parent ) )
244   {
245     return createIndex( row, column, row );
246   }
247 
248   return QModelIndex();
249 }
250 
parent(const QModelIndex & child) const251 QModelIndex QgsFieldModel::parent( const QModelIndex &child ) const
252 {
253   Q_UNUSED( child )
254   return QModelIndex();
255 }
256 
rowCount(const QModelIndex & parent) const257 int QgsFieldModel::rowCount( const QModelIndex &parent ) const
258 {
259   if ( parent.isValid() )
260   {
261     return 0;
262   }
263 
264   return ( mAllowEmpty ? 1 : 0 ) + ( mAllowExpression ? mFields.count() + mExpression.count() : mFields.count() );
265 }
266 
columnCount(const QModelIndex & parent) const267 int QgsFieldModel::columnCount( const QModelIndex &parent ) const
268 {
269   Q_UNUSED( parent )
270   return 1;
271 }
272 
data(const QModelIndex & index,int role) const273 QVariant QgsFieldModel::data( const QModelIndex &index, int role ) const
274 {
275   if ( !index.isValid() )
276     return QVariant();
277 
278   int exprIdx = index.row() - mFields.count();
279   if ( mAllowEmpty )
280     exprIdx--;
281   const bool isEmpty = mAllowEmpty && index.row() == 0;
282   const int fieldOffset = mAllowEmpty ? 1 : 0;
283 
284   switch ( role )
285   {
286     case FieldNameRole:
287     {
288       if ( isEmpty || exprIdx >= 0 )
289       {
290         return QString();
291       }
292       const QgsField field = mFields.at( index.row() - fieldOffset );
293       return field.name();
294     }
295 
296     case ExpressionRole:
297     {
298       if ( exprIdx >= 0 )
299       {
300         return mExpression.at( exprIdx );
301       }
302       else if ( isEmpty )
303       {
304         return QVariant();
305       }
306       else
307       {
308         const QgsField field = mFields.at( index.row() - fieldOffset );
309         return field.name();
310       }
311     }
312 
313     case FieldIndexRole:
314     {
315       if ( isEmpty || exprIdx >= 0 )
316       {
317         return QVariant();
318       }
319       return index.row() - fieldOffset;
320     }
321 
322     case IsExpressionRole:
323     {
324       return exprIdx >= 0;
325     }
326 
327     case ExpressionValidityRole:
328     {
329       if ( exprIdx >= 0 )
330       {
331         QgsExpression exp( mExpression.at( exprIdx ) );
332         QgsExpressionContext context;
333         if ( mLayer )
334           context.setFields( mLayer->fields() );
335 
336         exp.prepare( &context );
337         return !exp.hasParserError();
338       }
339       return true;
340     }
341 
342     case FieldTypeRole:
343     {
344       if ( exprIdx < 0 && !isEmpty )
345       {
346         const QgsField field = mFields.at( index.row() - fieldOffset );
347         return static_cast< int >( field.type() );
348       }
349       return QVariant();
350     }
351 
352     case FieldOriginRole:
353     {
354       if ( exprIdx < 0 && !isEmpty )
355       {
356         return static_cast< int >( mFields.fieldOrigin( index.row() - fieldOffset ) );
357       }
358       return QVariant();
359     }
360 
361     case IsEmptyRole:
362     {
363       return isEmpty;
364     }
365 
366     case EditorWidgetType:
367     {
368       if ( exprIdx < 0 && !isEmpty )
369       {
370         return mFields.at( index.row() - fieldOffset ).editorWidgetSetup().type();
371       }
372       return QVariant();
373     }
374 
375     case JoinedFieldIsEditable:
376     {
377       if ( exprIdx < 0 && !isEmpty )
378       {
379         if ( mLayer && mFields.fieldOrigin( index.row() - fieldOffset ) == QgsFields::OriginJoin )
380         {
381           int srcFieldIndex;
382           const QgsVectorLayerJoinInfo *info = mLayer->joinBuffer()->joinForFieldIndex( index.row() - fieldOffset, mLayer->fields(), srcFieldIndex );
383 
384           if ( !info || !info->isEditable() )
385             return false;
386 
387           return true;
388         }
389       }
390       return QVariant();
391     }
392 
393     case FieldIsWidgetEditable:
394     {
395       return !( mLayer->editFormConfig().readOnly( index.row() - fieldOffset ) );
396     }
397 
398 
399     case Qt::DisplayRole:
400     case Qt::EditRole:
401     case Qt::ToolTipRole:
402     {
403       if ( isEmpty )
404       {
405         return QVariant();
406       }
407       else if ( exprIdx >= 0 )
408       {
409         return mExpression.at( exprIdx );
410       }
411       else if ( role == Qt::EditRole )
412       {
413         return mFields.at( index.row() - fieldOffset ).name();
414       }
415       else if ( role == Qt::ToolTipRole )
416       {
417         return fieldToolTip( mFields.at( index.row() - fieldOffset ) );
418       }
419       else if ( mLayer )
420       {
421         return mLayer->attributeDisplayName( index.row() - fieldOffset );
422       }
423       else if ( mFields.size() > index.row() - fieldOffset )
424       {
425         return mFields.field( index.row() - fieldOffset ).displayName();
426       }
427       else
428         return QVariant();
429     }
430 
431     case Qt::ForegroundRole:
432     {
433       if ( !isEmpty && exprIdx >= 0 )
434       {
435         // if expression, test validity
436         QgsExpression exp( mExpression.at( exprIdx ) );
437         QgsExpressionContext context;
438         if ( mLayer )
439           context.setFields( mLayer->fields() );
440 
441         exp.prepare( &context );
442         if ( exp.hasParserError() )
443         {
444           return QBrush( QColor( Qt::red ) );
445         }
446       }
447       return QVariant();
448     }
449 
450     case Qt::FontRole:
451     {
452       if ( !isEmpty && exprIdx >= 0 )
453       {
454         // if the line is an expression, set it as italic
455         QFont font = QFont();
456         font.setItalic( true );
457         return font;
458       }
459       return QVariant();
460     }
461 
462     case Qt::DecorationRole:
463     {
464       if ( !isEmpty && exprIdx < 0 )
465       {
466         return mFields.iconForField( index.row() - fieldOffset );
467       }
468       return QIcon();
469     }
470 
471     default:
472       return QVariant();
473   }
474 }
475 
fieldToolTip(const QgsField & field)476 QString QgsFieldModel::fieldToolTip( const QgsField &field )
477 {
478   QString toolTip;
479   if ( !field.alias().isEmpty() )
480   {
481     toolTip = QStringLiteral( "<b>%1</b> (%2)" ).arg( field.alias(), field.name() );
482   }
483   else
484   {
485     toolTip = QStringLiteral( "<b>%1</b>" ).arg( field.name() );
486   }
487 
488   toolTip += QStringLiteral( "<br><font style='font-family:monospace; white-space: nowrap;'>%3</font>" ).arg( field.displayType( true ) );
489 
490   const QString comment = field.comment();
491 
492   if ( ! comment.isEmpty() )
493   {
494     toolTip += QStringLiteral( "<br><em>%1</em>" ).arg( comment );
495   }
496 
497   return toolTip;
498 }
499 
fieldToolTipExtended(const QgsField & field,const QgsVectorLayer * layer)500 QString QgsFieldModel::fieldToolTipExtended( const QgsField &field, const QgsVectorLayer *layer )
501 {
502   QString toolTip = QgsFieldModel::fieldToolTip( field );
503   const QgsFields fields = layer->fields();
504   const int fieldIdx = fields.indexOf( field.name() );
505 
506   if ( fieldIdx < 0 )
507     return QString();
508 
509   const QString expressionString = fields.fieldOrigin( fieldIdx ) == QgsFields::OriginExpression
510                                    ? layer->expressionField( fieldIdx )
511                                    : QString();
512 
513   if ( !expressionString.isEmpty() )
514   {
515     toolTip += QStringLiteral( "<br><font style='font-family:monospace;'>%3</font>" ).arg( expressionString );
516   }
517 
518   return toolTip;
519 }
520 
setFields(const QgsFields & fields)521 void QgsFieldModel::setFields( const QgsFields &fields )
522 {
523   setLayer( nullptr );
524   beginResetModel();
525   mFields = fields;
526   endResetModel();
527 }
528 
fields() const529 QgsFields QgsFieldModel::fields() const
530 {
531   return mFields;
532 }
533