From c3fad246bc2be3f6304cc6a528962bacf51bf75b Mon Sep 17 00:00:00 2001 From: mhugent Date: Sat, 15 May 2010 14:15:47 +0000 Subject: [PATCH] Drag and drop support for composer legend model git-svn-id: http://svn.osgeo.org/qgis/trunk/qgis@13491 c8812cc2-4d05-0410-92ff-de0c093fc19c --- src/app/composer/qgscomposer.cpp | 4 + src/app/composer/qgscomposerlegendwidget.cpp | 4 - src/app/composer/qgscomposerlegendwidget.h | 5 +- src/core/composer/qgscomposerlegend.cpp | 1 - src/core/composer/qgscomposerlegenditem.cpp | 144 ++++++++++- src/core/composer/qgscomposerlegenditem.h | 4 + src/core/composer/qgslegendmodel.cpp | 239 ++++++++++--------- src/core/composer/qgslegendmodel.h | 10 +- 8 files changed, 290 insertions(+), 121 deletions(-) diff --git a/src/app/composer/qgscomposer.cpp b/src/app/composer/qgscomposer.cpp index 03b139ebed43..0651febbb6b6 100644 --- a/src/app/composer/qgscomposer.cpp +++ b/src/app/composer/qgscomposer.cpp @@ -1349,6 +1349,10 @@ void QgsComposer::addComposerLegend( QgsComposerLegend* legend ) } QgsComposerLegendWidget* lWidget = new QgsComposerLegendWidget( legend ); + if ( sender() ) //only update if created from GUI (not after XML read) + { + lWidget->updateLegend(); + } mItemWidgetMap.insert( legend, lWidget ); } diff --git a/src/app/composer/qgscomposerlegendwidget.cpp b/src/app/composer/qgscomposerlegendwidget.cpp index 4fd0abb2eeca..2160ab9acaf3 100644 --- a/src/app/composer/qgscomposerlegendwidget.cpp +++ b/src/app/composer/qgscomposerlegendwidget.cpp @@ -39,13 +39,9 @@ QgsComposerLegendWidget::QgsComposerLegendWidget( QgsComposerLegend* legend ): m mItemTreeView->setModel( legend->model() ); } - updateLegend(); - mItemTreeView->setDragEnabled( true ); mItemTreeView->setAcceptDrops( true ); mItemTreeView->setDropIndicatorShown( true ); - //only available in 4.6 - //mItemTreeView->setDefaultDropAction( Qt::MoveAction ); mItemTreeView->setDragDropMode( QAbstractItemView::InternalMove ); setGuiElements(); diff --git a/src/app/composer/qgscomposerlegendwidget.h b/src/app/composer/qgscomposerlegendwidget.h index 57f129c0e4d9..deed8751e081 100644 --- a/src/app/composer/qgscomposerlegendwidget.h +++ b/src/app/composer/qgscomposerlegendwidget.h @@ -34,6 +34,9 @@ class QgsComposerLegendWidget: public QWidget, private Ui::QgsComposerLegendWidg QgsComposerLegendWidget( QgsComposerLegend* legend ); ~QgsComposerLegendWidget(); + /**Updates the legend layers and groups*/ + void updateLegend(); + public slots: void on_mTitleLineEdit_textChanged( const QString& text ); @@ -61,8 +64,6 @@ class QgsComposerLegendWidget: public QWidget, private Ui::QgsComposerLegendWidg QgsComposerLegendWidget(); /**Sets GUI according to state of mLegend*/ void setGuiElements(); - /**Updates the legend layers and groups*/ - void updateLegend(); QgsComposerLegend* mLegend; }; diff --git a/src/core/composer/qgscomposerlegend.cpp b/src/core/composer/qgscomposerlegend.cpp index 14962e4e9d91..5a81d9e2b594 100644 --- a/src/core/composer/qgscomposerlegend.cpp +++ b/src/core/composer/qgscomposerlegend.cpp @@ -604,7 +604,6 @@ bool QgsComposerLegend::readXML( const QDomElement& itemElem, const QDomDocument if ( modelNodeList.size() > 0 ) { QDomElement modelElem = modelNodeList.at( 0 ).toElement(); - mLegendModel.clear(); mLegendModel.readXML( modelElem, doc ); } diff --git a/src/core/composer/qgscomposerlegenditem.cpp b/src/core/composer/qgscomposerlegenditem.cpp index 2e5ed3663670..d25a5ac09e9d 100644 --- a/src/core/composer/qgscomposerlegenditem.cpp +++ b/src/core/composer/qgscomposerlegenditem.cpp @@ -16,9 +16,11 @@ ***************************************************************************/ #include "qgscomposerlegenditem.h" +#include "qgsmaplayerregistry.h" #include "qgssymbol.h" #include "qgssymbolv2.h" #include "qgssymbollayerv2utils.h" +#include "qgsvectorlayer.h" #include #include @@ -79,7 +81,6 @@ void QgsComposerSymbolItem::setSymbol( QgsSymbol* s ) QStandardItem* QgsComposerSymbolItem::clone() const { - qWarning( "QgsComposerSymbolItem::clone" ); QgsComposerSymbolItem* cloneItem = new QgsComposerSymbolItem(); *cloneItem = *this; if ( mSymbol ) @@ -97,12 +98,48 @@ void QgsComposerSymbolItem::writeXML( QDomElement& elem, QDomDocument& doc ) con mSymbol->writeXML( vectorClassElem, doc, 0 ); } vectorClassElem.setAttribute( "text", text() ); + vectorClassElem.setAttribute( "layerId", mLayerID ); + elem.appendChild( vectorClassElem ); } void QgsComposerSymbolItem::readXML( const QDomElement& itemElem ) { - //soon... + if ( itemElem.isNull() ) + { + return; + } + setText( itemElem.attribute( "text", "" ) ); + setLayerID( itemElem.attribute( "layerId", "" ) ); + + QgsVectorLayer* vLayer = qobject_cast( QgsMapLayerRegistry::instance()->mapLayer( mLayerID ) ); + if ( vLayer ) + { + QDomElement symbolElem = itemElem.firstChildElement( "symbol" ); + if ( !symbolElem.isNull() ) + { + QgsSymbol* symbol = new QgsSymbol( vLayer->geometryType() ); + symbol->readXML( symbolElem, vLayer ); + setSymbol( symbol ); + + //add icon + switch ( symbol->type() ) + { + case QGis::Point: + setIcon( QIcon( QPixmap::fromImage( symbol->getPointSymbolAsImage() ) ) ); + break; + case QGis::Line: + setIcon( QIcon( QPixmap::fromImage( symbol->getLineSymbolAsImage() ) ) ); + break; + case QGis::Polygon: + setIcon( QIcon( QPixmap::fromImage( symbol->getPolygonSymbolAsImage() ) ) ); + break; + case QGis::UnknownGeometry: + // should not occur + break; + } + } + } } ////////////////QgsComposerSymbolV2Item @@ -153,7 +190,28 @@ void QgsComposerSymbolV2Item::writeXML( QDomElement& elem, QDomDocument& doc ) c void QgsComposerSymbolV2Item::readXML( const QDomElement& itemElem ) { - //soon... + if ( itemElem.isNull() ) + { + return; + } + + setText( itemElem.attribute( "text", "" ) ); + QDomElement symbolsElem = itemElem.firstChildElement( "symbols" ); + if ( !symbolsElem.isNull() ) + { + QgsSymbolV2Map loadSymbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem ); + //we assume there is only one symbol in the map... + QgsSymbolV2Map::iterator mapIt = loadSymbolMap.begin(); + if ( mapIt != loadSymbolMap.end() ) + { + QgsSymbolV2* symbolNg = mapIt.value(); + if ( symbolNg ) + { + setSymbolV2( symbolNg ); + setIcon( QgsSymbolLayerV2Utils::symbolPreviewIcon( symbolNg, QSize( 30, 30 ) ) ); + } + } + } } void QgsComposerSymbolV2Item::setSymbolV2( QgsSymbolV2* s ) @@ -195,7 +253,45 @@ void QgsComposerLayerItem::writeXML( QDomElement& elem, QDomDocument& doc ) cons void QgsComposerLayerItem::readXML( const QDomElement& itemElem ) { - //soon... + if ( itemElem.isNull() ) + { + return; + } + setText( itemElem.attribute( "text", "" ) ); + setLayerID( itemElem.attribute( "layerId", "" ) ); + + //now call readXML for all the child items + QDomNodeList childList = itemElem.childNodes(); + QDomNode currentNode; + QDomElement currentElem; + QgsComposerLegendItem* currentChildItem = 0; + + int nChildItems = childList.count(); + for ( int i = 0; i < nChildItems; ++i ) + { + currentNode = childList.at( i ); + if ( !currentNode.isElement() ) + { + continue; + } + + currentElem = currentNode.toElement(); + QString elemTag = currentElem.tagName(); + if ( elemTag == "VectorClassificationItem" ) + { + currentChildItem = new QgsComposerSymbolItem(); + } + else if ( elemTag == "VectorClassificationItemNg" ) + { + currentChildItem = new QgsComposerSymbolV2Item(); + } + else + { + continue; //unsupported child type + } + currentChildItem->readXML( currentElem ); + appendRow( currentChildItem ); + } } ////////////////////QgsComposerGroupItem @@ -229,5 +325,43 @@ void QgsComposerGroupItem::writeXML( QDomElement& elem, QDomDocument& doc ) cons void QgsComposerGroupItem::readXML( const QDomElement& itemElem ) { - //soon... + if ( itemElem.isNull() ) + { + return; + } + setText( itemElem.attribute( "text", "" ) ); + + //now call readXML for all the child items + QDomNodeList childList = itemElem.childNodes(); + QDomNode currentNode; + QDomElement currentElem; + QgsComposerLegendItem* currentChildItem = 0; + + int nChildItems = childList.count(); + for ( int i = 0; i < nChildItems; ++i ) + { + currentNode = childList.at( i ); + if ( !currentNode.isElement() ) + { + continue; + } + + currentElem = currentNode.toElement(); + QString elemTag = currentElem.tagName(); + + if ( elemTag == "GroupItem" ) + { + currentChildItem = new QgsComposerGroupItem(); + } + else if ( elemTag == "LayerItem" ) + { + currentChildItem = new QgsComposerLayerItem(); + } + else + { + continue; //unsupported child item type + } + currentChildItem->readXML( currentElem ); + appendRow( currentChildItem ); + } } diff --git a/src/core/composer/qgscomposerlegenditem.h b/src/core/composer/qgscomposerlegenditem.h index af42912d23b5..70765c352ba4 100644 --- a/src/core/composer/qgscomposerlegenditem.h +++ b/src/core/composer/qgscomposerlegenditem.h @@ -69,10 +69,14 @@ class CORE_EXPORT QgsComposerSymbolItem: public QgsComposerLegendItem void setSymbol( QgsSymbol* s ); QgsSymbol* symbol() {return mSymbol;} + void setLayerID( const QString& id ) { mLayerID = id; } + QString layerID() const { return mLayerID; } + ItemType itemType() const { return SymbologyItem; } private: QgsSymbol* mSymbol; + QString mLayerID; //this is needed to read the symbol from XML }; class QgsSymbolV2; diff --git a/src/core/composer/qgslegendmodel.cpp b/src/core/composer/qgslegendmodel.cpp index 1ebb7befd285..b10024d69b48 100644 --- a/src/core/composer/qgslegendmodel.cpp +++ b/src/core/composer/qgslegendmodel.cpp @@ -29,6 +29,7 @@ #include "qgsvectorlayer.h" #include #include +#include #include QgsLegendModel::QgsLegendModel(): QStandardItemModel() @@ -233,7 +234,7 @@ int QgsLegendModel::addVectorLayerItems( QStandardItem* layerItem, QgsVectorLaye continue; } - QStandardItem* currentSymbolItem = itemFromSymbol( *symbolIt, opacity ); + QStandardItem* currentSymbolItem = itemFromSymbol( *symbolIt, opacity, vlayer->getLayerID() ); if ( !currentSymbolItem ) { continue; @@ -409,7 +410,7 @@ void QgsLegendModel::updateVectorClassificationItem( QStandardItem* classificati currentSymbol = *symbolIt; if ( currentSymbol->lowerValue() + " - " + currentSymbol->upperValue() == itemText ) { - parentItem->insertRow( classificationItem->row(), itemFromSymbol( currentSymbol, opacity ) ); + parentItem->insertRow( classificationItem->row(), itemFromSymbol( currentSymbol, opacity, vl->getLayerID() ) ); parentItem->removeRow( classificationItem->row() ); return; } @@ -422,7 +423,7 @@ void QgsLegendModel::updateVectorClassificationItem( QStandardItem* classificati currentSymbol = *symbolIt; if ( currentSymbol->lowerValue() == itemText ) { - parentItem->insertRow( classificationItem->row(), itemFromSymbol( currentSymbol, opacity ) ); + parentItem->insertRow( classificationItem->row(), itemFromSymbol( currentSymbol, opacity, vl->getLayerID() ) ); parentItem->removeRow( classificationItem->row() ); return; } @@ -436,7 +437,7 @@ void QgsLegendModel::updateVectorClassificationItem( QStandardItem* classificati if ( currentSymbol->label() == itemText ) { removeSymbol( symbol ); - parentItem->insertRow( classificationItem->row(), itemFromSymbol( currentSymbol, opacity ) ); + parentItem->insertRow( classificationItem->row(), itemFromSymbol( currentSymbol, opacity, vl->getLayerID() ) ); parentItem->removeRow( classificationItem->row() ); return; } @@ -548,7 +549,7 @@ void QgsLegendModel::addLayer( QgsMapLayer* theMapLayer ) emit layersChanged(); } -QStandardItem* QgsLegendModel::itemFromSymbol( QgsSymbol* s, int opacity ) +QStandardItem* QgsLegendModel::itemFromSymbol( QgsSymbol* s, int opacity, const QString& layerID ) { QgsComposerSymbolItem* currentSymbolItem = 0; @@ -614,12 +615,12 @@ QStandardItem* QgsLegendModel::itemFromSymbol( QgsSymbol* s, int opacity ) { return 0; } - currentSymbolItem->setData( QgsLegendModel::ClassificationItem, Qt::UserRole + 1 ); //first user data stores the item type //Pass deep copy of QgsSymbol as user data. Cast to void* necessary such that QMetaType handles it QgsSymbol* symbolCopy = new QgsSymbol( *s ); currentSymbolItem->setSymbol( symbolCopy ); currentSymbolItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable ); + currentSymbolItem ->setLayerID( layerID ); return currentSymbolItem; } @@ -656,112 +657,34 @@ bool QgsLegendModel::readXML( const QDomElement& legendModelElem, const QDomDocu return false; } - return false; - //todo: adapt to new legend item structure - /* - //iterate over layer items - QDomNodeList layerItemList = legendModelElem.elementsByTagName( "LayerItem" ); - QgsMapLayer* currentLayer = 0; //store current layer to get - - for ( int i = 0; i < layerItemList.size(); ++i ) - { - QDomElement layerItemElem = layerItemList.at( i ).toElement(); - QString layerId = layerItemElem.attribute( "layerId" ); - - QStandardItem* layerItem = new QStandardItem( layerItemElem.attribute( "text" ) ); - - //set layer id as user data into the item - layerItem->setData( QVariant( layerId ) ); - layerItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable ); - - currentLayer = QgsMapLayerRegistry::instance()->mapLayer( layerId ); - - //go through all children of layerItemElem - QDomElement currentChildElement = layerItemElem.firstChildElement(); - while ( !currentChildElement.isNull() ) - { - QStandardItem* childItem = new QStandardItem( currentChildElement.attribute( "text" ) ); - if ( currentChildElement.tagName() == "RasterItem" ) - { - //get icon from current layer - QgsRasterLayer* rasterLayer = qobject_cast( currentLayer ); - if ( rasterLayer ) - { - childItem->setIcon( QIcon( rasterLayer->legendAsPixmap( true ) ) ); - } - layerItem->setChild( layerItem->rowCount(), 0, childItem ); - } - else if ( currentChildElement.tagName() == "VectorClassificationItem" ) - { - //read QgsSymbol from xml and get icon - QgsVectorLayer* vectorLayer = qobject_cast( currentLayer ); - if ( vectorLayer ) - { - //look for symbol - QDomNodeList symbolNodeList = currentChildElement.elementsByTagName( "symbol" ); - if ( symbolNodeList.size() > 0 ) - { - QgsSymbol* symbol = new QgsSymbol( vectorLayer->geometryType() ); - QDomNode symbolNode = symbolNodeList.at( 0 ); - symbol->readXML( symbolNode, vectorLayer ); - childItem->setData( QVariant::fromValue(( void* )symbol ) ); - - //add icon - switch ( symbol->type() ) - { - case QGis::Point: - childItem->setIcon( QIcon( QPixmap::fromImage( symbol->getPointSymbolAsImage() ) ) ); - break; - case QGis::Line: - childItem->setIcon( QIcon( QPixmap::fromImage( symbol->getLineSymbolAsImage() ) ) ); - break; - case QGis::Polygon: - childItem->setIcon( QIcon( QPixmap::fromImage( symbol->getPolygonSymbolAsImage() ) ) ); - break; - case QGis::UnknownGeometry: - // should not occur - break; - } - insertSymbol( symbol ); - } - } - layerItem->setChild( layerItem->rowCount(), 0, childItem ); - } - else if ( currentChildElement.tagName() == "VectorClassificationItemNg" ) - { - QDomElement symbolNgElem = currentChildElement.firstChildElement( "symbols" ); - if ( !symbolNgElem.isNull() ) - { - QgsSymbolV2Map loadSymbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolNgElem ); - //we assume there is only one symbol in the map... - QgsSymbolV2Map::iterator mapIt = loadSymbolMap.begin(); - if ( mapIt != loadSymbolMap.end() ) - { - QgsSymbolV2* symbolNg = mapIt.value(); - insertSymbolV2( symbolNg ); - childItem->setData( QVariant::fromValue(( void* )symbolNg ), Qt::UserRole + 2 ); - childItem->setIcon( QgsSymbolLayerV2Utils::symbolPreviewIcon( symbolNg, QSize( 30, 30 ) ) ); - } - layerItem->setChild( layerItem->rowCount(), 0, childItem ); - } - } - else if ( currentChildElement.tagName() == "TextItem" ) - { - layerItem->setChild( layerItem->rowCount(), 0, childItem ); - } - else //unknown tag name, don't add item - { - delete childItem; - } + clear(); - currentChildElement = currentChildElement.nextSiblingElement(); - } + QDomNodeList topLevelItemList = legendModelElem.childNodes(); + QDomElement currentElem; + QgsComposerLegendItem* currentItem = 0; - invisibleRootItem()->setChild( invisibleRootItem()->rowCount(), layerItem ); + int nTopLevelItems = topLevelItemList.size(); + for ( int i = 0; i < nTopLevelItems; ++i ) + { + currentElem = topLevelItemList.at( i ).toElement(); + if ( currentElem.isNull() ) + { + continue; } - return true; - */ + //toplevel items can be groups or layers + if ( currentElem.tagName() == "LayerItem" ) + { + currentItem = new QgsComposerLayerItem(); + } + else if ( currentElem.tagName() == "GroupItem" ) + { + currentItem = new QgsComposerGroupItem(); + } + currentItem->readXML( currentElem ); + appendRow( currentItem ); + } + return true; } Qt::DropActions QgsLegendModel::supportedDropActions() const @@ -822,3 +745,103 @@ QgsLegendModel::ItemType QgsLegendModel::itemType( const QStandardItem& item ) c { return ( QgsLegendModel::ItemType )item.data( Qt::UserRole + 1 ).toInt(); } + +QMimeData* QgsLegendModel::mimeData( const QModelIndexList &indexes ) const +{ + QMimeData* mimeData = new QMimeData(); + QByteArray encodedData; + QDomDocument xmlDoc; + QDomElement xmlRootElement = xmlDoc.createElement( "LegendModelDragData" ); + xmlDoc.appendChild( xmlRootElement ); + + QModelIndexList::const_iterator indexIt = indexes.constBegin(); + for ( ; indexIt != indexes.constEnd(); ++indexIt ) + { + QStandardItem* sItem = itemFromIndex( *indexIt ); + if ( sItem ) + { + QgsComposerLegendItem* mItem = dynamic_cast( sItem ); + if ( mItem ) + { + mItem->writeXML( xmlRootElement, xmlDoc ); + } + } + } + mimeData->setData( "text/xml", xmlDoc.toByteArray() ); + return mimeData; +} + +QStringList QgsLegendModel::mimeTypes() const +{ + QStringList types; + types << "text/xml"; + return types; +} + +bool QgsLegendModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent ) +{ + if ( !data->hasFormat( "text/xml" ) ) + { + return false; + } + + QStandardItem* dropIntoItem = 0; + if ( parent.isValid() ) + { + dropIntoItem = itemFromIndex( parent ); + } + else + { + dropIntoItem = invisibleRootItem(); + } + + //get XML doc + QByteArray encodedData = data->data( "text/xml" ); + QDomDocument xmlDoc; + xmlDoc.setContent( encodedData ); + + QDomElement dragDataElem = xmlDoc.documentElement(); + if ( dragDataElem.tagName() != "LegendModelDragData" ) + { + return false; + } + + QDomNodeList nodeList = dragDataElem.childNodes(); + int nChildNodes = nodeList.size(); + QDomElement currentElem; + QString currentTagName; + QgsComposerLegendItem* currentItem = 0; + + for ( int i = 0; i < nChildNodes; ++i ) + { + currentElem = nodeList.at( i ).toElement(); + if ( currentElem.isNull() ) + { + continue; + } + currentTagName = currentElem.tagName(); + if ( currentTagName == "LayerItem" ) + { + currentItem = new QgsComposerLayerItem(); + } + else if ( currentTagName == "GroupItem" ) + { + currentItem = new QgsComposerGroupItem(); + } + else + { + continue; + } + currentItem->readXML( currentElem ); + if ( row < 0 ) + { + dropIntoItem->insertRow( dropIntoItem->rowCount(), currentItem ); + } + else + { + dropIntoItem->insertRow( row + i, currentItem ); + } + } + emit layersChanged(); + return true; +} diff --git a/src/core/composer/qgslegendmodel.h b/src/core/composer/qgslegendmodel.h index 8b0ff93abe6b..9b0ea720c6f6 100644 --- a/src/core/composer/qgslegendmodel.h +++ b/src/core/composer/qgslegendmodel.h @@ -79,6 +79,14 @@ class CORE_EXPORT QgsLegendModel: public QStandardItemModel QgsLegendModel::ItemType itemType( const QStandardItem& item ) const; + /**For the drag operation*/ + QMimeData* mimeData( const QModelIndexList &indexes ) const; + QStringList mimeTypes() const; + + /**Implements the drop operation*/ + bool dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent ); + + public slots: void removeLayer( const QString& layerId ); void addLayer( QgsMapLayer* theMapLayer ); @@ -99,7 +107,7 @@ class CORE_EXPORT QgsLegendModel: public QStandardItemModel int addRasterLayerItem( QStandardItem* layerItem, QgsMapLayer* rlayer ); /**Creates a model item for a vector symbol. The calling function takes ownership*/ - QStandardItem* itemFromSymbol( QgsSymbol* s, int opacity ); + QStandardItem* itemFromSymbol( QgsSymbol* s, int opacity, const QString& layerID ); protected: QStringList mLayerIds;