From c4b74c9266d48bb864d4c424f87adc8fa940d9bf Mon Sep 17 00:00:00 2001 From: Radim Blazek Date: Sun, 11 Nov 2012 19:51:38 +0100 Subject: [PATCH] Categorized vector GUI DnD and sorting, fixes #2816 --- .../qgscategorizedsymbolrendererv2widget.sip | 12 +- src/core/qgis.cpp | 36 ++ src/core/qgis.h | 5 + .../qgscategorizedsymbolrendererv2.cpp | 77 ++- .../qgscategorizedsymbolrendererv2.h | 10 + .../symbology-ng/qgssymbollayerv2utils.cpp | 8 +- .../qgscategorizedsymbolrendererv2widget.cpp | 444 +++++++++++++----- .../qgscategorizedsymbolrendererv2widget.h | 57 ++- .../qgscategorizedsymbolrendererv2widget.ui | 13 +- 9 files changed, 499 insertions(+), 163 deletions(-) diff --git a/python/gui/symbology-ng/qgscategorizedsymbolrendererv2widget.sip b/python/gui/symbology-ng/qgscategorizedsymbolrendererv2widget.sip index 57c8e14fb89..e73b831e290 100644 --- a/python/gui/symbology-ng/qgscategorizedsymbolrendererv2widget.sip +++ b/python/gui/symbology-ng/qgscategorizedsymbolrendererv2widget.sip @@ -16,10 +16,10 @@ class QgsCategorizedSymbolRendererV2Widget : QgsRendererV2Widget void changeCategorizedSymbol(); void categoryColumnChanged(); void categoriesDoubleClicked( const QModelIndex & idx ); + void addCategory(); void addCategories(); - void deleteCategory(); + void deleteCategories(); void deleteAllCategories(); - void changeCurrentValue( QStandardItem * item ); void rotationFieldChanged( QString fldName ); void sizeScaleFieldChanged( QString fldName ); @@ -39,19 +39,11 @@ class QgsCategorizedSymbolRendererV2Widget : QgsRendererV2Widget //! populate column combo void populateColumns(); - void addCategory( const QgsRendererCategoryV2& cat ); - //! return row index for the currently selected category (-1 if on no selection) int currentCategoryRow(); - //! return key for the currently selected category - QVariant currentCategory(); - void changeCategorySymbol(); QList selectedSymbols(); void refreshSymbolView(); - - protected slots: - void addCategory(); }; diff --git a/src/core/qgis.cpp b/src/core/qgis.cpp index 7a07cd81494..5f40481b42d 100644 --- a/src/core/qgis.cpp +++ b/src/core/qgis.cpp @@ -20,6 +20,9 @@ #include "qgsversion.h" #endif #include +#include +#include +#include #include "qgsconfig.h" #include "qgslogger.h" @@ -140,3 +143,36 @@ void QgsFree( void *ptr ) { free( ptr ); } + +bool qgsVariantLessThan( const QVariant& lhs, const QVariant& rhs ) +{ + switch ( lhs.type() ) + { + case QVariant::Int: + return lhs.toInt() < rhs.toInt(); + case QVariant::UInt: + return lhs.toUInt() < rhs.toUInt(); + case QVariant::LongLong: + return lhs.toLongLong() < rhs.toLongLong(); + case QVariant::ULongLong: + return lhs.toULongLong() < rhs.toULongLong(); + case QVariant::Double: + return lhs.toDouble() < rhs.toDouble(); + case QVariant::Char: + return lhs.toChar() < rhs.toChar(); + case QVariant::Date: + return lhs.toDate() < rhs.toDate(); + case QVariant::Time: + return lhs.toTime() < rhs.toTime(); + case QVariant::DateTime: + return lhs.toDateTime() < rhs.toDateTime(); + default: + return QString::localeAwareCompare( lhs.toString(), rhs.toString() ) < 0; + } +} + +bool qgsVariantGreaterThan( const QVariant& lhs, const QVariant& rhs ) +{ + return ! qgsVariantLessThan( lhs, rhs ); +} + diff --git a/src/core/qgis.h b/src/core/qgis.h index 4e9918c74d6..cfea676d269 100644 --- a/src/core/qgis.h +++ b/src/core/qgis.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -194,6 +195,10 @@ inline bool doubleNearSig( double a, double b, int significantDigits = 10 ) qRound( ar * pow( 10.0, significantDigits ) ) == qRound( br * pow( 10.0, significantDigits ) ) ; } +bool qgsVariantLessThan( const QVariant& lhs, const QVariant& rhs ); + +bool qgsVariantGreaterThan( const QVariant& lhs, const QVariant& rhs ); + /** Allocates size bytes and returns a pointer to the allocated memory. Works like C malloc() but prints debug message by QgsLogger if allocation fails. @param size size in bytes diff --git a/src/core/symbology-ng/qgscategorizedsymbolrendererv2.cpp b/src/core/symbology-ng/qgscategorizedsymbolrendererv2.cpp index 12e443600b4..b7f7c930c52 100644 --- a/src/core/symbology-ng/qgscategorizedsymbolrendererv2.cpp +++ b/src/core/symbology-ng/qgscategorizedsymbolrendererv2.cpp @@ -12,6 +12,7 @@ * (at your option) any later version. * * * ***************************************************************************/ +#include #include "qgscategorizedsymbolrendererv2.h" @@ -27,21 +28,40 @@ #include #include // for legend +QgsRendererCategoryV2::QgsRendererCategoryV2() + : mValue(), mSymbol( 0 ), mLabel() +{ +} + QgsRendererCategoryV2::QgsRendererCategoryV2( QVariant value, QgsSymbolV2* symbol, QString label ) : mValue( value ), mSymbol( symbol ), mLabel( label ) { } QgsRendererCategoryV2::QgsRendererCategoryV2( const QgsRendererCategoryV2& cat ) - : mValue( cat.mValue ), mLabel( cat.mLabel ) + : mValue( cat.mValue ), mSymbol( 0 ), mLabel( cat.mLabel ) { - mSymbol = cat.mSymbol->clone(); + if ( cat.mSymbol ) + { + mSymbol = cat.mSymbol->clone(); + } } - QgsRendererCategoryV2::~QgsRendererCategoryV2() { - delete mSymbol; + if ( mSymbol ) delete mSymbol; +} + +QgsRendererCategoryV2& QgsRendererCategoryV2::operator=( const QgsRendererCategoryV2 & cat ) +{ + mValue = cat.mValue; + mLabel = cat.mLabel; + mSymbol = 0; + if ( cat.mSymbol ) + { + mSymbol = cat.mSymbol->clone(); + } + return *this; } QVariant QgsRendererCategoryV2::value() const @@ -293,6 +313,55 @@ void QgsCategorizedSymbolRendererV2::deleteAllCategories() mCategories.clear(); } +void QgsCategorizedSymbolRendererV2::moveCategory( int from, int to ) +{ + if ( from < 0 || from >= mCategories.size() || to < 0 || to >= mCategories.size() ) return; + mCategories.move( from, to ); +} + +bool valueLessThan( const QgsRendererCategoryV2 &c1, const QgsRendererCategoryV2 &c2 ) +{ + return qgsVariantLessThan( c1.value(), c2.value() ); +} +bool valueGreaterThan( const QgsRendererCategoryV2 &c1, const QgsRendererCategoryV2 &c2 ) +{ + return qgsVariantGreaterThan( c1.value(), c2.value() ); +} + +void QgsCategorizedSymbolRendererV2::sortByValue( Qt::SortOrder order ) +{ + if ( order == Qt::AscendingOrder ) + { + qSort( mCategories.begin(), mCategories.end(), valueLessThan ); + } + else + { + qSort( mCategories.begin(), mCategories.end(), valueGreaterThan ); + } +} + +bool labelLessThan( const QgsRendererCategoryV2 &c1, const QgsRendererCategoryV2 &c2 ) +{ + return QString::localeAwareCompare( c1.label(), c2.label() ) < 0; +} + +bool labelGreaterThan( const QgsRendererCategoryV2 &c1, const QgsRendererCategoryV2 &c2 ) +{ + return !labelLessThan( c1, c2 ); +} + +void QgsCategorizedSymbolRendererV2::sortByLabel( Qt::SortOrder order ) +{ + if ( order == Qt::AscendingOrder ) + { + qSort( mCategories.begin(), mCategories.end(), labelLessThan ); + } + else + { + qSort( mCategories.begin(), mCategories.end(), labelGreaterThan ); + } +} + void QgsCategorizedSymbolRendererV2::startRender( QgsRenderContext& context, const QgsVectorLayer *vlayer ) { // make sure that the hash table is up to date diff --git a/src/core/symbology-ng/qgscategorizedsymbolrendererv2.h b/src/core/symbology-ng/qgscategorizedsymbolrendererv2.h index 2421638cad8..0e32cca636a 100644 --- a/src/core/symbology-ng/qgscategorizedsymbolrendererv2.h +++ b/src/core/symbology-ng/qgscategorizedsymbolrendererv2.h @@ -27,6 +27,7 @@ class QgsVectorLayer; class CORE_EXPORT QgsRendererCategoryV2 { public: + QgsRendererCategoryV2( ); //! takes ownership of symbol QgsRendererCategoryV2( QVariant value, QgsSymbolV2* symbol, QString label ); @@ -34,8 +35,11 @@ class CORE_EXPORT QgsRendererCategoryV2 //! copy constructor QgsRendererCategoryV2( const QgsRendererCategoryV2& cat ); + ~QgsRendererCategoryV2(); + QgsRendererCategoryV2& operator=( const QgsRendererCategoryV2& cat ); + QVariant value() const; QgsSymbolV2* symbol() const; QString label() const; @@ -100,6 +104,12 @@ class CORE_EXPORT QgsCategorizedSymbolRendererV2 : public QgsFeatureRendererV2 bool deleteCategory( int catIndex ); void deleteAllCategories(); + //! Moves the category at index position from to index position to. + void moveCategory( int from, int to ); + + void sortByValue( Qt::SortOrder order = Qt::AscendingOrder ); + void sortByLabel( Qt::SortOrder order = Qt::AscendingOrder ); + QString classAttribute() const { return mAttrName; } void setClassAttribute( QString attr ) { mAttrName = attr; } diff --git a/src/core/symbology-ng/qgssymbollayerv2utils.cpp b/src/core/symbology-ng/qgssymbollayerv2utils.cpp index 29e51c63307..18c3b30b0ff 100644 --- a/src/core/symbology-ng/qgssymbollayerv2utils.cpp +++ b/src/core/symbology-ng/qgssymbollayerv2utils.cpp @@ -2528,6 +2528,7 @@ void QgsSymbolLayerV2Utils::multiplyImageOpacity( QImage* image, qreal alpha ) } } +#if 0 static bool _QVariantLessThan( const QVariant& lhs, const QVariant& rhs ) { switch ( lhs.type() ) @@ -2559,16 +2560,19 @@ static bool _QVariantGreaterThan( const QVariant& lhs, const QVariant& rhs ) { return ! _QVariantLessThan( lhs, rhs ); } +#endif void QgsSymbolLayerV2Utils::sortVariantList( QList& list, Qt::SortOrder order ) { if ( order == Qt::AscendingOrder ) { - qSort( list.begin(), list.end(), _QVariantLessThan ); + //qSort( list.begin(), list.end(), _QVariantLessThan ); + qSort( list.begin(), list.end(), qgsVariantLessThan ); } else // Qt::DescendingOrder { - qSort( list.begin(), list.end(), _QVariantGreaterThan ); + //qSort( list.begin(), list.end(), _QVariantGreaterThan ); + qSort( list.begin(), list.end(), qgsVariantGreaterThan ); } } diff --git a/src/gui/symbology-ng/qgscategorizedsymbolrendererv2widget.cpp b/src/gui/symbology-ng/qgscategorizedsymbolrendererv2widget.cpp index 245cf75229e..855fde274a9 100644 --- a/src/gui/symbology-ng/qgscategorizedsymbolrendererv2widget.cpp +++ b/src/gui/symbology-ng/qgscategorizedsymbolrendererv2widget.cpp @@ -12,6 +12,7 @@ * (at your option) any later version. * * * ***************************************************************************/ + #include "qgscategorizedsymbolrendererv2widget.h" #include "qgscategorizedsymbolrendererv2.h" @@ -32,6 +33,264 @@ #include #include +QgsCategorizedSymbolRendererV2Model::QgsCategorizedSymbolRendererV2Model( QObject * parent ) : QAbstractItemModel( parent ) + , mRenderer( 0 ) + , mMimeFormat( "application/x-qgscategorizedsymbolrendererv2model" ) +{ +} + +void QgsCategorizedSymbolRendererV2Model::setRenderer( QgsCategorizedSymbolRendererV2* renderer ) +{ + if ( mRenderer ) + { + beginRemoveRows( QModelIndex(), 0, mRenderer->categories().size() - 1 ); + mRenderer = 0; + endRemoveRows(); + } + if ( renderer ) + { + beginInsertRows( QModelIndex(), 0, renderer->categories().size() - 1 ); + mRenderer = renderer; + endInsertRows(); + } +} + +void QgsCategorizedSymbolRendererV2Model::addCategory( const QgsRendererCategoryV2 &cat ) +{ + if ( !mRenderer ) return; + int idx = mRenderer->categories().size(); + beginInsertRows( QModelIndex(), idx, idx ); + mRenderer->addCategory( cat ); + endInsertRows(); +} + + +Qt::ItemFlags QgsCategorizedSymbolRendererV2Model::flags( const QModelIndex & index ) const +{ + Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; + if ( index.column() == 1 || index.column() == 2 ) + { + flags |= Qt::ItemIsEditable; + } + return flags; +} + +Qt::DropActions QgsCategorizedSymbolRendererV2Model::supportedDropActions() const +{ + return Qt::MoveAction; +} + +QVariant QgsCategorizedSymbolRendererV2Model::data( const QModelIndex &index, int role ) const +{ + if ( !index.isValid() || !mRenderer ) return QVariant(); + + const QgsRendererCategoryV2 category = mRenderer->categories().value( index.row() ); + + if ( role == Qt::DisplayRole || role == Qt::ToolTipRole ) + { + switch ( index.column() ) + { + case 1: return category.value().toString(); + case 2: return category.label(); + default: return QVariant(); + } + } + else if ( role == Qt::DecorationRole && index.column() == 0 && category.symbol() ) + { + return QgsSymbolLayerV2Utils::symbolPreviewIcon( category.symbol(), QSize( 16, 16 ) ); + } + else if ( role == Qt::TextAlignmentRole ) + { + return ( index.column() == 0 ) ? Qt::AlignHCenter : Qt::AlignLeft; + } + else if ( role == Qt::EditRole ) + { + switch ( index.column() ) + { + case 1: return category.value(); + case 2: return category.label(); + default: return QVariant(); + } + } + + return QVariant(); +} + +bool QgsCategorizedSymbolRendererV2Model::setData( const QModelIndex & index, const QVariant & value, int role ) +{ + if ( !index.isValid() || role != Qt::EditRole ) + return false; + + switch ( index.column() ) + { + case 1: // value + { + // try to preserve variant type for this value + QVariant val; + switch ( mRenderer->categories().value( index.row() ).value().type() ) + { + case QVariant::Int: + val = value.toInt(); + break; + case QVariant::Double: + val = value.toDouble(); + break; + default: + val = value.toString(); + break; + } + mRenderer->updateCategoryValue( index.row(), val ); + break; + } + case 2: // label + mRenderer->updateCategoryLabel( index.row(), value.toString() ); + break; + default: + return false; + } + + emit dataChanged( index, index ); + return true; +} + +QVariant QgsCategorizedSymbolRendererV2Model::headerData( int section, Qt::Orientation orientation, int role ) const +{ + if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 3 ) + { + QStringList lst; lst << tr( "Symbol" ) << tr( "Value" ) << tr( "Label" ); + return lst.value( section ); + } + return QVariant(); +} + +int QgsCategorizedSymbolRendererV2Model::rowCount( const QModelIndex &parent ) const +{ + if ( parent.column() > 0 || !mRenderer ) return 0; + return mRenderer->categories().size(); +} + +int QgsCategorizedSymbolRendererV2Model::columnCount( const QModelIndex & index ) const +{ + Q_UNUSED( index ); + return 3; +} + +QModelIndex QgsCategorizedSymbolRendererV2Model::index( int row, int column, const QModelIndex &parent ) const +{ + if ( hasIndex( row, column, parent ) ) + { + return createIndex( row, column, 0 ); + } + return QModelIndex(); +} + +QModelIndex QgsCategorizedSymbolRendererV2Model::parent( const QModelIndex &index ) const +{ + Q_UNUSED( index ); + return QModelIndex(); +} + +QStringList QgsCategorizedSymbolRendererV2Model::mimeTypes() const +{ + QStringList types; + types << mMimeFormat; + return types; +} + +QMimeData *QgsCategorizedSymbolRendererV2Model::mimeData( const QModelIndexList &indexes ) const +{ + QMimeData *mimeData = new QMimeData(); + QByteArray encodedData; + + QDataStream stream( &encodedData, QIODevice::WriteOnly ); + + // Create list of rows + foreach ( const QModelIndex &index, indexes ) + { + if ( !index.isValid() || index.column() != 0 ) + continue; + + stream << index.row(); + } + mimeData->setData( mMimeFormat, encodedData ); + return mimeData; +} + +bool QgsCategorizedSymbolRendererV2Model::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent ) +{ + Q_UNUSED( row ); + Q_UNUSED( column ); + if ( action != Qt::MoveAction ) return true; + + if ( !data->hasFormat( mMimeFormat ) ) return false; + + QByteArray encodedData = data->data( mMimeFormat ); + QDataStream stream( &encodedData, QIODevice::ReadOnly ); + + QVector rows; + while ( !stream.atEnd() ) + { + int r; + stream >> r; + rows.append( r ); + } + + int to = parent.row(); + for ( int i = rows.size() - 1; i >= 0; i-- ) + { + QgsDebugMsg( QString( "move %1 to %2" ).arg( rows[i] ).arg( to ) ); + int t = to; + // moveCategory first removes and then inserts + if ( rows[i] < to ) t--; + mRenderer->moveCategory( rows[i], t ); + // current moved under another, shift its index up + for ( int j = 0; j < i; j++ ) + { + if ( to < rows[j] && rows[i] > rows[j] ) rows[j] += 1; + } + // removed under 'to' so the target shifted down + if ( rows[i] < to ) to--; + } + emit dataChanged( createIndex( 0, 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) ); + emit rowsMoved(); + return false; +} + +void QgsCategorizedSymbolRendererV2Model::removeRows( QList rows ) +{ + for ( int i = rows.size() - 1; i >= 0; i-- ) + { + beginRemoveRows( QModelIndex(), rows[i], rows[i] ); + mRenderer->deleteCategory( rows[i] ); + endRemoveRows(); + } +} + +void QgsCategorizedSymbolRendererV2Model::removeAllRows( ) +{ + beginRemoveRows( QModelIndex(), 0, mRenderer->categories().size() - 1 ); + mRenderer->deleteAllCategories(); + endRemoveRows(); +} + +void QgsCategorizedSymbolRendererV2Model::sort( int column, Qt::SortOrder order ) +{ + QgsDebugMsg( "Entered" ); + if ( column == 0 ) + { + return; + } + if ( column == 1 ) + { + mRenderer->sortByValue( order ); + } + else if ( column == 2 ) + { + mRenderer->sortByLabel( order ); + } + emit dataChanged( createIndex( 0, 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) ); + QgsDebugMsg( "Done" ); +} QgsRendererV2Widget* QgsCategorizedSymbolRendererV2Widget::create( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer ) { @@ -40,6 +299,8 @@ QgsRendererV2Widget* QgsCategorizedSymbolRendererV2Widget::create( QgsVectorLaye QgsCategorizedSymbolRendererV2Widget::QgsCategorizedSymbolRendererV2Widget( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer ) : QgsRendererV2Widget( layer, style ) + , mRenderer( 0 ) + , mModel( 0 ) { // try to recognize the previous renderer @@ -75,14 +336,17 @@ QgsCategorizedSymbolRendererV2Widget::QgsCategorizedSymbolRendererV2Widget( QgsV cboCategorizedColorRamp->setCurrentIndex( index ); } - QStandardItemModel* m = new QStandardItemModel( this ); - QStringList labels; - labels << tr( "Symbol" ) << tr( "Value" ) << tr( "Label" ); - m->setHorizontalHeaderLabels( labels ); - viewCategories->setModel( m ); + mModel = new QgsCategorizedSymbolRendererV2Model( this ); + mModel->setRenderer( mRenderer ); + viewCategories->setModel( mModel ); + viewCategories->resizeColumnToContents( 0 ); + viewCategories->resizeColumnToContents( 1 ); + viewCategories->resizeColumnToContents( 2 ); mCategorizedSymbol = QgsSymbolV2::defaultSymbol( mLayer->geometryType() ); + connect( mModel, SIGNAL( rowsMoved() ), this, SLOT( rowsMoved() ) ); + connect( cboCategorizedColumn, SIGNAL( currentIndexChanged( int ) ), this, SLOT( categoryColumnChanged() ) ); connect( viewCategories, SIGNAL( doubleClicked( const QModelIndex & ) ), this, SLOT( categoriesDoubleClicked( const QModelIndex & ) ) ); @@ -90,10 +354,9 @@ QgsCategorizedSymbolRendererV2Widget::QgsCategorizedSymbolRendererV2Widget( QgsV connect( btnChangeCategorizedSymbol, SIGNAL( clicked() ), this, SLOT( changeCategorizedSymbol() ) ); connect( btnAddCategories, SIGNAL( clicked() ), this, SLOT( addCategories() ) ); - connect( btnDeleteCategory, SIGNAL( clicked() ), this, SLOT( deleteCategory() ) ); + connect( btnDeleteCategories, SIGNAL( clicked() ), this, SLOT( deleteCategories() ) ); connect( btnDeleteAllCategories, SIGNAL( clicked() ), this, SLOT( deleteAllCategories() ) ); connect( btnAddCategory, SIGNAL( clicked() ), this, SLOT( addCategory() ) ); - connect( m, SIGNAL( itemChanged( QStandardItem * ) ), this, SLOT( changeCurrentValue( QStandardItem * ) ) ); // update GUI from renderer updateUiFromRenderer(); @@ -113,14 +376,15 @@ QgsCategorizedSymbolRendererV2Widget::QgsCategorizedSymbolRendererV2Widget( QgsV QgsCategorizedSymbolRendererV2Widget::~QgsCategorizedSymbolRendererV2Widget() { - delete mRenderer; + if ( mRenderer ) delete mRenderer; + if ( mModel ) delete mModel; } void QgsCategorizedSymbolRendererV2Widget::updateUiFromRenderer() { - updateCategorizedSymbolIcon(); - populateCategories(); + + //mModel->setRenderer ( mRenderer ); // necessary? // set column disconnect( cboCategorizedColumn, SIGNAL( currentIndexChanged( int ) ), this, SLOT( categoryColumnChanged() ) ); @@ -177,8 +441,6 @@ void QgsCategorizedSymbolRendererV2Widget::changeSelectedSymbols() } } } - - populateCategories(); } void QgsCategorizedSymbolRendererV2Widget::changeCategorizedSymbol() @@ -207,7 +469,6 @@ void QgsCategorizedSymbolRendererV2Widget::changeCategorizedSymbol() updateCategorizedSymbolIcon(); mRenderer->updateSymbols( mCategorizedSymbol ); - populateCategories(); } void QgsCategorizedSymbolRendererV2Widget::updateCategorizedSymbolIcon() @@ -218,26 +479,7 @@ void QgsCategorizedSymbolRendererV2Widget::updateCategorizedSymbolIcon() void QgsCategorizedSymbolRendererV2Widget::populateCategories() { - QStandardItemModel* m = qobject_cast( viewCategories->model() ); - m->clear(); - QStringList labels; - labels << tr( "Symbol" ) << tr( "Value" ) << tr( "Label" ); - m->setHorizontalHeaderLabels( labels ); - - int i, count = mRenderer->categories().count(); - - // TODO: sort?? utils.sortVariantList(keys); - - for ( i = 0; i < count; i++ ) - { - const QgsRendererCategoryV2 &cat = mRenderer->categories()[i]; - addCategory( cat ); - } - - viewCategories->resizeColumnToContents( 0 ); - viewCategories->resizeColumnToContents( 1 ); - viewCategories->resizeColumnToContents( 2 ); } void QgsCategorizedSymbolRendererV2Widget::populateColumns() @@ -251,23 +493,6 @@ void QgsCategorizedSymbolRendererV2Widget::populateColumns() } } - -void QgsCategorizedSymbolRendererV2Widget::addCategory( const QgsRendererCategoryV2 &cat ) -{ - QSize iconSize( 16, 16 ); - - QIcon icon = QgsSymbolLayerV2Utils::symbolPreviewIcon( cat.symbol(), iconSize ); - QStandardItem *symbolItem = new QStandardItem( icon, "" ); - symbolItem->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled ); - - QStandardItem *valueItem = new QStandardItem( cat.value().toString() ); - valueItem->setData( cat.value() ); // set attribute value as user role - - QList list; - list << symbolItem << valueItem << new QStandardItem( cat.label() ); - qobject_cast( viewCategories->model() )->appendRow( list ); -} - void QgsCategorizedSymbolRendererV2Widget::categoryColumnChanged() { mRenderer->setClassAttribute( cboCategorizedColumn->currentText() ); @@ -281,29 +506,32 @@ void QgsCategorizedSymbolRendererV2Widget::categoriesDoubleClicked( const QModel void QgsCategorizedSymbolRendererV2Widget::changeCategorySymbol() { - QVariant k = currentCategory(); - if ( !k.isValid() ) - return; + int catIdx = currentCategoryRow(); + QgsRendererCategoryV2 category = mRenderer->categories().value( currentCategoryRow() ); - int catIdx = mRenderer->categoryIndexForValue( k ); - QgsSymbolV2* newSymbol = mRenderer->categories()[catIdx].symbol()->clone(); + QgsSymbolV2 *symbol = category.symbol(); + if ( symbol ) + { + symbol = symbol->clone(); + } + else + { + symbol = QgsSymbolV2::defaultSymbol( mLayer->geometryType() ); + } - QgsSymbolV2SelectorDialog dlg( newSymbol, mStyle, mLayer, this ); + QgsSymbolV2SelectorDialog dlg( symbol, mStyle, mLayer, this ); if ( !dlg.exec() ) { - delete newSymbol; + delete symbol; return; } - mRenderer->updateCategorySymbol( catIdx, newSymbol ); - - populateCategories(); + mRenderer->updateCategorySymbol( catIdx, symbol ); } static void _createCategories( QgsCategoryList& cats, QList& values, QgsSymbolV2* symbol, QgsVectorColorRampV2* ramp ) { // sort the categories first - //TODO: make the order configurable? QgsSymbolLayerV2Utils::sortVariantList( values, Qt::AscendingOrder ); int num = values.count(); @@ -423,10 +651,13 @@ void QgsCategorizedSymbolRendererV2Widget::addCategories() r->setScaleMethod( mRenderer->scaleMethod() ); r->setSizeScaleField( mRenderer->sizeScaleField() ); r->setRotationField( mRenderer->rotationField() ); + + if ( mModel ) + { + mModel->setRenderer( r ); + } delete mRenderer; mRenderer = r; - - populateCategories(); } int QgsCategorizedSymbolRendererV2Widget::currentCategoryRow() @@ -437,87 +668,38 @@ int QgsCategorizedSymbolRendererV2Widget::currentCategoryRow() return idx.row(); } -QVariant QgsCategorizedSymbolRendererV2Widget::currentCategory() +QList QgsCategorizedSymbolRendererV2Widget::selectedCategories() { - int row = currentCategoryRow(); - if ( row == -1 ) - return QVariant(); - QStandardItemModel* m = qobject_cast( viewCategories->model() ); - return m->item( row, 1 )->data(); -} + QList rows; + QModelIndexList selectedRows = viewCategories->selectionModel()->selectedRows(); -QList QgsCategorizedSymbolRendererV2Widget::selectedCategories() -{ - QList categories; - QModelIndexList rows = viewCategories->selectionModel()->selectedRows(); - QStandardItemModel* m = qobject_cast( viewCategories->model() ); - - foreach ( QModelIndex r, rows ) + foreach ( QModelIndex r, selectedRows ) { if ( r.isValid() ) { - categories.append( m->item( r.row(), 1 )->data() ); + rows.append( r.row() ); } } - - return categories; + return rows; } -void QgsCategorizedSymbolRendererV2Widget::deleteCategory() +void QgsCategorizedSymbolRendererV2Widget::deleteCategories() { - QList categories = selectedCategories(); - - if ( !categories.size() ) - return; - - foreach ( const QVariant k, categories ) - { - if ( k.isValid() ) - { - int idx = mRenderer->categoryIndexForValue( k ); - if ( idx >= 0 ) - { - mRenderer->deleteCategory( idx ); - } - } - } - populateCategories(); + QList categoryIndexes = selectedCategories(); + mModel->removeRows( categoryIndexes ); } void QgsCategorizedSymbolRendererV2Widget::deleteAllCategories() { - mRenderer->deleteAllCategories(); - populateCategories(); -} - -void QgsCategorizedSymbolRendererV2Widget::changeCurrentValue( QStandardItem * item ) -{ - int idx = item->row(); - QString newtext = item->text(); - if ( item->column() == 1 ) - { - QVariant value = newtext; - // try to preserve variant type for this value - QVariant::Type t = item->data().type(); - if ( t == QVariant::Int ) - value = newtext.toInt(); - else if ( t == QVariant::Double ) - value = newtext.toDouble(); - mRenderer->updateCategoryValue( idx, value ); - item->setData( value ); - } - else if ( item->column() == 2 ) - { - mRenderer->updateCategoryLabel( idx, newtext ); - } + mModel->removeAllRows(); } void QgsCategorizedSymbolRendererV2Widget::addCategory() { + if ( !mModel ) return; QgsSymbolV2 *symbol = QgsSymbolV2::defaultSymbol( mLayer->geometryType() ); QgsRendererCategoryV2 cat( QString(), symbol, QString() ); - addCategory( cat ); - mRenderer->addCategory( cat ); + mModel->addCategory( cat ); } void QgsCategorizedSymbolRendererV2Widget::rotationFieldChanged( QString fldName ) @@ -548,14 +730,11 @@ QList QgsCategorizedSymbolRendererV2Widget::selectedSymbols() QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin(); for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt ) { - QStandardItem* currentItem = qobject_cast( m->model() )->itemFromIndex( *indexIt ); - if ( currentItem ) + int row = ( *indexIt ).row(); + QgsSymbolV2* s = categories[row].symbol(); + if ( s ) { - QgsSymbolV2* s = categories[mRenderer->categoryIndexForValue( currentItem->data() )].symbol(); - if ( s ) - { - selectedSymbols.append( s ); - } + selectedSymbols.append( s ); } } } @@ -566,3 +745,8 @@ void QgsCategorizedSymbolRendererV2Widget::showSymbolLevels() { showSymbolLevelsDialog( mRenderer ); } + +void QgsCategorizedSymbolRendererV2Widget::rowsMoved() +{ + viewCategories->selectionModel()->clear(); +} diff --git a/src/gui/symbology-ng/qgscategorizedsymbolrendererv2widget.h b/src/gui/symbology-ng/qgscategorizedsymbolrendererv2widget.h index 1fdefff3374..972ff0453d1 100644 --- a/src/gui/symbology-ng/qgscategorizedsymbolrendererv2widget.h +++ b/src/gui/symbology-ng/qgscategorizedsymbolrendererv2widget.h @@ -24,6 +24,39 @@ class QgsRendererCategoryV2; #include "ui_qgscategorizedsymbolrendererv2widget.h" +class GUI_EXPORT QgsCategorizedSymbolRendererV2Model : public QAbstractItemModel +{ + Q_OBJECT + public: + QgsCategorizedSymbolRendererV2Model( QObject * parent = 0 ); + Qt::ItemFlags flags( const QModelIndex & index ) const; + Qt::DropActions supportedDropActions() const; + QVariant data( const QModelIndex &index, int role ) const; + bool setData( const QModelIndex & index, const QVariant & value, int role ); + QVariant headerData( int section, Qt::Orientation orientation, int role ) const; + int rowCount( const QModelIndex &parent = QModelIndex() ) const; + int columnCount( const QModelIndex & = QModelIndex() ) const; + QModelIndex index( int row, int column, const QModelIndex &parent = QModelIndex() ) const; + QModelIndex parent( const QModelIndex &index ) const; + QStringList mimeTypes() const; + QMimeData *mimeData( const QModelIndexList &indexes ) const; + bool dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent ); + + void setRenderer( QgsCategorizedSymbolRendererV2* renderer ); + + void addCategory( const QgsRendererCategoryV2 &cat ); + void removeRows( QList rows ); + void removeAllRows( ); + void sort( int column, Qt::SortOrder order = Qt::AscendingOrder ); + + signals: + void rowsMoved(); + + private: + QgsCategorizedSymbolRendererV2* mRenderer; + QString mMimeFormat; +}; + class GUI_EXPORT QgsCategorizedSymbolRendererV2Widget : public QgsRendererV2Widget, private Ui::QgsCategorizedSymbolRendererV2Widget { Q_OBJECT @@ -39,10 +72,10 @@ class GUI_EXPORT QgsCategorizedSymbolRendererV2Widget : public QgsRendererV2Widg void changeCategorizedSymbol(); void categoryColumnChanged(); void categoriesDoubleClicked( const QModelIndex & idx ); + void addCategory(); void addCategories(); - void deleteCategory(); + void deleteCategories(); void deleteAllCategories(); - void changeCurrentValue( QStandardItem * item ); void rotationFieldChanged( QString fldName ); void sizeScaleFieldChanged( QString fldName ); @@ -50,28 +83,25 @@ class GUI_EXPORT QgsCategorizedSymbolRendererV2Widget : public QgsRendererV2Widg void showSymbolLevels(); + void rowsMoved(); + protected: void updateUiFromRenderer(); void updateCategorizedSymbolIcon(); - //! populate categories view + // Called by virtual refreshSymbolView() void populateCategories(); //! populate column combo void populateColumns(); - void addCategory( const QgsRendererCategoryV2& cat ); - //! return row index for the currently selected category (-1 if on no selection) int currentCategoryRow(); - //! return key for the currently selected category - QVariant currentCategory(); - - //! return a list of keys for the categories unders selection - QList selectedCategories(); + //! return a list of indexes for the categories unders selection + QList selectedCategories(); //! change the selected symbols alone for the change button, if there is a selection void changeSelectedSymbols(); @@ -81,9 +111,6 @@ class GUI_EXPORT QgsCategorizedSymbolRendererV2Widget : public QgsRendererV2Widg QList selectedSymbols(); void refreshSymbolView() { populateCategories(); } - protected slots: - void addCategory(); - protected: QgsCategorizedSymbolRendererV2* mRenderer; @@ -91,10 +118,10 @@ class GUI_EXPORT QgsCategorizedSymbolRendererV2Widget : public QgsRendererV2Widg QgsRendererV2DataDefinedMenus* mDataDefinedMenus; + QgsCategorizedSymbolRendererV2Model* mModel; + private: QString mOldClassificationAttribute; }; - - #endif // QGSCATEGORIZEDSYMBOLRENDERERV2WIDGET_H diff --git a/src/ui/qgscategorizedsymbolrendererv2widget.ui b/src/ui/qgscategorizedsymbolrendererv2widget.ui index b302b29059f..ded019ff880 100644 --- a/src/ui/qgscategorizedsymbolrendererv2widget.ui +++ b/src/ui/qgscategorizedsymbolrendererv2widget.ui @@ -94,6 +94,12 @@ Qt::CustomContextMenu + + QAbstractItemView::InternalMove + + + Qt::IgnoreAction + QAbstractItemView::ExtendedSelection @@ -106,6 +112,9 @@ false + + false + true @@ -131,7 +140,7 @@ - + Delete @@ -191,7 +200,7 @@ cboCategorizedColorRamp viewCategories btnAddCategories - btnDeleteCategory + btnDeleteCategories btnDeleteAllCategories btnJoinCategories