[feature] New configuration options for attribute table

* Allow reordering the attribute table columns
 * Allow adding a new column to trigger an action to the attribute table
This commit is contained in:
Matthias Kuhn 2016-04-28 15:47:43 +02:00
parent aa9010e8ed
commit dd88fa99f3
27 changed files with 740 additions and 107 deletions

View File

@ -22,6 +22,7 @@
%Include qgsaction.sip %Include qgsaction.sip
%Include qgsactionmanager.sip %Include qgsactionmanager.sip
%Include qgsattributeaction.sip %Include qgsattributeaction.sip
%Include qgsattributetableconfig.sip
%Include qgsbrowsermodel.sip %Include qgsbrowsermodel.sip
%Include qgsclipper.sip %Include qgsclipper.sip
%Include qgscolorscheme.sip %Include qgscolorscheme.sip

View File

@ -0,0 +1,78 @@
/***************************************************************************
qgsattributetableconfig.h - QgsAttributeTableConfig
---------------------
begin : 27.4.2016
copyright : (C) 2016 by Matthias Kuhn
email : matthias@opengis.ch
***************************************************************************
* *
* 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. *
* *
***************************************************************************/
/**
* This is a container for configuration of the attribute table.
* The configuration is specific for one vector layer.
*/
class QgsAttributeTableConfig
{
%TypeHeaderCode
#include <qgsattributetableconfig.h>
%End
public:
/**
* The type of an attribute table column.
*/
enum Type
{
Field, //!< This column represents a field
Action //!< This column represents an action widget
};
/**
* Defines the configuration of a column in the attribute table.
*/
struct ColumnConfig
{
QgsAttributeTableConfig::Type mType; //!< The type of this column.
QString mName; //!< The name of the attribute if this column represents a field
bool mHidden; //!< Flag that controls if the column is hidden
};
QgsAttributeTableConfig();
/**
* Get the list with all columns and their configuration.
* The list order defines the order of appearance.
*/
QVector<QgsAttributeTableConfig::ColumnConfig> columns() const;
/**
* Set the list of columns visible in the attribute table.
* The list order defines the order of appearance.
*/
void setColumns( const QVector<QgsAttributeTableConfig::ColumnConfig>& columns );
/**
* Update the configuration with the given fields.
* Any field which is present in the configuration but not present in the
* parameter fields will be removed. Any field which is in the parameter
* fields but not in the configuration will be appended.
*/
void update( const QgsFields& fields );
/**
* Serialize to XML on layer save
*/
void writeXml( QDomNode& node ) const;
/**
* Deserialize to XML on layer load
*/
void readXml( const QDomNode& node );
};

View File

@ -1321,6 +1321,18 @@ class QgsVectorLayer : QgsMapLayer
*/ */
QgsConditionalLayerStyles *conditionalStyles() const; QgsConditionalLayerStyles *conditionalStyles() const;
/**
* Get the attribute table configuration object.
* This defines the appearance of the attribute table.
*/
QgsAttributeTableConfig attributeTableConfig() const;
/**
* Set the attribute table configuration object.
* This defines the appearance of the attribute table.
*/
void setAttributeTableConfig( const QgsAttributeTableConfig& attributeTableConfig );
public slots: public slots:
/** /**
* Select feature by its ID * Select feature by its ID

View File

@ -13,6 +13,18 @@ class QgsAttributeTableFilterModel: QSortFilterProxyModel, QgsFeatureModel
ShowEdited ShowEdited
}; };
enum ColumnType
{
ColumnTypeField, //!< This column shows a field
ColumnTypeActionButton //!< This column shows action buttons
};
enum Role
{
TypeRole = QgsAttributeTableModel::UserRole //!< The type of a given column
};
/** /**
* *
* Make sure, the master model is already loaded, so the selection will get synchronized. * Make sure, the master model is already loaded, so the selection will get synchronized.
@ -99,6 +111,25 @@ class QgsAttributeTableFilterModel: QSortFilterProxyModel, QgsFeatureModel
/** Returns the map canvas*/ /** Returns the map canvas*/
QgsMapCanvas* mapCanvas() const; QgsMapCanvas* mapCanvas() const;
virtual QVariant data( const QModelIndex& index, int role ) const;
QVariant headerData( int section, Qt::Orientation orientation, int role ) const;
/**
* Get the index of the first column that contains an action widget.
* Returns -1 if none is defined.
*/
int actionColumnIndex() const;
int columnCount( const QModelIndex &parent ) const;
/**
* Set the attribute table configuration to control which fields are shown,
* in which order they are shown as well as if and where an action column
* is shown.
*/
void setAttributeTableConfig( const QgsAttributeTableConfig& config );
protected: protected:
/** /**
* Returns true if the source row will be accepted * Returns true if the source row will be accepted

View File

@ -19,7 +19,7 @@ class QgsAttributeTableView : QTableView
/** /**
* This event filter is installed on the verticalHeader to intercept mouse press and release * This event filter is installed on the verticalHeader to intercept mouse press and release
* events. These are used to disable / enable live synchronisation with the map canvas selection * events. These are used to disable / enable live synchronisation with the map canvas selection
* which can be slow due to recurring canvas repaints. Updating the * which can be slow due to recurring canvas repaints.
* *
* @param object The object which is the target of the event. * @param object The object which is the target of the event.
* @param event The intercepted event * @param event The intercepted event

View File

@ -65,6 +65,11 @@ class QgsDualView : QStackedWidget
*/ */
void setFilterMode( QgsAttributeTableFilterModel::FilterMode filterMode ); void setFilterMode( QgsAttributeTableFilterModel::FilterMode filterMode );
/**
* Get the filter mode
*
* @return the filter mode
*/
QgsAttributeTableFilterModel::FilterMode filterMode(); QgsAttributeTableFilterModel::FilterMode filterMode();
/** /**
@ -98,6 +103,9 @@ class QgsDualView : QStackedWidget
*/ */
void setFilteredFeatures( const QgsFeatureIds& filteredFeatures ); void setFilteredFeatures( const QgsFeatureIds& filteredFeatures );
/**
* Get a list of currently visible feature ids.
*/
QgsFeatureIds filteredFeatures(); QgsFeatureIds filteredFeatures();
/** /**
@ -112,6 +120,11 @@ class QgsDualView : QStackedWidget
void setFeatureSelectionManager( QgsIFeatureSelectionManager* featureSelectionManager ); void setFeatureSelectionManager( QgsIFeatureSelectionManager* featureSelectionManager );
QgsAttributeTableView* tableView(); QgsAttributeTableView* tableView();
/**
* Set the attribute table config which should be used to control
* the appearance of the attribute table.
*/
void setAttributeTableConfig( const QgsAttributeTableConfig& config );
protected: protected:
/** /**

View File

@ -8,16 +8,19 @@ class QgsOrganizeTableColumnsDialog : QDialog
/** /**
* Constructor * Constructor
* @param vl The concerned vector layer * @param vl The concerned vector layer
* @param visible the cuurent list of visible fields name * @param visible the current list of visible fields name
* @param parent parent object * @param parent parent object
* @param flags window flags * @param flags window flags
*/ */
QgsOrganizeTableColumnsDialog( const QgsVectorLayer* vl, QStringList visible, QWidget *parent /TransferThis/ = nullptr, Qt::WindowFlags flags = Qt::Window ); QgsOrganizeTableColumnsDialog(const QgsVectorLayer* vl, QWidget* parent = nullptr, Qt::WindowFlags flags = Qt::Window );
/**
* Destructor
*/
~QgsOrganizeTableColumnsDialog(); ~QgsOrganizeTableColumnsDialog();
/** /**
* Get the selected fields name * Get the updated configuration
* @return The selected fields name
*/ */
QStringList selectedFields(); QgsAttributeTableConfig config() const;
}; };

