mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-06 00:05:16 -04:00
429 lines
10 KiB
C++
429 lines
10 KiB
C++
/***************************************************************************
|
|
qgsfieldmodel.cpp
|
|
--------------------------------------
|
|
Date : 01.04.2014
|
|
Copyright : (C) 2014 Denis Rouzaud
|
|
Email : denis.rouzaud@gmail.com
|
|
***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include <QFont>
|
|
#include <QIcon>
|
|
|
|
#include "qgsfieldmodel.h"
|
|
#include "qgsmaplayermodel.h"
|
|
#include "qgsmaplayerproxymodel.h"
|
|
#include "qgslogger.h"
|
|
#include "qgsapplication.h"
|
|
#include "qgsvectorlayer.h"
|
|
|
|
QgsFieldModel::QgsFieldModel( QObject *parent )
|
|
: QAbstractItemModel( parent )
|
|
{
|
|
}
|
|
|
|
QModelIndex QgsFieldModel::indexFromName( const QString &fieldName )
|
|
{
|
|
QString fldName( fieldName ); // we may need a copy
|
|
|
|
if ( mLayer )
|
|
{
|
|
// the name could be an alias
|
|
// it would be better to have "display name" directly in QgsFields
|
|
// rather than having to consult layer in various places in code!
|
|
QString fieldNameWithAlias = mLayer->attributeAliases().key( fldName );
|
|
if ( !fieldNameWithAlias.isNull() )
|
|
fldName = fieldNameWithAlias;
|
|
}
|
|
|
|
if ( mAllowEmpty && fieldName.isEmpty() )
|
|
return index( 0, 0 );
|
|
|
|
int r = mFields.lookupField( fldName );
|
|
if ( r >= 0 )
|
|
{
|
|
if ( mAllowEmpty )
|
|
r++;
|
|
|
|
QModelIndex idx = index( r, 0 );
|
|
if ( idx.isValid() )
|
|
{
|
|
return idx;
|
|
}
|
|
}
|
|
|
|
if ( mAllowExpression )
|
|
{
|
|
int exprIdx = mExpression.indexOf( fldName );
|
|
if ( exprIdx != -1 )
|
|
{
|
|
return index( ( mAllowEmpty ? 1 : 0 ) + mFields.count() + exprIdx, 0 );
|
|
}
|
|
}
|
|
|
|
return QModelIndex();
|
|
}
|
|
|
|
bool QgsFieldModel::isField( const QString &expression ) const
|
|
{
|
|
int index = mFields.indexFromName( expression );
|
|
return index >= 0;
|
|
}
|
|
|
|
void QgsFieldModel::setLayer( QgsVectorLayer *layer )
|
|
{
|
|
if ( mLayer )
|
|
{
|
|
disconnect( mLayer, &QgsVectorLayer::updatedFields, this, &QgsFieldModel::updateModel );
|
|
disconnect( mLayer, &QObject::destroyed, this, &QgsFieldModel::layerDeleted );
|
|
}
|
|
|
|
mLayer = layer;
|
|
|
|
if ( mLayer )
|
|
{
|
|
connect( mLayer, &QgsVectorLayer::updatedFields, this, &QgsFieldModel::updateModel );
|
|
connect( mLayer, &QObject::destroyed, this, &QgsFieldModel::layerDeleted );
|
|
}
|
|
|
|
updateModel();
|
|
}
|
|
|
|
void QgsFieldModel::layerDeleted()
|
|
{
|
|
mLayer = nullptr;
|
|
updateModel();
|
|
}
|
|
|
|
void QgsFieldModel::updateModel()
|
|
{
|
|
int offset = mAllowEmpty ? 1 : 0;
|
|
if ( mLayer )
|
|
{
|
|
QgsFields newFields = mLayer->fields();
|
|
if ( mFields.toList() != newFields.toList() )
|
|
{
|
|
// Try to handle two special cases: addition of a new field and removal of a field.
|
|
// It would be better to listen directly to attributeAdded/attributeDeleted
|
|
// so we would not have to check for addition/removal here.
|
|
|
|
if ( mFields.count() == newFields.count() - 1 )
|
|
{
|
|
QgsFields tmpNewFields = newFields;
|
|
tmpNewFields.remove( tmpNewFields.count() - 1 );
|
|
if ( mFields.toList() == tmpNewFields.toList() )
|
|
{
|
|
// the only change is a new field at the end
|
|
beginInsertRows( QModelIndex(), mFields.count() + offset, mFields.count() + offset );
|
|
mFields = newFields;
|
|
endInsertRows();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( mFields.count() == newFields.count() + 1 )
|
|
{
|
|
QgsFields tmpOldFields = mFields;
|
|
tmpOldFields.remove( tmpOldFields.count() - 1 );
|
|
if ( tmpOldFields.toList() == newFields.toList() )
|
|
{
|
|
// the only change is a field removed at the end
|
|
beginRemoveRows( QModelIndex(), mFields.count() - 1 + offset, mFields.count() - 1 + offset );
|
|
mFields = newFields;
|
|
endRemoveRows();
|
|
return;
|
|
}
|
|
|
|
for ( int i = 0; i < newFields.count(); ++i )
|
|
{
|
|
if ( mFields.at( i ) != newFields.at( i ) )
|
|
{
|
|
QgsFields tmpOldFields = mFields;
|
|
tmpOldFields.remove( i );
|
|
if ( tmpOldFields.toList() != newFields.toList() )
|
|
break; // the change is more complex - go with general case
|
|
|
|
// the only change is a field removed at index i
|
|
beginRemoveRows( QModelIndex(), i + offset, i + offset );
|
|
mFields = newFields;
|
|
endRemoveRows();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// general case with reset - not good - resets selections
|
|
beginResetModel();
|
|
mFields = mLayer->fields();
|
|
endResetModel();
|
|
}
|
|
else
|
|
emit dataChanged( index( 0 + offset, 0 ), index( rowCount(), 0 ) );
|
|
}
|
|
else
|
|
{
|
|
beginResetModel();
|
|
mFields = QgsFields();
|
|
endResetModel();
|
|
}
|
|
}
|
|
|
|
void QgsFieldModel::setAllowExpression( bool allowExpression )
|
|
{
|
|
if ( allowExpression == mAllowExpression )
|
|
return;
|
|
|
|
mAllowExpression = allowExpression;
|
|
|
|
if ( !mAllowExpression )
|
|
{
|
|
int start = mFields.count();
|
|
int end = start + mExpression.count() - 1;
|
|
beginRemoveRows( QModelIndex(), start, end );
|
|
mExpression = QList<QString>();
|
|
endRemoveRows();
|
|
}
|
|
}
|
|
|
|
void QgsFieldModel::setAllowEmptyFieldName( bool allowEmpty )
|
|
{
|
|
if ( allowEmpty == mAllowEmpty )
|
|
return;
|
|
|
|
if ( allowEmpty )
|
|
{
|
|
beginInsertRows( QModelIndex(), 0, 0 );
|
|
mAllowEmpty = true;
|
|
endInsertRows();
|
|
}
|
|
else
|
|
{
|
|
beginRemoveRows( QModelIndex(), 0, 0 );
|
|
mAllowEmpty = false;
|
|
endRemoveRows();
|
|
}
|
|
}
|
|
|
|
|
|
void QgsFieldModel::setExpression( const QString &expression )
|
|
{
|
|
if ( !mAllowExpression )
|
|
return;
|
|
|
|
QModelIndex idx = indexFromName( expression );
|
|
if ( idx.isValid() )
|
|
return;
|
|
|
|
beginResetModel();
|
|
mExpression = QList<QString>();
|
|
if ( !expression.isEmpty() )
|
|
mExpression << expression;
|
|
endResetModel();
|
|
}
|
|
|
|
void QgsFieldModel::removeExpression()
|
|
{
|
|
beginResetModel();
|
|
mExpression = QList<QString>();
|
|
endResetModel();
|
|
}
|
|
|
|
QModelIndex QgsFieldModel::index( int row, int column, const QModelIndex &parent ) const
|
|
{
|
|
if ( hasIndex( row, column, parent ) )
|
|
{
|
|
return createIndex( row, column, row );
|
|
}
|
|
|
|
return QModelIndex();
|
|
}
|
|
|
|
QModelIndex QgsFieldModel::parent( const QModelIndex &child ) const
|
|
{
|
|
Q_UNUSED( child );
|
|
return QModelIndex();
|
|
}
|
|
|
|
int QgsFieldModel::rowCount( const QModelIndex &parent ) const
|
|
{
|
|
if ( parent.isValid() )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return ( mAllowEmpty ? 1 : 0 ) + ( mAllowExpression ? mFields.count() + mExpression.count() : mFields.count() );
|
|
}
|
|
|
|
int QgsFieldModel::columnCount( const QModelIndex &parent ) const
|
|
{
|
|
Q_UNUSED( parent );
|
|
return 1;
|
|
}
|
|
|
|
QVariant QgsFieldModel::data( const QModelIndex &index, int role ) const
|
|
{
|
|
if ( !index.isValid() )
|
|
return QVariant();
|
|
|
|
int exprIdx = index.row() - mFields.count();
|
|
if ( mAllowEmpty )
|
|
exprIdx--;
|
|
bool isEmpty = mAllowEmpty && index.row() == 0;
|
|
int fieldOffset = mAllowEmpty ? 1 : 0;
|
|
|
|
switch ( role )
|
|
{
|
|
case FieldNameRole:
|
|
{
|
|
if ( isEmpty || exprIdx >= 0 )
|
|
{
|
|
return "";
|
|
}
|
|
QgsField field = mFields.at( index.row() - fieldOffset );
|
|
return field.name();
|
|
}
|
|
|
|
case ExpressionRole:
|
|
{
|
|
if ( exprIdx >= 0 )
|
|
{
|
|
return mExpression.at( exprIdx );
|
|
}
|
|
else if ( isEmpty )
|
|
{
|
|
return QVariant();
|
|
}
|
|
else
|
|
{
|
|
QgsField field = mFields.at( index.row() - fieldOffset );
|
|
return field.name();
|
|
}
|
|
}
|
|
|
|
case FieldIndexRole:
|
|
{
|
|
if ( isEmpty || exprIdx >= 0 )
|
|
{
|
|
return QVariant();
|
|
}
|
|
return index.row() - fieldOffset;
|
|
}
|
|
|
|
case IsExpressionRole:
|
|
{
|
|
return exprIdx >= 0;
|
|
}
|
|
|
|
case ExpressionValidityRole:
|
|
{
|
|
if ( exprIdx >= 0 )
|
|
{
|
|
QgsExpression exp( mExpression.at( exprIdx ) );
|
|
QgsExpressionContext context;
|
|
if ( mLayer )
|
|
context.setFields( mLayer->fields() );
|
|
|
|
exp.prepare( &context );
|
|
return !exp.hasParserError();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
case FieldTypeRole:
|
|
{
|
|
if ( exprIdx < 0 && !isEmpty )
|
|
{
|
|
QgsField field = mFields.at( index.row() - fieldOffset );
|
|
return static_cast< int >( field.type() );
|
|
}
|
|
return QVariant();
|
|
}
|
|
|
|
case FieldOriginRole:
|
|
{
|
|
if ( exprIdx < 0 && !isEmpty )
|
|
{
|
|
return static_cast< int >( mFields.fieldOrigin( index.row() - fieldOffset ) );
|
|
}
|
|
return QVariant();
|
|
}
|
|
|
|
case IsEmptyRole:
|
|
{
|
|
return isEmpty;
|
|
}
|
|
|
|
case Qt::DisplayRole:
|
|
case Qt::EditRole:
|
|
{
|
|
if ( isEmpty )
|
|
{
|
|
return QVariant();
|
|
}
|
|
else if ( exprIdx >= 0 )
|
|
{
|
|
return mExpression.at( exprIdx );
|
|
}
|
|
else if ( role == Qt::EditRole )
|
|
{
|
|
return mFields.at( index.row() - fieldOffset ).name();
|
|
}
|
|
else if ( mLayer )
|
|
{
|
|
return mLayer->attributeDisplayName( index.row() - fieldOffset );
|
|
}
|
|
else
|
|
return QVariant();
|
|
}
|
|
|
|
case Qt::ForegroundRole:
|
|
{
|
|
if ( !isEmpty && exprIdx >= 0 )
|
|
{
|
|
// if expression, test validity
|
|
QgsExpression exp( mExpression.at( exprIdx ) );
|
|
QgsExpressionContext context;
|
|
if ( mLayer )
|
|
context.setFields( mLayer->fields() );
|
|
|
|
exp.prepare( &context );
|
|
if ( exp.hasParserError() )
|
|
{
|
|
return QBrush( QColor( Qt::red ) );
|
|
}
|
|
}
|
|
return QVariant();
|
|
}
|
|
|
|
case Qt::FontRole:
|
|
{
|
|
if ( !isEmpty && exprIdx >= 0 )
|
|
{
|
|
// if the line is an expression, set it as italic
|
|
QFont font = QFont();
|
|
font.setItalic( true );
|
|
return font;
|
|
}
|
|
return QVariant();
|
|
}
|
|
|
|
case Qt::DecorationRole:
|
|
{
|
|
if ( !isEmpty && exprIdx < 0 )
|
|
{
|
|
return mFields.iconForField( index.row() - fieldOffset );
|
|
}
|
|
return QIcon();
|
|
}
|
|
|
|
default:
|
|
return QVariant();
|
|
}
|
|
}
|