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