View File

@ -125,6 +125,7 @@ QgsAttributeTableDialog::QgsAttributeTableDialog( QgsVectorLayer *theLayer, QWid
// Initialize dual view // Initialize dual view
mMainView->init( mLayer, QgisApp::instance()->mapCanvas(), r, context ); mMainView->init( mLayer, QgisApp::instance()->mapCanvas(), r, context );
mMainView->setAttributeTableConfig( mLayer->attributeTableConfig() );
// Initialize filter gui elements // Initialize filter gui elements
mFilterActionMapper = new QSignalMapper( this ); mFilterActionMapper = new QSignalMapper( this );
@ -776,17 +777,12 @@ void QgsAttributeTableDialog::on_mFilterTableFields_clicked()
return; return;
} }
QgsOrganizeTableColumnsDialog dialog( mLayer, mVisibleFields ); QgsOrganizeTableColumnsDialog dialog( mLayer );
if ( dialog.exec() == QDialog::Accepted ) if ( dialog.exec() == QDialog::Accepted )
{ {
mVisibleFields = dialog.selectedFields(); QgsAttributeTableConfig config = dialog.config();
mLayer->setAttributeTableConfig( config );
const QgsFields layerAttributes = mLayer->fields(); mMainView->setAttributeTableConfig( config );
for ( int idx = 0; idx < layerAttributes.count(); ++idx )
{
mMainView->tableView()->setColumnHidden(
idx, !mVisibleFields.contains( layerAttributes[idx].name() ) );
}
} }
} }

View File

@ -79,6 +79,7 @@ SET(QGIS_CORE_SRCS
qgsapplication.cpp qgsapplication.cpp
qgsaction.cpp qgsaction.cpp
qgsactionmanager.cpp qgsactionmanager.cpp
qgsattributetableconfig.cpp
qgsbrowsermodel.cpp qgsbrowsermodel.cpp
qgscachedfeatureiterator.cpp qgscachedfeatureiterator.cpp
qgscacheindex.cpp qgscacheindex.cpp
@ -591,7 +592,7 @@ SET(QGIS_CORE_HDRS
qgis.h qgis.h
qgsaction.h qgsaction.h
qgsactionmanager.h qgsattributetableconfig.h
qgsattributeaction.h qgsattributeaction.h
qgscachedfeatureiterator.h qgscachedfeatureiterator.h
qgscacheindex.h qgscacheindex.h

View File

@ -23,7 +23,6 @@ email : marco.hugentobler at sourcepole dot com
#include <QString> #include <QString>
class QgsCoordinateTransform;
class QgsMapToPixel; class QgsMapToPixel;
class QgsCurveV2; class QgsCurveV2;
class QgsMultiCurveV2; class QgsMultiCurveV2;

View File

@ -0,0 +1,150 @@
/***************************************************************************
qgsattributetableconfig.cpp - QgsAttributeTableConfig
---------------------
begin : 27.4.2016
copyright : (C) 2016 by mku
email : [your-email-here]
***************************************************************************
* *
* 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 "qgsattributetableconfig.h"
#include <QStringList>
QgsAttributeTableConfig::QgsAttributeTableConfig()
{
}
QVector<QgsAttributeTableConfig::ColumnConfig> QgsAttributeTableConfig::columns() const
{
return mColumns;
}
void QgsAttributeTableConfig::setColumns( const QVector<ColumnConfig>& columns )
{
mColumns = columns;
}
void QgsAttributeTableConfig::update( const QgsFields& fields )
{
QStringList columns;
bool containsActionColumn = false;
for ( int i = mColumns.count() - 1; i >= 0; --i )
{
const ColumnConfig& column = mColumns.at( i );
if ( column.mType == Field )
{
if ( fields.fieldNameIndex( column.mName ) == -1 )
{
mColumns.remove( i );
}
else
{
columns.append( column.mName );
}
}
else if ( column.mType == Action )
{
containsActionColumn = true;
}
}
Q_FOREACH ( const QgsField& field, fields )
{
if ( !columns.contains( field.name() ) )
{
ColumnConfig newColumn;
newColumn.mHidden = false;
newColumn.mType = Field;
newColumn.mName = field.name();
mColumns.append( newColumn );
}
}
if ( !containsActionColumn )
{
ColumnConfig actionConfig;
actionConfig.mType = Action;
actionConfig.mHidden = true;
mColumns.append( actionConfig );
}
}
void QgsAttributeTableConfig::readXml( const QDomNode& node )
{
mColumns.clear();
QDomNode configNode = node.namedItem( "attributetableconfig" );
if ( !configNode.isNull() )
{
QDomNode columnsNode = configNode.toElement().namedItem( "columns" );
QDomNodeList columns = columnsNode.childNodes();
for ( int i = 0; i < columns.size(); ++i )
{
QDomElement columnElement = columns.at( i ).toElement();
ColumnConfig column;
if ( columnElement.attribute( "type" ) == "actions" )
{
column.mType = Action;
}
else
{
column.mType = Field;
column.mName = columnElement.attribute( "name" );
}
column.mHidden = columnElement.attribute( "hidden" ) == "1";
mColumns.append( column );
}
}
}
void QgsAttributeTableConfig::writeXml( QDomNode& node ) const
{
QDomDocument doc( node.ownerDocument() );
QDomElement configElement = doc.createElement( "attributetableconfig" );
QDomElement columnsElement = doc.createElement( "columns" );
Q_FOREACH ( const ColumnConfig& column, mColumns )
{
QDomElement columnElement = doc.createElement( "column" );
if ( column.mType == Action )
{
columnElement.setAttribute( "type", "actions" );
}
else
{
columnElement.setAttribute( "type", "field" );
columnElement.setAttribute( "name", column.mName );
}
columnElement.setAttribute( "hidden", column.mHidden );
columnsElement.appendChild( columnElement );
}
configElement.appendChild( columnsElement );
node.appendChild( configElement );
}

View File

@ -0,0 +1,90 @@
/***************************************************************************
qgsattributetableconfig.h - QgsAttributeTableConfig
---------------------
begin : 27.4.2016
copyright : (C) 2016 by Matthias Kuhn
email : matthias@opengis.ch
***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#ifndef QGSATTRIBUTETABLECONFIG_H
#define QGSATTRIBUTETABLECONFIG_H
#include <QString>
#include <QVector>
#include <QDomNode>
#include "qgsfield.h"
/**
* This is a container for configuration of the attribute table.
* The configuration is specific for one vector layer.
*/
class CORE_EXPORT QgsAttributeTableConfig
{
public:
/**
* The type of an attribute table column.
*/
enum Type
{
Field, //!< This column represents a field
Action //!< This column represents an action widget
};
/**
* Defines the configuration of a column in the attribute table.
*/
struct ColumnConfig
{
Type mType; //!< The type of this column.
QString mName; //!< The name of the attribute if this column represents a field
bool mHidden; //!< Flag that controls if the column is hidden
};
QgsAttributeTableConfig();
/**
* Get the list with all columns and their configuration.
* The list order defines the order of appearance.
*/
QVector<ColumnConfig> columns() const;
/**
* Set the list of columns visible in the attribute table.
* The list order defines the order of appearance.
*/
void setColumns( const QVector<ColumnConfig>& columns );
/**
* Update the configuration with the given fields.
* Any field which is present in the configuration but not present in the
* parameter fields will be removed. Any field which is in the parameter
* fields but not in the configuration will be appended.
*/
void update( const QgsFields& fields );
/**
* Serialize to XML on layer save
*/
void writeXml( QDomNode& node ) const;
/**
* Deserialize to XML on layer load
*/
void readXml( const QDomNode& node );
private:
QVector<ColumnConfig> mColumns;
};
Q_DECLARE_METATYPE( QgsAttributeTableConfig::ColumnConfig )
#endif // QGSATTRIBUTETABLECONFIG_H

View File

@ -42,7 +42,6 @@ class CORE_EXPORT QgsCoordinateTransformCache
void invalidateCrs( const QString& crsAuthId ); void invalidateCrs( const QString& crsAuthId );
private: private:
static QgsCoordinateTransformCache* mInstance;
QMultiHash< QPair< QString, QString >, QgsCoordinateTransform* > mTransforms; //same auth_id pairs might have different datum transformations QMultiHash< QPair< QString, QString >, QgsCoordinateTransform* > mTransforms; //same auth_id pairs might have different datum transformations
QgsCoordinateTransformCache(); QgsCoordinateTransformCache();

View File

@ -1926,8 +1926,6 @@ bool QgsVectorLayer::readSymbology( const QDomNode& node, QString& errorMessage
// process the attribute actions // process the attribute actions
mActions->readXML( node ); mActions->readXML( node );
mEditFormConfig->readXml( node );
QDomNode annotationFormNode = node.namedItem( "annotationform" ); QDomNode annotationFormNode = node.namedItem( "annotationform" );
if ( !annotationFormNode.isNull() ) if ( !annotationFormNode.isNull() )
{ {
@ -1988,6 +1986,8 @@ bool QgsVectorLayer::readSymbology( const QDomNode& node, QString& errorMessage
mEditFormConfig->readXml( node ); mEditFormConfig->readXml( node );
mAttributeTableConfig.readXml( node );
mConditionalStyles->readXml( node ); mConditionalStyles->readXml( node );
return true; return true;
@ -2154,6 +2154,8 @@ bool QgsVectorLayer::writeSymbology( QDomNode& node, QDomDocument& doc, QString&
mEditFormConfig->writeXml( node ); mEditFormConfig->writeXml( node );
mAttributeTableConfig.writeXml( node );
mConditionalStyles->writeXml( node, doc ); mConditionalStyles->writeXml( node, doc );
return true; return true;
@ -2266,11 +2268,11 @@ void QgsVectorLayer::addAttributeAlias( int attIndex, const QString& aliasString
QString QgsVectorLayer::attributeAlias( int attributeIndex ) const QString QgsVectorLayer::attributeAlias( int attributeIndex ) const
{ {
if ( attributeIndex < 0 || attributeIndex >= fields().count() ) if ( attributeIndex < 0 || attributeIndex >= fields().count() )
return ""; return QString();
QString name = fields().at( attributeIndex ).name(); QString name = fields().at( attributeIndex ).name();
return mAttributeAliasMap.value( name, "" ); return mAttributeAliasMap.value( name, QString() );
} }
QString QgsVectorLayer::attributeDisplayName( int attributeIndex ) const QString QgsVectorLayer::attributeDisplayName( int attributeIndex ) const
@ -3591,6 +3593,16 @@ void QgsVectorLayer::readSldLabeling( const QDomNode& node )
} }
} }
QgsAttributeTableConfig QgsVectorLayer::attributeTableConfig() const
{
return mAttributeTableConfig;
}
void QgsVectorLayer::setAttributeTableConfig( const QgsAttributeTableConfig& attributeTableConfig )
{
mAttributeTableConfig = attributeTableConfig;
}
void QgsVectorLayer::setDiagramLayerSettings( const QgsDiagramLayerSettings& s ) void QgsVectorLayer::setDiagramLayerSettings( const QgsDiagramLayerSettings& s )
{ {
if ( !mDiagramLayerSettings ) if ( !mDiagramLayerSettings )

View File

@ -34,6 +34,7 @@
#include "qgssnapper.h" #include "qgssnapper.h"
#include "qgsvectorsimplifymethod.h" #include "qgsvectorsimplifymethod.h"
#include "qgseditformconfig.h" #include "qgseditformconfig.h"
#include "qgsattributetableconfig.h"
class QPainter; class QPainter;
class QImage; class QImage;
@ -631,7 +632,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
* The pointer which is returned directly points to the actions object * The pointer which is returned directly points to the actions object
* which is used by the layer, so any changes are immediately applied. * which is used by the layer, so any changes are immediately applied.
*/ */
QgsActionManager *actions() { return mActions; } QgsActionManager* actions() { return mActions; }
/** /**
* The number of features that are selected in this layer * The number of features that are selected in this layer
@ -1321,7 +1322,12 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
*/ */
void clearAttributeEditorWidgets() { mEditFormConfig->clearTabs(); } void clearAttributeEditorWidgets() { mEditFormConfig->clearTabs(); }
/** Returns the alias of an attribute name or an empty string if there is no alias */ /**
* Returns the alias of an attribute name or a null string if there is no alias.
*
* @see {@attributeDisplayName( int attributeIndex )} which returns the field name
* if no alias is defined.
*/
QString attributeAlias( int attributeIndex ) const; QString attributeAlias( int attributeIndex ) const;
/** Convenience function that returns the attribute alias if defined or the field name else */ /** Convenience function that returns the attribute alias if defined or the field name else */
@ -1686,6 +1692,18 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
*/ */
QgsConditionalLayerStyles *conditionalStyles() const; QgsConditionalLayerStyles *conditionalStyles() const;
/**
* Get the attribute table configuration object.
* This defines the appearance of the attribute table.
*/
QgsAttributeTableConfig attributeTableConfig() const;
/**
* Set the attribute table configuration object.
* This defines the appearance of the attribute table.
*/
void setAttributeTableConfig( const QgsAttributeTableConfig& attributeTableConfig );
public slots: public slots:
/** /**
* Select feature by its ID * Select feature by its ID
@ -2088,6 +2106,8 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
QgsFeatureIds mDeletedFids; QgsFeatureIds mDeletedFids;
QgsAttributeTableConfig mAttributeTableConfig;
friend class QgsVectorLayerFeatureSource; friend class QgsVectorLayerFeatureSource;
}; };

View File

@ -118,9 +118,23 @@ void QgsAttributeTableDelegate::setFeatureSelectionModel( QgsFeatureSelectionMod
mFeatureSelectionModel = featureSelectionModel; mFeatureSelectionModel = featureSelectionModel;
} }
void QgsAttributeTableDelegate::setActionWidgetImage( const QImage& image )
{
mActionWidgetImage = image;
}
void QgsAttributeTableDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const void QgsAttributeTableDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{ {
QgsAttributeTableFilterModel::ColumnType columnType = static_cast<QgsAttributeTableFilterModel::ColumnType>( index.model()->data( index, QgsAttributeTableFilterModel::TypeRole ).toInt() );
if ( columnType == QgsAttributeTableFilterModel::ColumnTypeActionButton )
{
QRect r = option.rect.adjusted( -1, 0, 0, 0 );
painter->drawImage( r.x(), r.y(), mActionWidgetImage );
}
else
{
QgsFeatureId fid = index.model()->data( index, QgsAttributeTableModel::FeatureIdRole ).toLongLong(); QgsFeatureId fid = index.model()->data( index, QgsAttributeTableModel::FeatureIdRole ).toLongLong();
QStyleOptionViewItem myOpt = option; QStyleOptionViewItem myOpt = option;
@ -134,13 +148,6 @@ void QgsAttributeTableDelegate::paint( QPainter* painter, const QStyleOptionView
if ( mFeatureSelectionModel && mFeatureSelectionModel->isSelected( fid ) ) if ( mFeatureSelectionModel && mFeatureSelectionModel->isSelected( fid ) )
myOpt.state |= QStyle::State_Selected; myOpt.state |= QStyle::State_Selected;
if ( index.column() == 0 )
{
painter->drawImage( QPoint( 0, 0 ), mActionButtonImage );
}
else
{
QItemDelegate::paint( painter, myOpt, index ); QItemDelegate::paint( painter, myOpt, index );
if ( option.state & QStyle::State_HasFocus ) if ( option.state & QStyle::State_HasFocus )

View File

@ -46,7 +46,6 @@ class GUI_EXPORT QgsAttributeTableDelegate : public QItemDelegate
: QItemDelegate( parent ) : QItemDelegate( parent )
, mLayer( nullptr ) , mLayer( nullptr )
, mFeatureSelectionModel( nullptr ) , mFeatureSelectionModel( nullptr )
, mActionButtonWidget( nullptr )
{ {
} }
@ -80,13 +79,15 @@ class GUI_EXPORT QgsAttributeTableDelegate : public QItemDelegate
void setFeatureSelectionModel( QgsFeatureSelectionModel* featureSelectionModel ); void setFeatureSelectionModel( QgsFeatureSelectionModel* featureSelectionModel );
private: /**
QWidget* createActionWidget( QWidget* parent, QgsVectorLayer* layer ) const; * Set an image that represents an action widget
*/
void setActionWidgetImage( const QImage& image );
private:
QgsVectorLayer* mLayer; QgsVectorLayer* mLayer;
QgsFeatureSelectionModel* mFeatureSelectionModel; QgsFeatureSelectionModel* mFeatureSelectionModel;
QWidget* mActionButtonWidget; QImage mActionWidgetImage;
QImage mActionButtonImage;
}; };
#endif //QGSATTRIBUTETABLEDELEGATE_H #endif //QGSATTRIBUTETABLEDELEGATE_H

View File

@ -69,13 +69,93 @@ void QgsAttributeTableFilterModel::sort( int column, Qt::SortOrder order )
QVariant QgsAttributeTableFilterModel::data( const QModelIndex& index, int role ) const QVariant QgsAttributeTableFilterModel::data( const QModelIndex& index, int role ) const
{ {
if ( index.column() == 0 ) if ( mColumnMapping.at( index.column() ) == -1 ) // actions
return "Wow";
else
{ {
QModelIndex sourceIndex = QSortFilterProxyModel::index( index.row(), index.column() - 1, index.parent() ); if ( role == TypeRole )
return QSortFilterProxyModel::data( sourceIndex, role ); return ColumnTypeActionButton;
else if ( role == QgsAttributeTableModel::FeatureIdRole )
{
QModelIndex fieldIndex = QSortFilterProxyModel::mapToSource( QSortFilterProxyModel::index( index.row(), 0, index.parent() ) );
return sourceModel()->data( fieldIndex, QgsAttributeTableModel::FeatureIdRole );
} }
}
else if ( role == TypeRole )
return ColumnTypeField;
return QSortFilterProxyModel::data( index, role );
}
QVariant QgsAttributeTableFilterModel::headerData( int section, Qt::Orientation orientation, int role ) const
{
if ( orientation == Qt::Horizontal )
{
if ( mColumnMapping.at( section ) == -1 && role == Qt::DisplayRole )
return tr( "Actions" );
else
return QSortFilterProxyModel::headerData( section, orientation, role );
}
else
return QSortFilterProxyModel::headerData( section, orientation, role );
}
int QgsAttributeTableFilterModel::actionColumnIndex() const
{
return mColumnMapping.indexOf( -1 );
}
int QgsAttributeTableFilterModel::columnCount( const QModelIndex& parent ) const
{
Q_UNUSED( parent );
return mColumnMapping.count();
}
void QgsAttributeTableFilterModel::setAttributeTableConfig( const QgsAttributeTableConfig& config )
{
int columnIndex = 0;
int configIndex = 0;
bool resetModel = false;
for ( ; configIndex < config.columns().size(); ++configIndex )
{
const QgsAttributeTableConfig::ColumnConfig& columnConfig = config.columns().at( configIndex );
// Hidden? No reason for further checks
if ( columnConfig.mHidden )
continue;
// Do the previous and current definition match?
if ( mColumnMapping.size() > columnIndex )
{
if (( columnConfig.mType == QgsAttributeTableConfig::Action && mColumnMapping.at( columnIndex ) == -1 ) ||
( columnConfig.mType == QgsAttributeTableConfig::Field && mColumnMapping.at( columnIndex ) == layer()->fieldNameIndex( columnConfig.mName ) ) )
{
++columnIndex;
continue;
}
else // There is a mismatch between previous and current configuration: remove all remaining columns, they will be readded
{
mColumnMapping.remove( columnIndex, mColumnMapping.count() - columnIndex );
}
}
if ( ! resetModel )
{
beginResetModel();
resetModel = true;
}
// New column? append
Q_ASSERT( mColumnMapping.size() == columnIndex );
if ( columnConfig.mType == QgsAttributeTableConfig::Action )
mColumnMapping.append( -1 );
else
mColumnMapping.append( layer()->fieldNameIndex( columnConfig.mName ) );
++columnIndex;
}
if ( resetModel )
endResetModel();
} }
void QgsAttributeTableFilterModel::setSelectedOnTop( bool selectedOnTop ) void QgsAttributeTableFilterModel::setSelectedOnTop( bool selectedOnTop )
@ -96,6 +176,12 @@ void QgsAttributeTableFilterModel::setSourceModel( QgsAttributeTableModel* sourc
{ {
mTableModel = sourceModel; mTableModel = sourceModel;
mColumnMapping.append( -1 ); // -1 for actions column
for ( int i = 0; i < mTableModel->columnCount(); ++i )
{
mColumnMapping.append( i );
}
QSortFilterProxyModel::setSourceModel( sourceModel ); QSortFilterProxyModel::setSourceModel( sourceModel );
} }
@ -310,14 +396,23 @@ QModelIndexList QgsAttributeTableFilterModel::fidToIndexList( QgsFeatureId fid )
return indexes; return indexes;
} }
QModelIndex QgsAttributeTableFilterModel::mapToMaster( const QModelIndex &proxyIndex ) const QModelIndex QgsAttributeTableFilterModel::mapToSource( const QModelIndex& proxyIndex ) const
{ {
// Master is source if ( !proxyIndex.isValid() )
return mapToSource( proxyIndex ); return QModelIndex();
int sourceColumn = mColumnMapping.at( proxyIndex.column() );
// For the action column there is no matching column in the source model: invalid
if ( sourceColumn == -1 )
return QModelIndex();
return QSortFilterProxyModel::mapToSource( index( proxyIndex.row(), mColumnMapping.at( proxyIndex.column() ), proxyIndex.parent() ) );
} }
QModelIndex QgsAttributeTableFilterModel::mapFromMaster( const QModelIndex &sourceIndex ) const QModelIndex QgsAttributeTableFilterModel::mapFromSource( const QModelIndex& sourceIndex ) const
{ {
// Master is source QModelIndex proxyIndex = QSortFilterProxyModel::mapFromSource( sourceIndex );
return mapFromSource( sourceIndex );
return index( proxyIndex.row(), mColumnMapping.indexOf( proxyIndex.column() ), proxyIndex.parent() );
} }

View File

@ -42,6 +42,17 @@ class GUI_EXPORT QgsAttributeTableFilterModel: public QSortFilterProxyModel, pub
ShowEdited ShowEdited
}; };
enum ColumnType
{
ColumnTypeField, //!< This column shows a field
ColumnTypeActionButton //!< This column shows action buttons
};
enum Role
{
TypeRole = QgsAttributeTableModel::UserRole //!< The type of a given column
};
/** /**
* *
@ -137,11 +148,16 @@ class GUI_EXPORT QgsAttributeTableFilterModel: public QSortFilterProxyModel, pub
QgsFeatureId rowToId( const QModelIndex& row ); QgsFeatureId rowToId( const QModelIndex& row );
QModelIndex fidToIndex( QgsFeatureId fid ) override; QModelIndex fidToIndex( QgsFeatureId fid ) override;
QModelIndexList fidToIndexList( QgsFeatureId fid ); QModelIndexList fidToIndexList( QgsFeatureId fid );
virtual QModelIndex mapToMaster( const QModelIndex &proxyIndex ) const; inline QModelIndex mapToMaster( const QModelIndex& proxyIndex ) const { return mapToSource( proxyIndex ); }
virtual QModelIndex mapFromMaster( const QModelIndex &sourceIndex ) const; inline QModelIndex mapFromMaster( const QModelIndex& sourceIndex ) const { return mapFromSource( sourceIndex ); }
virtual QModelIndex mapToSource( const QModelIndex& proxyIndex ) const override;
virtual QModelIndex mapFromSource( const QModelIndex& sourceIndex ) const override;
/** /**
* Sort by the given column using the given order. * Sort by the given column using the given order.
@ -157,6 +173,23 @@ class GUI_EXPORT QgsAttributeTableFilterModel: public QSortFilterProxyModel, pub
virtual QVariant data( const QModelIndex& index, int role ) const override; virtual QVariant data( const QModelIndex& index, int role ) const override;
QVariant headerData( int section, Qt::Orientation orientation, int role ) const override;
/**
* Get the index of the first column that contains an action widget.
* Returns -1 if none is defined.
*/
int actionColumnIndex() const;
int columnCount( const QModelIndex &parent ) const override;
/**
* Set the attribute table configuration to control which fields are shown,
* in which order they are shown as well as if and where an action column
* is shown.
*/
void setAttributeTableConfig( const QgsAttributeTableConfig& config );
protected: protected:
/** /**
* Returns true if the source row will be accepted * Returns true if the source row will be accepted
@ -195,6 +228,8 @@ class GUI_EXPORT QgsAttributeTableFilterModel: public QSortFilterProxyModel, pub
FilterMode mFilterMode; FilterMode mFilterMode;
bool mSelectedOnTop; bool mSelectedOnTop;
QgsAttributeTableModel* mTableModel; QgsAttributeTableModel* mTableModel;
QVector<int> mColumnMapping;
}; };
#endif #endif

View File

@ -51,9 +51,10 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel
public: public:
enum Role enum Role
{ {
SortRole = Qt::UserRole + 1, SortRole = Qt::UserRole + 1, //!< Role used for sorting
FeatureIdRole = Qt::UserRole + 2, FeatureIdRole, //!< Get the feature id of the feature in this row
FieldIndexRole = Qt::UserRole + 3 FieldIndexRole, //!< Get the field index of this column
UserRole //!< Start further roles starting from this role
}; };
public: public:

View File

@ -64,14 +64,10 @@ QgsAttributeTableView::QgsAttributeTableView( QWidget *parent )
connect( verticalHeader(), SIGNAL( sectionPressed( int ) ), this, SLOT( selectRow( int ) ) ); connect( verticalHeader(), SIGNAL( sectionPressed( int ) ), this, SLOT( selectRow( int ) ) );
connect( verticalHeader(), SIGNAL( sectionEntered( int ) ), this, SLOT( _q_selectRow( int ) ) ); connect( verticalHeader(), SIGNAL( sectionEntered( int ) ), this, SLOT( _q_selectRow( int ) ) );
connect( horizontalHeader(), SIGNAL( sectionResized( int, int, int ) ), this, SLOT( columnSizeChanged( int, int, int ) ) );
connect( horizontalHeader(), SIGNAL( sortIndicatorChanged( int, Qt::SortOrder ) ), this, SLOT( showHorizontalSortIndicator() ) ); connect( horizontalHeader(), SIGNAL( sortIndicatorChanged( int, Qt::SortOrder ) ), this, SLOT( showHorizontalSortIndicator() ) );
} }
QgsAttributeTableView::~QgsAttributeTableView()
{
delete mActionPopup;
}
bool QgsAttributeTableView::eventFilter( QObject *object, QEvent *event ) bool QgsAttributeTableView::eventFilter( QObject *object, QEvent *event )
{ {
if ( object == verticalHeader()->viewport() ) if ( object == verticalHeader()->viewport() )
@ -123,6 +119,9 @@ void QgsAttributeTableView::setModel( QgsAttributeTableFilterModel* filterModel
connect( mFeatureSelectionModel, SIGNAL( requestRepaint( QModelIndexList ) ), this, SLOT( repaintRequested( QModelIndexList ) ) ); connect( mFeatureSelectionModel, SIGNAL( requestRepaint( QModelIndexList ) ), this, SLOT( repaintRequested( QModelIndexList ) ) );
connect( mFeatureSelectionModel, SIGNAL( requestRepaint() ), this, SLOT( repaintRequested() ) ); connect( mFeatureSelectionModel, SIGNAL( requestRepaint() ), this, SLOT( repaintRequested() ) );
} }
mActionWidget = createActionWidget( 0 );
mActionWidget->setVisible( false );
} }
void QgsAttributeTableView::setFeatureSelectionManager( QgsIFeatureSelectionManager* featureSelectionManager ) void QgsAttributeTableView::setFeatureSelectionManager( QgsIFeatureSelectionManager* featureSelectionManager )
@ -136,19 +135,35 @@ void QgsAttributeTableView::setFeatureSelectionManager( QgsIFeatureSelectionMana
mFeatureSelectionModel->setFeatureSelectionManager( mFeatureSelectionManager ); mFeatureSelectionModel->setFeatureSelectionManager( mFeatureSelectionManager );
} }
QWidget* QgsAttributeTableView::createActionWidget() QWidget* QgsAttributeTableView::createActionWidget( QgsFeatureId fid )
{ {
QToolButton* toolButton = new QToolButton( this ); QToolButton* toolButton = new QToolButton( this );
toolButton->setPopupMode( QToolButton::MenuButtonPopup );
for ( int i = 0; i < mFilterModel->layer()->actions()->size(); ++i ) QgsActionManager* actions = mFilterModel->layer()->actions();
for ( int i = 0; i < actions->size(); ++i )
{ {
const QgsAction& action = mFilterModel->layer()->actions()->at( i ); const QgsAction& action = actions->at( i );
QAction* act = new QAction( action.icon(), action.shortTitle(), toolButton ); QAction* act = new QAction( action.icon(), action.shortTitle().isEmpty() ? action.name() : action.shortTitle(), toolButton );
act->setToolTip( action.name() );
act->setData( i );
act->setProperty( "fid", fid );
connect( act, SIGNAL( triggered( bool ) ), this, SLOT( actionTriggered() ) );
toolButton->addAction( act ); toolButton->addAction( act );
if ( actions->defaultAction() == i )
toolButton->setDefaultAction( act );
} }
if ( !toolButton->actions().isEmpty() && actions->defaultAction() == -1 )
toolButton->setDefaultAction( toolButton->actions().first() );
updateActionImage( toolButton );
return toolButton; return toolButton;
} }
@ -176,12 +191,12 @@ void QgsAttributeTableView::mouseReleaseEvent( QMouseEvent *event )
void QgsAttributeTableView::mouseMoveEvent( QMouseEvent *event ) void QgsAttributeTableView::mouseMoveEvent( QMouseEvent *event )
{ {
QModelIndex index = indexAt( event->pos() ); QModelIndex index = indexAt( event->pos() );
if ( index.column() == 0 ) if ( index.data( QgsAttributeTableFilterModel::TypeRole ) == QgsAttributeTableFilterModel::ColumnTypeActionButton )
{ {
Q_ASSERT( index.isValid() ); Q_ASSERT( index.isValid() );
if ( !indexWidget( index ) ) if ( !indexWidget( index ) )
setIndexWidget( index, createActionWidget() ); setIndexWidget( index, createActionWidget( mFilterModel->data( index, QgsAttributeTableModel::FeatureIdRole ).toLongLong() ) );
} }
setSelectionMode( QAbstractItemView::NoSelection ); setSelectionMode( QAbstractItemView::NoSelection );
@ -246,7 +261,7 @@ void QgsAttributeTableView::contextMenuEvent( QContextMenuEvent* event )
if ( !vlayer ) if ( !vlayer )
return; return;
mActionPopup = new QMenu(); mActionPopup = new QMenu( this );
mActionPopup->addAction( tr( "Select All" ), this, SLOT( selectAll() ), QKeySequence::SelectAll ); mActionPopup->addAction( tr( "Select All" ), this, SLOT( selectAll() ), QKeySequence::SelectAll );
@ -318,3 +333,32 @@ void QgsAttributeTableView::showHorizontalSortIndicator()
{ {
horizontalHeader()->setSortIndicatorShown( true ); horizontalHeader()->setSortIndicatorShown( true );
} }
void QgsAttributeTableView::actionTriggered()
{
QAction* action = qobject_cast<QAction*>( sender() );
QgsFeatureId fid = action->property( "fid" ).toLongLong();
QgsFeature f;
mFilterModel->layerCache()->getFeatures( QgsFeatureRequest( fid ) ).nextFeature( f );
mFilterModel->layer()->actions()->doAction( action->data().toInt(), f );
}
void QgsAttributeTableView::columnSizeChanged( int index, int oldWidth, int newWidth )
{
Q_UNUSED( oldWidth )
if ( mFilterModel->actionColumnIndex() == index )
{
mActionWidget->resize( newWidth, mActionWidget->height() );
updateActionImage( mActionWidget );
}
}
void QgsAttributeTableView::updateActionImage( QWidget* widget )
{
QImage image( widget->size(), QImage::Format_ARGB32_Premultiplied );
QPainter painter( &image );
widget->render( &painter );
mTableDelegate->setActionWidgetImage( image );
}

View File

@ -47,7 +47,6 @@ class GUI_EXPORT QgsAttributeTableView : public QTableView
public: public:
QgsAttributeTableView( QWidget* parent = nullptr ); QgsAttributeTableView( QWidget* parent = nullptr );
virtual ~QgsAttributeTableView();
virtual void setModel( QgsAttributeTableFilterModel* filterModel ); virtual void setModel( QgsAttributeTableFilterModel* filterModel );
@ -140,9 +139,12 @@ class GUI_EXPORT QgsAttributeTableView : public QTableView
private slots: private slots:
void modelDeleted(); void modelDeleted();
void showHorizontalSortIndicator(); void showHorizontalSortIndicator();
void actionTriggered();
void columnSizeChanged( int index, int oldWidth, int newWidth );
private: private:
QWidget* createActionWidget(); void updateActionImage( QWidget* widget );
QWidget* createActionWidget( QgsFeatureId fid );
void selectRow( int row, bool anchor ); void selectRow( int row, bool anchor );
QgsAttributeTableModel* mMasterModel; QgsAttributeTableModel* mMasterModel;
@ -154,6 +156,7 @@ class GUI_EXPORT QgsAttributeTableView : public QTableView
QMenu *mActionPopup; QMenu *mActionPopup;
int mRowSectionAnchor; int mRowSectionAnchor;
QItemSelectionModel::SelectionFlag mCtrlDragSelectionFlag; QItemSelectionModel::SelectionFlag mCtrlDragSelectionFlag;
QWidget* mActionWidget;
}; };
#endif #endif

View File

@ -464,6 +464,11 @@ void QgsDualView::setFeatureSelectionManager( QgsIFeatureSelectionManager* featu
mFeatureSelectionManager = featureSelectionManager; mFeatureSelectionManager = featureSelectionManager;
} }
void QgsDualView::setAttributeTableConfig( const QgsAttributeTableConfig& config )
{
mFilterModel->setAttributeTableConfig( config );
}
void QgsDualView::progress( int i, bool& cancel ) void QgsDualView::progress( int i, bool& cancel )
{ {
if ( !mProgressDlg ) if ( !mProgressDlg )

View File

@ -141,6 +141,9 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
*/ */
void setFilteredFeatures( const QgsFeatureIds& filteredFeatures ); void setFilteredFeatures( const QgsFeatureIds& filteredFeatures );
/**
* Get a list of currently visible feature ids.
*/
QgsFeatureIds filteredFeatures() { return mFilterModel->filteredFeatures(); } QgsFeatureIds filteredFeatures() { return mFilterModel->filteredFeatures(); }
/** /**
@ -169,7 +172,13 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
* *
* @return The table view * @return The table view
*/ */
QgsAttributeTableView* tableView() { return mTableView; }; QgsAttributeTableView* tableView() { return mTableView; }
/**
* Set the attribute table config which should be used to control
* the appearance of the attribute table.
*/
void setAttributeTableConfig( const QgsAttributeTableConfig& config );
protected: protected:
/** /**

View File

@ -41,18 +41,30 @@
#include "qgseditorwidgetregistry.h" #include "qgseditorwidgetregistry.h"
QgsOrganizeTableColumnsDialog::QgsOrganizeTableColumnsDialog( const QgsVectorLayer* vl, const QStringList visble, QWidget *parent, Qt::WindowFlags flags ) QgsOrganizeTableColumnsDialog::QgsOrganizeTableColumnsDialog( const QgsVectorLayer* vl, QWidget* parent, Qt::WindowFlags flags )
: QDialog( parent, flags ) : QDialog( parent, flags )
{ {
setupUi( this ); setupUi( this );
if ( vl ) if ( vl )
{ {
QgsAttributeTableConfig config = vl->attributeTableConfig();
config.update( vl->fields() );
mFieldsList->clear(); mFieldsList->clear();
const QgsFields& layerAttributes = vl->fields();
for ( int idx = 0; idx < layerAttributes.count(); ++idx ) Q_FOREACH ( const QgsAttributeTableConfig::ColumnConfig& columnConfig, config.columns() )
{ {
QListWidgetItem* item = new QListWidgetItem( layerAttributes[idx].name(), mFieldsList ); QListWidgetItem* item;
item->setCheckState( visble.contains( layerAttributes[idx].name() ) ? Qt::Checked : Qt::Unchecked ); if ( columnConfig.mType == QgsAttributeTableConfig::Action )
{
item = new QListWidgetItem( tr( "[Action Widget]" ), mFieldsList );
item->setIcon( QgsApplication::getThemeIcon( "/propertyicons/action.svg" ) );
}
else
{
int idx = vl->fieldNameIndex( columnConfig.mName );
item = new QListWidgetItem( vl->attributeDisplayName( idx ), mFieldsList );
switch ( vl->fields().fieldOrigin( idx ) ) switch ( vl->fields().fieldOrigin( idx ) )
{ {
case QgsFields::OriginExpression: case QgsFields::OriginExpression:
@ -67,31 +79,38 @@ QgsOrganizeTableColumnsDialog::QgsOrganizeTableColumnsDialog( const QgsVectorLay
item->setIcon( QgsApplication::getThemeIcon( "/propertyicons/attributes.png" ) ); item->setIcon( QgsApplication::getThemeIcon( "/propertyicons/attributes.png" ) );
break; break;
} }
}
item->setData( Qt::UserRole, idx ); item->setCheckState( columnConfig.mHidden ? Qt::Unchecked : Qt::Checked );
item->setData( Qt::UserRole, QVariant::fromValue( columnConfig ) );
} }
} }
QSettings settings; QSettings settings;
restoreGeometry( settings.value( "/Windows/QgsFilterTableFieldsDialog/geometry" ).toByteArray() ); restoreGeometry( settings.value( "/Windows/QgsOrganizeTableColumnsDialog/geometry" ).toByteArray() );
} }
QgsOrganizeTableColumnsDialog::~QgsOrganizeTableColumnsDialog() QgsOrganizeTableColumnsDialog::~QgsOrganizeTableColumnsDialog()
{ {
QSettings settings; QSettings settings;
settings.setValue( "/Windows/QgsFilterTableFieldsDialog/geometry", saveGeometry() ); settings.setValue( "/Windows/QgsOrganizeTableColumnsDialog/geometry", saveGeometry() );
} }
QStringList QgsOrganizeTableColumnsDialog::selectedFields() const QgsAttributeTableConfig QgsOrganizeTableColumnsDialog::config() const
{ {
QStringList selectionList; QVector<QgsAttributeTableConfig::ColumnConfig> columns;
for ( int i = 0 ; i < mFieldsList->count() ; i++ ) for ( int i = 0 ; i < mFieldsList->count() ; i++ )
{ {
const QListWidgetItem* item = mFieldsList->item( i ); const QListWidgetItem* item = mFieldsList->item( i );
if ( item->checkState() == Qt::Checked ) QgsAttributeTableConfig::ColumnConfig columnConfig = item->data( Qt::UserRole ).value<QgsAttributeTableConfig::ColumnConfig>();
{
selectionList.push_back( item->text() ); columnConfig.mHidden = item->checkState() == Qt::Unchecked;
columns.append( columnConfig );
} }
}
return selectionList; QgsAttributeTableConfig config;
config.setColumns( columns );
return config;
} }

View File

@ -21,6 +21,8 @@
#include "ui_qgsorganizetablecolumnsdialog.h" #include "ui_qgsorganizetablecolumnsdialog.h"
#include "qgsattributetableconfig.h"
class QgsVectorLayer; class QgsVectorLayer;
class GUI_EXPORT QgsOrganizeTableColumnsDialog : public QDialog, private Ui::QgsOrganizeTableColumnsDialog class GUI_EXPORT QgsOrganizeTableColumnsDialog : public QDialog, private Ui::QgsOrganizeTableColumnsDialog
@ -35,17 +37,17 @@ class GUI_EXPORT QgsOrganizeTableColumnsDialog : public QDialog, private Ui::Qgs
* @param parent parent object * @param parent parent object
* @param flags window flags * @param flags window flags
*/ */
QgsOrganizeTableColumnsDialog( const QgsVectorLayer* vl, QStringList visible, QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::Window ); QgsOrganizeTableColumnsDialog( const QgsVectorLayer* vl, QWidget* parent = nullptr, Qt::WindowFlags flags = Qt::Window );
/** /**
* Destructor * Destructor
*/ */
~QgsOrganizeTableColumnsDialog(); ~QgsOrganizeTableColumnsDialog();
/** /**
* Get the selected fields name * Get the updated configuration
* @return The selected fields name
*/ */
QStringList selectedFields() const; QgsAttributeTableConfig config() const;
}; };
#endif #endif

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>240</width> <width>392</width>
<height>219</height> <height>357</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -15,7 +15,14 @@
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QListWidget" name="mFieldsList"/> <widget class="QListWidget" name="mFieldsList">
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::MoveAction</enum>
</property>
</widget>
</item> </item>
<item> <item>
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="buttonBox">