diff --git a/python/core/qgsrect.sip b/python/core/qgsrect.sip index bcc22a38bbc..c4439f4fda9 100644 --- a/python/core/qgsrect.sip +++ b/python/core/qgsrect.sip @@ -64,6 +64,9 @@ class QgsRectangle //! return true when rectangle contains other rectangle //! @note added in version 1.1 bool contains( const QgsRectangle& rect ) const; + //! return true when rectangle contains a point + //! @note added in version 1.3 + bool contains( const QgsPoint& p ) const; //! expand the rectangle so that covers both the original rectangle and the given rectangle void combineExtentWith(QgsRectangle *rect); //! expand the rectangle so that covers both the original rectangle and the given point diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 97727ee9a79..6d7d933763e 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -767,7 +767,7 @@ void QgisApp::createActions() shortcuts->registerAction( mActionIdentify, tr( "Ctrl+Shift+I", "Click on features to identify them" ) ); mActionIdentify->setStatusTip( tr( "Click on features to identify them" ) ); connect( mActionIdentify, SIGNAL( triggered() ), this, SLOT( identify() ) ); - mActionIdentify->setEnabled( false ); + mActionIdentify->setEnabled( QSettings().value( "/Map/identifyMode", 0 ).toInt() != 0 ); mActionMeasure = new QAction( getThemeIcon( "mActionMeasure.png" ), tr( "Measure Line " ), this ); shortcuts->registerAction( mActionMeasure, tr( "Ctrl+Shift+M", "Measure a Line" ) ); @@ -5542,7 +5542,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer ) if ( !layer ) { mActionSelect->setEnabled( false ); - mActionIdentify->setEnabled( false ); + mActionIdentify->setEnabled( QSettings().value( "/Map/identifyMode", 0 ).toInt() != 0 ); mActionZoomActualSize->setEnabled( false ); mActionOpenTable->setEnabled( false ); mActionToggleEditing->setEnabled( false ); @@ -5785,18 +5785,24 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer ) //Enable the Identify tool ( GDAL datasets draw without a provider ) //but turn off if data provider exists and has no Identify capabilities mActionIdentify->setEnabled( true ); - const QgsRasterLayer* vlayer = dynamic_cast( layer ); - const QgsRasterDataProvider* dprovider = vlayer->dataProvider(); - if ( dprovider ) + + QSettings settings; + int identifyMode = settings.value( "/Map/identifyMode", 0 ).toInt(); + if ( identifyMode == 0 ) { - // does provider allow the identify map tool? - if ( dprovider->capabilities() & QgsRasterDataProvider::Identify ) + const QgsRasterLayer *rlayer = dynamic_cast( layer ); + const QgsRasterDataProvider* dprovider = rlayer->dataProvider(); + if ( dprovider ) { - mActionIdentify->setEnabled( TRUE ); - } - else - { - mActionIdentify->setEnabled( FALSE ); + // does provider allow the identify map tool? + if ( dprovider->capabilities() & QgsRasterDataProvider::Identify ) + { + mActionIdentify->setEnabled( true ); + } + else + { + mActionIdentify->setEnabled( false ); + } } } } diff --git a/src/app/qgsidentifyresults.cpp b/src/app/qgsidentifyresults.cpp index 0ab537bb020..3cd8d8e4a4f 100644 --- a/src/app/qgsidentifyresults.cpp +++ b/src/app/qgsidentifyresults.cpp @@ -21,6 +21,12 @@ #include "qgscontexthelp.h" #include "qgsapplication.h" #include "qgisapp.h" +#include "qgsmaplayer.h" +#include "qgsvectorlayer.h" +#include "qgsrubberband.h" +#include "qgsgeometry.h" +#include "qgsattributedialog.h" +#include "qgsmapcanvas.h" #include #include @@ -33,13 +39,25 @@ #include "qgslogger.h" -QgsIdentifyResults::QgsIdentifyResults( const QgsAttributeAction& actions, - QWidget *parent, Qt::WFlags f ) +// Tree hierachy +// +// layer [userrole: QgsMapLayer] +// feature: displayfield|displayvalue [userrole: fid] +// derived attributes +// name value +// name value +// name value +// name value +// feature +// derived attributes +// name value +// name value + +QgsIdentifyResults::QgsIdentifyResults( QgsMapCanvas *canvas, QWidget *parent, Qt::WFlags f ) : QDialog( parent, f ), - mActions( actions ), - mClickedOnValue( 0 ), mActionPopup( 0 ), - mCurrentFeatureId( 0 ) + mRubberBand( 0 ), + mCanvas( canvas ) { setupUi( this ); lstResults->setColumnCount( 2 ); @@ -48,16 +66,12 @@ QgsIdentifyResults::QgsIdentifyResults( const QgsAttributeAction& actions, connect( buttonCancel, SIGNAL( clicked() ), this, SLOT( close() ) ); - connect( lstResults, SIGNAL( itemClicked( QTreeWidgetItem*, int ) ), - this, SLOT( clicked( QTreeWidgetItem * ) ) ); + connect( lstResults, SIGNAL( itemExpanded( QTreeWidgetItem* ) ), this, SLOT( itemExpanded( QTreeWidgetItem* ) ) ); connect( lstResults, SIGNAL( currentItemChanged( QTreeWidgetItem*, QTreeWidgetItem* ) ), this, SLOT( handleCurrentItemChanged( QTreeWidgetItem*, QTreeWidgetItem* ) ) ); - - // The label to use for the Derived node in the identify results - mDerivedLabel = tr( "(Derived)" ); } QgsIdentifyResults::~QgsIdentifyResults() @@ -65,6 +79,58 @@ QgsIdentifyResults::~QgsIdentifyResults() delete mActionPopup; } +QTreeWidgetItem *QgsIdentifyResults::layerItem( QObject *layer ) +{ + QTreeWidgetItem *item; + + for ( int i = 0; i < lstResults->topLevelItemCount(); i++ ) + { + item = lstResults->topLevelItem( i ); + if ( item->data( 0, Qt::UserRole ).value() == layer ) + return item; + } + + return 0; +} + +void QgsIdentifyResults::addFeature( QgsMapLayer *layer, int fid, + QString displayField, QString displayValue, + const QMap &attributes, + const QMap &derivedAttributes ) +{ + QTreeWidgetItem *item = layerItem( layer ); + + if ( item == 0 ) + { + item = new QTreeWidgetItem( QStringList() << layer->name() << tr( "Layer" ) ); + item->setData( 0, Qt::UserRole, QVariant::fromValue( dynamic_cast( layer ) ) ); + lstResults->addTopLevelItem( item ); + + connect( layer, SIGNAL( destroyed() ), this, SLOT( layerDestroyed() ) ); + } + + QTreeWidgetItem *featItem = new QTreeWidgetItem( QStringList() << displayField << displayValue ); + featItem->setData( 0, Qt::UserRole, fid ); + + for ( QMap::const_iterator it = attributes.begin(); it != attributes.end(); it++ ) + { + featItem->addChild( new QTreeWidgetItem( QStringList() << it.key() << it.value() ) ); + } + + if ( derivedAttributes.size() >= 0 ) + { + QTreeWidgetItem *derivedItem = new QTreeWidgetItem( QStringList() << tr( "(Derived)" ) ); + featItem->addChild( derivedItem ); + + for ( QMap< QString, QString>::const_iterator it = derivedAttributes.begin(); it != derivedAttributes.end(); it++ ) + { + derivedItem->addChild( new QTreeWidgetItem( QStringList() << it.key() << it.value() ) ); + } + } + + item->addChild( featItem ); +} + // Call to show the dialog box. void QgsIdentifyResults::show() { @@ -96,16 +162,30 @@ void QgsIdentifyResults::closeEvent( QCloseEvent *e ) void QgsIdentifyResults::contextMenuEvent( QContextMenuEvent* event ) { - QTreeWidgetItem* item = lstResults->itemAt( lstResults->viewport()->mapFrom( this, event->pos() ) ); + QTreeWidgetItem *item = lstResults->itemAt( lstResults->viewport()->mapFrom( this, event->pos() ) ); // if the user clicked below the end of the attribute list, just return - if ( item == NULL ) + if ( !item ) return; if ( mActionPopup == 0 ) { + QgsVectorLayer *vlayer = vectorLayer( item ); + if ( vlayer == 0 ) + return; + + QgsAttributeAction *actions = vlayer->actions(); + mActionPopup = new QMenu(); QAction *a; + + if ( vlayer->isEditable() ) + { + a = mActionPopup->addAction( tr( "Edit feature" ) ); + a->setEnabled( true ); + a->setData( QVariant::fromValue( -3 ) ); + } + a = mActionPopup->addAction( tr( "Copy attribute value" ) ); a->setEnabled( true ); a->setData( QVariant::fromValue( -2 ) ); @@ -114,7 +194,7 @@ void QgsIdentifyResults::contextMenuEvent( QContextMenuEvent* event ) a->setEnabled( true ); a->setData( QVariant::fromValue( -1 ) ); - if ( mActions.size() > 0 ) + if ( actions && actions->size() > 0 ) { // The assumption is made that an instance of QgsIdentifyResults is // created for each new Identify Results dialog box, and that the @@ -124,8 +204,8 @@ void QgsIdentifyResults::contextMenuEvent( QContextMenuEvent* event ) a->setEnabled( false ); mActionPopup->addSeparator(); - QgsAttributeAction::aIter iter = mActions.begin(); - for ( int j = 0; iter != mActions.end(); ++iter, ++j ) + QgsAttributeAction::aIter iter = actions->begin(); + for ( int j = 0; iter != actions->end(); ++iter, ++j ) { QAction* a = mActionPopup->addAction( iter->name() ); // The menu action stores an integer that is used later on to @@ -134,14 +214,9 @@ void QgsIdentifyResults::contextMenuEvent( QContextMenuEvent* event ) } } - connect( mActionPopup, SIGNAL( triggered( QAction* ) ), - this, SLOT( popupItemSelected( QAction* ) ) ); + connect( mActionPopup, SIGNAL( triggered( QAction* ) ), this, SLOT( popupItemSelected( QAction* ) ) ); } - // Save the attribute values as these are needed for substituting into - // the action. - extractAllItemData( item ); - mActionPopup->popup( event->globalPos() ); } @@ -160,75 +235,6 @@ void QgsIdentifyResults::saveWindowLocation() settings.setValue( "/Windows/Identify/geometry", saveGeometry() ); } -/** add an attribute and its value to the list */ -void QgsIdentifyResults::addAttribute( QTreeWidgetItem * fnode, QString field, QString value ) -{ - QStringList labels; - labels << field << value; - new QTreeWidgetItem( fnode, labels ); -} - -void QgsIdentifyResults::addAttribute( QString field, QString value ) -{ - QStringList labels; - labels << field << value; - new QTreeWidgetItem( lstResults, labels ); -} - -void QgsIdentifyResults::addDerivedAttribute( QTreeWidgetItem * fnode, QString field, QString value ) -{ - QTreeWidgetItem * daRootNode; - - // Determine if this is the first derived attribute for this feature or not - if ( mDerivedAttributeRootNodes.find( fnode ) != mDerivedAttributeRootNodes.end() ) - { - // Reuse existing derived-attribute root node - daRootNode = mDerivedAttributeRootNodes[fnode]; - } - else - { - // Create new derived-attribute root node - daRootNode = new QTreeWidgetItem( fnode, QStringList( mDerivedLabel ) ); - QFont font = daRootNode->font( 0 ); - font.setItalic( true ); - daRootNode->setFont( 0, font ); - mDerivedAttributeRootNodes[fnode] = daRootNode; - } - - QStringList labels; - labels << field << value; - new QTreeWidgetItem( daRootNode, labels ); -} - -void QgsIdentifyResults::addEdit( QTreeWidgetItem * fnode, int id ) -{ - QStringList labels; - labels << "edit" << QString::number( id ); - QTreeWidgetItem *item = new QTreeWidgetItem( fnode, labels ); - - item->setIcon( 0, QgisApp::getThemeIcon( "/mIconEditable.png" ) ); -} - -void QgsIdentifyResults::addAction( QTreeWidgetItem * fnode, int id, QString field, QString value ) -{ - QStringList labels; - labels << field << value << "action" << QString::number( id ); - QTreeWidgetItem *item = new QTreeWidgetItem( fnode, labels ); - - item->setIcon( 0, QgisApp::getThemeIcon( "/mAction.png" ) ); -} - -/** Add a feature node to the list */ -QTreeWidgetItem *QgsIdentifyResults::addNode( QString label ) -{ - return new QTreeWidgetItem( lstResults, QStringList( label ) ); -} - -void QgsIdentifyResults::setTitle( QString title ) -{ - setWindowTitle( tr( "Identify Results - %1" ).arg( title ) ); -} - void QgsIdentifyResults::setColumnText( int column, const QString & label ) { QTreeWidgetItem* header = lstResults->headerItem(); @@ -238,6 +244,10 @@ void QgsIdentifyResults::setColumnText( int column, const QString & label ) // Run the action that was selected in the popup menu void QgsIdentifyResults::popupItemSelected( QAction* menuAction ) { + QTreeWidgetItem *item = lstResults->currentItem(); + if ( item == 0 ) + return; + int id = menuAction->data().toInt(); if ( id < 0 ) @@ -245,13 +255,20 @@ void QgsIdentifyResults::popupItemSelected( QAction* menuAction ) QClipboard *clipboard = QApplication::clipboard(); QString text; - if ( id == -2 ) + if ( id == -3 ) { - text = mValues[ mClickedOnValue ].second; + editFeature( item ); + } + else if ( id == -2 ) + { + text = item->data( 1, Qt::DisplayRole ).toString(); } else { - for ( std::vector< std::pair >::const_iterator it = mValues.begin(); it != mValues.end(); it++ ) + std::vector< std::pair > attributes; + retrieveAttributes( item, attributes ); + + for ( std::vector< std::pair >::iterator it = attributes.begin(); it != attributes.end(); it++ ) { text += QString( "%1: %2\n" ).arg( it->first ).arg( it->second ); } @@ -262,17 +279,10 @@ void QgsIdentifyResults::popupItemSelected( QAction* menuAction ) } else { - mActions.doAction( id, mValues, mClickedOnValue ); + doAction( item ); } } -/** Expand all the identified features (show their attributes). */ -void QgsIdentifyResults::showAllAttributes() -{ - // Easy now with Qt 4.2... - lstResults->expandAll(); -} - void QgsIdentifyResults::expandColumnsToFit() { lstResults->resizeColumnToContents( 0 ); @@ -281,40 +291,86 @@ void QgsIdentifyResults::expandColumnsToFit() void QgsIdentifyResults::clear() { - mDerivedAttributeRootNodes.clear(); lstResults->clear(); + + if ( mRubberBand ) + { + delete mRubberBand; + mRubberBand = 0; + } } -void QgsIdentifyResults::setMessage( QString shortMsg, QString longMsg ) +void QgsIdentifyResults::doAction( QTreeWidgetItem *item ) { - QStringList labels; - labels << shortMsg << longMsg; - new QTreeWidgetItem( lstResults, labels ); -} - -void QgsIdentifyResults::setActions( const QgsAttributeAction& actions ) -{ - mActions = actions; -} - -void QgsIdentifyResults::clicked( QTreeWidgetItem *item ) -{ - if ( !item ) + std::vector< std::pair > attributes; + QTreeWidgetItem *featItem = retrieveAttributes( item, attributes ); + if ( !featItem ) return; - if ( item->text( 2 ) == "action" ) - { - int id = item->text( 3 ).toInt(); + int id = featItem->data( 0, Qt::UserRole ).toInt(); - extractAllItemData( item ); + QgsVectorLayer *layer = dynamic_cast( featItem->parent()->data( 0, Qt::UserRole ).value() ); + if ( !layer ) + return; - mActions.doAction( id, mValues, mClickedOnValue ); - } - else if ( item->text( 0 ) == "edit" ) - { - emit editFeature( item->text( 1 ).toInt() ); - } + layer->actions()->doAction( id, attributes, id ); } + +QTreeWidgetItem *QgsIdentifyResults::featureItem( QTreeWidgetItem *item ) +{ + QTreeWidgetItem *featItem; + if ( item->parent() ) + { + if ( item->parent()->parent() ) + { + // attribute item + featItem = item->parent(); + } + else + { + // feature item + featItem = item; + } + } + else + { + // layer item + if ( item->childCount() > 1 ) + return 0; + + featItem = item->child( 0 ); + } + + return featItem; +} + +QgsVectorLayer *QgsIdentifyResults::vectorLayer( QTreeWidgetItem *item ) +{ + if ( item->parent() ) + { + item = featureItem( item )->parent(); + } + + return dynamic_cast( item->data( 0, Qt::UserRole ).value() ); +} + + +QTreeWidgetItem *QgsIdentifyResults::retrieveAttributes( QTreeWidgetItem *item, std::vector< std::pair > &attributes ) +{ + QTreeWidgetItem *featItem = featureItem( item ); + + attributes.clear(); + for ( int i = 0; i < featItem->childCount(); i++ ) + { + QTreeWidgetItem *item = featItem->child( i ); + if ( item->childCount() > 0 ) + continue; + attributes.push_back( std::make_pair( item->data( 0, Qt::DisplayRole ).toString(), item->data( 1, Qt::DisplayRole ).toString() ) ); + } + + return featItem; +} + void QgsIdentifyResults::on_buttonHelp_clicked() { QgsContextHelp::run( context_id ); @@ -327,99 +383,94 @@ void QgsIdentifyResults::itemExpanded( QTreeWidgetItem* item ) void QgsIdentifyResults::handleCurrentItemChanged( QTreeWidgetItem* current, QTreeWidgetItem* previous ) { - if ( lstResults->model()->rowCount() <= 1 ) - return; - if ( current == NULL ) { - mCurrentFeatureId = 0; - emit selectedFeatureChanged( 0 ); + emit selectedFeatureChanged( 0, 0 ); return; } - // move to node where is saved feature ID - QTreeWidgetItem* topLevelItem = current; - while ( topLevelItem->parent() != NULL ) - { - topLevelItem = topLevelItem->parent(); - } - - QVariant fid = topLevelItem->data( 0, Qt::UserRole ); - - // no data saved... - if ( fid.type() != QVariant::Int ) - return; - int fid2 = fid.toInt(); - - if ( fid2 == mCurrentFeatureId ) - return; - - mCurrentFeatureId = fid2; - emit selectedFeatureChanged( mCurrentFeatureId ); + highlightFeature( current ); } -void QgsIdentifyResults::extractAllItemData( QTreeWidgetItem* item ) +void QgsIdentifyResults::layerDestroyed() { - // Extracts the name/value pairs from the given item. This includes data - // under the (Derived) item. + delete layerItem( sender() ); +} - // A little bit complicated because the user could of right-clicked - // on any item in the dialog box. We want a toplevel item, so walk upwards - // as far as possible. - // We also want to keep track of which row in the identify results table was - // actually clicked on. This is stored as an index into the mValues vector. +void QgsIdentifyResults::highlightFeature( QTreeWidgetItem *item ) +{ + QgsVectorLayer *layer = vectorLayer( item ); + if ( !layer ) + return; - QTreeWidgetItem* child = item; - QTreeWidgetItem* parent = child->parent(); - while ( parent != 0 ) + QTreeWidgetItem *featItem = featureItem( item ); + if ( !featItem ) + return; + + int fid = featItem->data( 0, Qt::UserRole ).toInt(); + + delete mRubberBand; + mRubberBand = 0; + + QgsFeature feat; + if ( ! layer->featureAtId( fid, feat, true, false ) ) { - child = parent; - parent = parent->parent(); + return; } - parent = child; - mValues.clear(); - - // For the code below we - // need to do the comparison on the text strings rather than the - // pointers because if the user clicked on the parent, we need - // to pick up which child that actually is (the parent in the - // identify results dialog box is just one of the children - // that has been chosen by some method). - - int valuesIndex = 0; - - for ( int j = 0; j < parent->childCount(); ++j ) + if ( !feat.geometry() ) { - // For derived attributes, build up a virtual name - if ( parent->child( j )->text( 0 ) == mDerivedLabel ) - { - for ( int k = 0; k < parent->child( j )->childCount(); ++k ) - { - mValues.push_back( - std::make_pair( mDerivedLabel + "." - + parent->child( j )->child( k )->text( 0 ), - parent->child( j )->child( k )->text( 1 ) ) ); + return; + } - if ( item == parent->child( j )->child( k ) ) - { - mClickedOnValue = valuesIndex; - } + mRubberBand = new QgsRubberBand( mCanvas, feat.geometry()->type() == QGis::Polygon ); - valuesIndex++; - } - } - else // do the actual feature attributes - { - mValues.push_back( std::make_pair( parent->child( j )->text( 0 ), - parent->child( j )->text( 1 ) ) ); - - if ( item == parent->child( j ) ) - { - mClickedOnValue = valuesIndex; - } - - valuesIndex++; - } + if ( mRubberBand ) + { + mRubberBand->setToGeometry( feat.geometry(), layer ); + mRubberBand->setWidth( 2 ); + mRubberBand->setColor( Qt::red ); + mRubberBand->show(); } } + +void QgsIdentifyResults::editFeature( QTreeWidgetItem *item ) +{ + QgsVectorLayer *layer = vectorLayer( item ); + if ( !layer || !layer->isEditable() ) + return; + + QTreeWidgetItem *featItem = featureItem( item ); + if ( !featItem ) + return; + + int fid = featItem->data( 0, Qt::UserRole ).toInt(); + + QgsFeature f; + if ( ! layer->featureAtId( fid, f ) ) + return; + + QgsAttributeMap src = f.attributeMap(); + + layer->beginEditCommand( tr( "Attribute changed" ) ); + QgsAttributeDialog *ad = new QgsAttributeDialog( layer, &f ); + if ( ad->exec() ) + { + const QgsAttributeMap &dst = f.attributeMap(); + for ( QgsAttributeMap::const_iterator it = dst.begin(); it != dst.end(); it++ ) + { + if ( !src.contains( it.key() ) || it.value() != src[it.key()] ) + { + layer->changeAttributeValue( f.id(), it.key(), it.value() ); + } + } + layer->endEditCommand(); + } + else + { + layer->destroyEditCommand(); + } + + delete ad; + mCanvas->refresh(); +} diff --git a/src/app/qgsidentifyresults.h b/src/app/qgsidentifyresults.h index cd8576f4240..0c25c3b9d97 100644 --- a/src/app/qgsidentifyresults.h +++ b/src/app/qgsidentifyresults.h @@ -30,6 +30,11 @@ class QTreeWidgetItem; class QAction; class QMenu; +class QgsMapLayer; +class QgsVectorLayer; +class QgsRubberBand; +class QgsMapCanvas; + /** *@author Gary E.Sherman */ @@ -41,54 +46,26 @@ class QgsIdentifyResults: public QDialog, private Ui::QgsIdentifyResultsBase //! Constructor - takes it own copy of the QgsAttributeAction so // that it is independent of whoever created it. - QgsIdentifyResults( const QgsAttributeAction& actions, QWidget *parent = 0, Qt::WFlags f = 0 ); + QgsIdentifyResults( QgsMapCanvas *canvas, QWidget *parent = 0, Qt::WFlags f = 0 ); ~QgsIdentifyResults(); - /** Add an attribute to the feature display node */ - void addAttribute( QTreeWidgetItem *parent, QString field, QString value ); - - /** Add an attribute */ - void addAttribute( QString field, QString value ); - - /** Add a derived attribute (e.g. Length, Area) to the feature display node */ - void addDerivedAttribute( QTreeWidgetItem *parent, QString field, QString value ); - - /** Add an action to the feature display node */ - void addAction( QTreeWidgetItem *parent, int id, QString field, QString value ); - - /** Add an edit action to the feature display node */ - void addEdit( QTreeWidgetItem *parent, int id ); - - /** Add a feature node to the feature display */ - QTreeWidgetItem * addNode( QString label ); - /** Set the title for the identify results dialog */ - void setTitle( QString title ); - /** Set header column */ - void setColumnText( int column, const QString & label ); - void saveWindowLocation(); - void restorePosition(); - void closeEvent( QCloseEvent *e ); - void showAllAttributes(); - - /** Resize all of the columns to fit the data in them */ - void expandColumnsToFit(); - /** Remove results */ void clear(); + /** Add add feature */ + void addFeature( QgsMapLayer *layer, int fid, + QString displayField, QString displayValue, + const QMap< QString, QString > &attributes, + const QMap< QString, QString > &derivedAttributes ); + + void closeEvent( QCloseEvent *e ); + /** Set "No features ... " */ void setMessage( QString shortMsg, QString longMsg ); - /** Set actions */ - void setActions( const QgsAttributeAction& actions ); - - //void accept(); - //void reject(); - signals: - void selectedFeatureChanged( int featureId ); - void editFeature( int featureId ); + void selectedFeatureChanged( QgsVectorLayer *, int featureId ); public slots: @@ -98,43 +75,39 @@ class QgsIdentifyResults: public QDialog, private Ui::QgsIdentifyResultsBase void contextMenuEvent( QContextMenuEvent* ); void popupItemSelected( QAction* menuAction ); - /* Item in tree was clicked */ - void clicked( QTreeWidgetItem *lvi ); + void layerDestroyed(); //! Context help void on_buttonHelp_clicked(); /* Called when an item is expanded so that we can ensure that the column width if expanded to show it */ - void itemExpanded( QTreeWidgetItem* ); + void itemExpanded( QTreeWidgetItem * ); //! sends signal if current feature id has changed - void handleCurrentItemChanged( QTreeWidgetItem* current, QTreeWidgetItem* previous ); - + void handleCurrentItemChanged( QTreeWidgetItem *current, QTreeWidgetItem *previous ); private: + QMenu *mActionPopup; + QgsRubberBand *mRubberBand; + QgsMapCanvas *mCanvas; - bool mEditable; - QgsAttributeAction mActions; - int mClickedOnValue; - QMenu* mActionPopup; - std::vector > mValues; static const int context_id = 689216579; - int mCurrentFeatureId; - QString mDerivedLabel; - /** - Keeps track of what derived-attribute (e.g. Length, Area) - root nodes have been generated for each feature in this widget. + QgsVectorLayer *vectorLayer( QTreeWidgetItem *item ); + QTreeWidgetItem *featureItem( QTreeWidgetItem *item ); + QTreeWidgetItem *layerItem( QObject *layer ); + QTreeWidgetItem *retrieveAttributes( QTreeWidgetItem *item, std::vector< std::pair > &attributes ); - First item: Feature root node - Second item: Derived-attribute root node for that feature - */ - std::map mDerivedAttributeRootNodes; + void setColumnText( int column, const QString & label ); + void expandColumnsToFit(); + void saveWindowLocation(); + void restorePosition(); - // Convenience function to populate mValues with all of the item names and - // values for a item, including the derived ones. - void extractAllItemData( QTreeWidgetItem* item ); + void highlightFeature( QTreeWidgetItem *item ); + void editFeature( QTreeWidgetItem *item ); + + void doAction( QTreeWidgetItem *item ); }; #endif diff --git a/src/app/qgsmaptoolidentify.cpp b/src/app/qgsmaptoolidentify.cpp index cd5abd0d656..d0f32fedbcb 100644 --- a/src/app/qgsmaptoolidentify.cpp +++ b/src/app/qgsmaptoolidentify.cpp @@ -26,11 +26,11 @@ #include "qgsmessageviewer.h" #include "qgsmaptoolidentify.h" #include "qgsrasterlayer.h" -#include "qgsrubberband.h" #include "qgscoordinatereferencesystem.h" #include "qgsvectordataprovider.h" #include "qgsvectorlayer.h" -#include "qgsattributedialog.h" +#include "qgsproject.h" +#include "qgsmaplayerregistry.h" #include #include @@ -39,14 +39,13 @@ #include QgsMapToolIdentify::QgsMapToolIdentify( QgsMapCanvas* canvas ) - : QgsMapTool( canvas ), - mResults( 0 ), - mRubberBand( 0 ), - mLayer( 0 ) + : QgsMapTool( canvas ) { // set cursor QPixmap myIdentifyQPixmap = QPixmap(( const char ** ) identify_cursor ); mCursor = QCursor( myIdentifyQPixmap, 1, 1 ); + + mResults = new QgsIdentifyResults( canvas, mCanvas->window() ); } QgsMapToolIdentify::~QgsMapToolIdentify() @@ -55,8 +54,6 @@ QgsMapToolIdentify::~QgsMapToolIdentify() { mResults->done( 0 ); } - - delete mRubberBand; } void QgsMapToolIdentify::canvasMoveEvent( QMouseEvent * e ) @@ -74,164 +71,86 @@ void QgsMapToolIdentify::canvasReleaseEvent( QMouseEvent * e ) return; } - // delete rubber band if there was any - delete mRubberBand; - mRubberBand = 0; + mResults->clear(); - mLayer = mCanvas->currentLayer(); + QSettings settings; + int identifyMode = settings.value( "/Map/identifyMode", 0 ).toInt(); - if ( !mLayer ) + bool res = false; + + if ( identifyMode == 0 ) { - QMessageBox::warning( mCanvas, - tr( "No active layer" ), - tr( "To identify features, you must choose an active layer by clicking on its name in the legend" ) ); - return; - } + QgsMapLayer *layer = mCanvas->currentLayer(); - // cleanup, when layer is removed - connect( mLayer, SIGNAL( destroyed() ), this, SLOT( layerDestroyed() ) ); + if ( !layer ) + { + QMessageBox::warning( mCanvas, + tr( "No active layer" ), + tr( "To identify features, you must choose an active layer by clicking on its name in the legend" ) ); + return; + } - // call identify method for selected layer - - // In the special case of the WMS provider, - // coordinates are sent back to the server as pixel coordinates - // not the layer's native CRS. So identify on screen coordinates! - if ( mLayer->type() == QgsMapLayer::RasterLayer && - dynamic_cast( mLayer )->providerKey() == "wms" ) - { - identifyRasterWmsLayer( QgsPoint( e->x(), e->y() ) ); + res = identifyLayer( layer, e->x(), e->y() ); } else { - // convert screen coordinates to map coordinates - QgsPoint idPoint = mCanvas->getCoordinateTransform()->toMapCoordinates( e->x(), e->y() ); + QStringList noIdentifyLayerIdList = QgsProject::instance()->readListEntry( "Identify", "/disabledLayers" ); - if ( mLayer->type() == QgsMapLayer::VectorLayer ) + for ( int i = 0; i < mCanvas->layerCount(); i++ ) { - identifyVectorLayer( idPoint ); - } - else if ( mLayer->type() == QgsMapLayer::RasterLayer ) - { - identifyRasterLayer( idPoint ); - } - else - { - QgsDebugMsg( "unknown layer type!" ); + QgsMapLayer *layer = mCanvas->layer( i ); + + if ( noIdentifyLayerIdList.contains( layer->getLayerID() ) ) + continue; + + if ( identifyLayer( layer, e->x(), e->y() ) ) + { + res = true; + if ( identifyMode == 1 ) + break; + } } } -} - -void QgsMapToolIdentify::identifyRasterLayer( const QgsPoint& point ) -{ - QgsRasterLayer *layer = dynamic_cast( mLayer ); - if ( !layer ) - { - return; - } - - QMap attributes; - layer->identify( point, attributes ); - - if ( !mResults ) - { - QgsAttributeAction aa; - mResults = new QgsIdentifyResults( aa, mCanvas->window() ); - mResults->setAttribute( Qt::WA_DeleteOnClose ); - // Be informed when the dialog box is closed so that we can stop using it. - connect( mResults, SIGNAL( accepted() ), this, SLOT( resultsDialogGone() ) ); - connect( mResults, SIGNAL( rejected() ), this, SLOT( resultsDialogGone() ) ); - mResults->restorePosition(); - } - else + if ( res ) { + mResults->show(); mResults->raise(); - mResults->clear(); - } - - mResults->setTitle( layer->name() ); - mResults->setColumnText( 0, tr( "Band" ) ); - - QMap::iterator it; - for ( it = attributes.begin(); it != attributes.end(); it++ ) - { - mResults->addAttribute( it.key(), it.value() ); - } - - mResults->addAttribute( tr( "(clicked coordinate)" ), point.toString() ); - - mResults->showAllAttributes(); - mResults->show(); -} - - -void QgsMapToolIdentify::identifyRasterWmsLayer( const QgsPoint& point ) -{ - QgsRasterLayer *layer = dynamic_cast( mLayer ); - if ( !layer ) - { - return; - } - - //if WMS layer does not cover the view origin, - //we need to map the view pixel coordinates - //to WMS layer pixel coordinates - QgsRectangle viewExtent = mCanvas->extent(); - double mapUnitsPerPixel = mCanvas->mapUnitsPerPixel(); - if ( mapUnitsPerPixel == 0 ) - { - return; - } - double xMinView = viewExtent.xMinimum(); - double yMaxView = viewExtent.yMaximum(); - - QgsRectangle layerExtent = layer->extent(); - double xMinLayer = layerExtent.xMinimum(); - double yMaxLayer = layerExtent.yMaximum(); - - double i, j; - - if ( xMinView < xMinLayer ) - { - i = ( int )( point.x() - ( xMinLayer - xMinView ) / mapUnitsPerPixel ); } else { - i = point.x(); + mResults->hide(); + QMessageBox::information( 0, tr( "Identify results" ), tr( "No features at this position found." ) ); } +} - if ( yMaxView > yMaxLayer ) +bool QgsMapToolIdentify::identifyLayer( QgsMapLayer *layer, int x, int y ) +{ + bool res = false; + + if ( layer->type() == QgsMapLayer::RasterLayer ) { - j = ( int )( point.y() - ( yMaxView - yMaxLayer ) / mapUnitsPerPixel ); + res = identifyRasterLayer( dynamic_cast( layer ), x, y ); } else { - j = point.y(); + res = identifyVectorLayer( dynamic_cast( layer ), x, y ); } - - QString text = layer->identifyAsText( QgsPoint( i, j ) ); - - if ( text.isEmpty() ) - { - showError(); - return; - } - - QgsMessageViewer* viewer = new QgsMessageViewer(); - viewer->setWindowTitle( layer->name() ); - viewer->setMessageAsPlainText( tr( "WMS identify result for %1:\n%2" ).arg( point.toString() ).arg( text ) ); - - viewer->showMessage(); // deletes itself on close + return res; } -void QgsMapToolIdentify::identifyVectorLayer( const QgsPoint& point ) + +bool QgsMapToolIdentify::identifyVectorLayer( QgsVectorLayer *layer, int x, int y ) { - QgsVectorLayer *layer = dynamic_cast( mLayer ); if ( !layer ) - { - return; - } + return false; + + QMap< QString, QString > attributes, derivedAttributes; + + QgsPoint point = mCanvas->getCoordinateTransform()->toMapCoordinates( x, y ); + + derivedAttributes.insert( tr( "(clicked coordinate)" ), point.toString() ); // load identify radius from settings QSettings settings; @@ -239,8 +158,6 @@ void QgsMapToolIdentify::identifyVectorLayer( const QgsPoint& point ) QString ellipsoid = settings.value( "/qgis/measure/ellipsoid", "WGS84" ).toString(); int featureCount = 0; - QgsAttributeAction& actions = *layer->actions(); - QString fieldIndex = layer->displayField(); const QgsFieldMap& fields = layer->pendingFields(); // init distance/area calculator @@ -249,9 +166,10 @@ void QgsMapToolIdentify::identifyVectorLayer( const QgsPoint& point ) calc.setEllipsoid( ellipsoid ); calc.setSourceCrs( layer->srs().srsid() ); - mFeatureList.clear(); QApplication::setOverrideCursor( Qt::WaitCursor ); + QgsFeatureList featureList; + // toLayerCoordinates will throw an exception for an 'invalid' point. // For example, if you project a world map onto a globe using EPSG 2163 // and then click somewhere off the globe, an exception will be thrown. @@ -271,7 +189,7 @@ void QgsMapToolIdentify::identifyVectorLayer( const QgsPoint& point ) layer->select( layer->pendingAllAttributesList(), r, true, true ); QgsFeature f; while ( layer->nextFeature( f ) ) - mFeatureList << QgsFeature( f ); + featureList << QgsFeature( f ); } catch ( QgsCsException & cse ) { @@ -282,62 +200,29 @@ void QgsMapToolIdentify::identifyVectorLayer( const QgsPoint& point ) QApplication::restoreOverrideCursor(); - if ( layer->isEditable() && mFeatureList.size() == 1 ) - { - editFeature( mFeatureList[0] ); - return; - } + QgsFeatureList::iterator f_it = featureList.begin(); - // display features falling within the search radius - if ( !mResults ) - { - mResults = new QgsIdentifyResults( actions, mCanvas->window() ); - mResults->setAttribute( Qt::WA_DeleteOnClose ); - // Be informed when the dialog box is closed so that we can stop using it. - connect( mResults, SIGNAL( accepted() ), this, SLOT( resultsDialogGone() ) ); - connect( mResults, SIGNAL( rejected() ), this, SLOT( resultsDialogGone() ) ); - connect( mResults, SIGNAL( selectedFeatureChanged( int ) ), this, SLOT( highlightFeature( int ) ) ); - connect( mResults, SIGNAL( editFeature( int ) ), this, SLOT( editFeature( int ) ) ); - - // restore the identify window position and show it - mResults->restorePosition(); - } - else - { - mResults->raise(); - mResults->clear(); - mResults->setActions( actions ); - } - - QApplication::setOverrideCursor( Qt::WaitCursor ); - - int lastFeatureId = 0; - QgsFeatureList::iterator f_it = mFeatureList.begin(); - - for ( ; f_it != mFeatureList.end(); ++f_it ) + for ( ; f_it != featureList.end(); ++f_it ) { featureCount++; - QTreeWidgetItem* featureNode = mResults->addNode( "foo" ); - featureNode->setData( 0, Qt::UserRole, QVariant( f_it->id() ) ); // save feature id - lastFeatureId = f_it->id(); - featureNode->setText( 0, fieldIndex ); - - if ( layer->isEditable() ) - mResults->addEdit( featureNode, f_it->id() ); + int fid = f_it->id(); + QString displayField, displayValue; + QMap attributes, derivedAttributes; const QgsAttributeMap& attr = f_it->attributeMap(); for ( QgsAttributeMap::const_iterator it = attr.begin(); it != attr.end(); ++it ) { - //QgsDebugMsg(it->fieldName() + " == " + fieldIndex); - - if ( fields[it.key()].name() == fieldIndex ) - { - featureNode->setText( 1, it->toString() ); - } QString attributeName = layer->attributeDisplayName( it.key() ); - mResults->addAttribute( featureNode, attributeName, it->isNull() ? "NULL" : it->toString() ); + + if ( fields[it.key()].name() == layer->displayField() ) + { + displayField = attributeName; + displayValue = it->toString(); + } + + attributes.insert( attributeName, it->isNull() ? "NULL" : it->toString() ); } // Calculate derived attributes and insert: @@ -348,18 +233,18 @@ void QgsMapToolIdentify::identifyVectorLayer( const QgsPoint& point ) QGis::UnitType myDisplayUnits; convertMeasurement( calc, dist, myDisplayUnits, false ); QString str = calc.textUnit( dist, 3, myDisplayUnits, false ); // dist and myDisplayUnits are out params - mResults->addDerivedAttribute( featureNode, tr( "Length" ), str ); + derivedAttributes.insert( tr( "Length" ), str ); if ( f_it->geometry()->wkbType() == QGis::WKBLineString ) { // Add the start and end points in as derived attributes str = QLocale::system().toString( f_it->geometry()->asPolyline().first().x(), 'g', 10 ); - mResults->addDerivedAttribute( featureNode, tr( "firstX", "attributes get sorted; translation for lastX should be lexically larger than this one" ), str ); + derivedAttributes.insert( tr( "firstX", "attributes get sorted; translation for lastX should be lexically larger than this one" ), str ); str = QLocale::system().toString( f_it->geometry()->asPolyline().first().y(), 'g', 10 ); - mResults->addDerivedAttribute( featureNode, tr( "firstY" ), str ); + derivedAttributes.insert( tr( "firstY" ), str ); str = QLocale::system().toString( f_it->geometry()->asPolyline().last().x(), 'g', 10 ); - mResults->addDerivedAttribute( featureNode, tr( "lastX", "attributes get sorted; translation for firstX should be lexically smaller than this one" ), str ); + derivedAttributes.insert( tr( "lastX", "attributes get sorted; translation for firstX should be lexically smaller than this one" ), str ); str = QLocale::system().toString( f_it->geometry()->asPolyline().last().y(), 'g', 10 ); - mResults->addDerivedAttribute( featureNode, tr( "lastY" ), str ); + derivedAttributes.insert( tr( "lastY" ), str ); } } else if ( layer->geometryType() == QGis::Polygon ) @@ -368,179 +253,91 @@ void QgsMapToolIdentify::identifyVectorLayer( const QgsPoint& point ) QGis::UnitType myDisplayUnits; convertMeasurement( calc, area, myDisplayUnits, true ); // area and myDisplayUnits are out params QString str = calc.textUnit( area, 3, myDisplayUnits, true ); - mResults->addDerivedAttribute( featureNode, tr( "Area" ), str ); + derivedAttributes.insert( tr( "Area" ), str ); } else if ( layer->geometryType() == QGis::Point ) { // Include the x and y coordinates of the point as a derived attribute QString str; str = QLocale::system().toString( f_it->geometry()->asPoint().x(), 'g', 10 ); - mResults->addDerivedAttribute( featureNode, "X", str ); + derivedAttributes.insert( "X", str ); str = QLocale::system().toString( f_it->geometry()->asPoint().y(), 'g', 10 ); - mResults->addDerivedAttribute( featureNode, "Y", str ); + derivedAttributes.insert( "Y", str ); } - // Add actions - QgsAttributeAction::aIter iter = actions.begin(); - for ( register int i = 0; iter != actions.end(); ++iter, ++i ) - { - mResults->addAction( featureNode, i, tr( "action" ), iter->name() ); - } + mResults->addFeature( layer, fid, displayField, displayValue, attributes, derivedAttributes ); } QgsDebugMsg( "Feature count on identify: " + QString::number( featureCount ) ); - //also test the not commited features //todo: eliminate copy past code - - if ( featureCount == 1 ) - { - mResults->showAllAttributes(); - highlightFeature( lastFeatureId ); - } - else if ( featureCount == 0 ) - { - mResults->setMessage( tr( "No features found" ), tr( "No features were found in the active layer at the point you clicked" ) ); - } - - mResults->setTitle( tr( "%1 - %n feature(s) found", "Identify results window title", featureCount ).arg( layer->name() ) ); - QApplication::restoreOverrideCursor(); - mResults->show(); + return featureCount > 0; } -void QgsMapToolIdentify::showError() +bool QgsMapToolIdentify::identifyRasterLayer( QgsRasterLayer *layer, int x, int y ) { -#if 0 - QMessageBox::warning( - this, - mapLayer->lastErrorTitle(), - tr( "Could not draw %1 because:\n%2", "COMMENTED OUT" ).arg( mapLayer->name() ).arg( mapLayer->lastError() ) - ); -#endif + bool res = true; - QgsMessageViewer * mv = new QgsMessageViewer(); + if ( !layer ) + return false; - if ( mLayer ) + QgsRasterDataProvider *dprovider = layer->dataProvider(); + if ( dprovider && ( dprovider->capabilities() & QgsRasterDataProvider::Identify ) == 0 ) + return false; + + QMap< QString, QString > attributes, derivedAttributes; + QgsPoint idPoint = mCanvas->getCoordinateTransform()->toMapCoordinates( x, y ); + QString type; + + if ( layer->providerKey() == "wms" ) { - mv->setWindowTitle( mLayer->lastErrorTitle() ); - mv->setMessageAsPlainText( tr( "Could not identify objects on %1 because:\n%2" ) - .arg( mLayer->name() ).arg( mLayer->lastError() ) ); + type = tr( "WMS layer" ); + + //if WMS layer does not cover the view origin, + //we need to map the view pixel coordinates + //to WMS layer pixel coordinates + QgsRectangle viewExtent = mCanvas->extent(); + QgsRectangle layerExtent = layer->extent(); + double mapUnitsPerPixel = mCanvas->mapUnitsPerPixel(); + if ( mapUnitsPerPixel > 0 && viewExtent.intersects( layerExtent ) ) + { + double xMinView = viewExtent.xMinimum(); + double yMaxView = viewExtent.yMaximum(); + double xMinLayer = layerExtent.xMinimum(); + double yMaxLayer = layerExtent.yMaximum(); + + idPoint.set( + xMinView < xMinLayer ? floor( x - ( xMinLayer - xMinView ) / mapUnitsPerPixel ) : x, + yMaxView > yMaxLayer ? floor( y - ( yMaxView - yMaxLayer ) / mapUnitsPerPixel ) : y + ); + + attributes.insert( tr( "Feature info" ), layer->identifyAsText( idPoint ) ); + } + else + { + res = false; + } } else { - mv->setWindowTitle( tr( "Layer was removed" ) ); - mv->setMessageAsPlainText( tr( "Layer to identify objects on was removed" ) ); + type = tr( "Raster" ); + res = layer->extent().contains( idPoint ) && layer->identify( idPoint, attributes ); } - mv->exec(); // deletes itself on close + if ( res ) + { + derivedAttributes.insert( tr( "(clicked coordinate)" ), idPoint.toString() ); + mResults->addFeature( layer, -1, type, "", attributes, derivedAttributes ); + } + + return res; } + void QgsMapToolIdentify::resultsDialogGone() { mResults = 0; - - delete mRubberBand; - mRubberBand = 0; -} - -void QgsMapToolIdentify::deactivate() -{ - if ( mResults ) - mResults->done( 0 ); // close the window - QgsMapTool::deactivate(); -} - -void QgsMapToolIdentify::highlightFeature( int featureId ) -{ - QgsVectorLayer* layer = dynamic_cast( mLayer ); - if ( !layer ) - return; - - delete mRubberBand; - mRubberBand = 0; - - QgsFeature feat; - if ( ! layer->featureAtId( featureId, feat, true, false ) ) - { - return; - } - - if ( !feat.geometry() ) - { - return; - } - - mRubberBand = new QgsRubberBand( mCanvas, feat.geometry()->type() == QGis::Polygon ); - - if ( mRubberBand ) - { - mRubberBand->setToGeometry( feat.geometry(), layer ); - mRubberBand->setWidth( 2 ); - mRubberBand->setColor( Qt::red ); - mRubberBand->show(); - } -} - -void QgsMapToolIdentify::editFeature( int featureId ) -{ - for ( QgsFeatureList::iterator it = mFeatureList.begin(); it != mFeatureList.end(); it++ ) - { - if ( it->id() == featureId ) - { - editFeature( *it ); - break; - } - } -} - -void QgsMapToolIdentify::editFeature( QgsFeature &f ) -{ - QgsVectorLayer* layer = dynamic_cast( mLayer ); - if ( !layer || !layer->isEditable() ) - { - return; - } - - QgsAttributeMap src = f.attributeMap(); - - layer->beginEditCommand( tr( "Attribute changed" ) ); - QgsAttributeDialog *ad = new QgsAttributeDialog( layer, &f ); - if ( ad->exec() ) - { - const QgsAttributeMap &dst = f.attributeMap(); - for ( QgsAttributeMap::const_iterator it = dst.begin(); it != dst.end(); it++ ) - { - if ( !src.contains( it.key() ) || it.value() != src[it.key()] ) - { - layer->changeAttributeValue( f.id(), it.key(), it.value() ); - } - } - layer->endEditCommand(); - } - else - { - layer->destroyEditCommand(); - } - - delete ad; - mCanvas->refresh(); -} - -void QgsMapToolIdentify::layerDestroyed() -{ - mLayer = 0; - - if ( mResults ) - { - mResults->clear(); - } - - if ( mRubberBand ) - { - delete mRubberBand; - mRubberBand = 0; - } } void QgsMapToolIdentify::convertMeasurement( QgsDistanceArea &calc, double &measure, QGis::UnitType &u, bool isArea ) diff --git a/src/app/qgsmaptoolidentify.h b/src/app/qgsmaptoolidentify.h index 3b1fbdf3f56..68f586c200f 100644 --- a/src/app/qgsmaptoolidentify.h +++ b/src/app/qgsmaptoolidentify.h @@ -57,68 +57,27 @@ class QgsMapToolIdentify : public QgsMapTool //! Overridden mouse release event virtual void canvasReleaseEvent( QMouseEvent * e ); - //! called when map tool is being deactivated - virtual void deactivate(); - - public slots: - //! creates rubberband on top of the feature to highlight it - void highlightFeature( int featureId ); - - //! edit a feature - void editFeature( int featureId ); - private: - - /** - * \brief function for identifying pixel values at a coordinate in a non-OGC-WMS raster layer - * - * \param point[in] The coordinate (as the CRS of the raster layer) - */ - void identifyRasterLayer( const QgsPoint& point ); - - /** - * \brief function for identifying a pixel in a OGC WMS raster layer - * - * \param point[in] The pixel coordinate (as it was displayed locally on screen) - * - * \note WMS Servers prefer to receive coordinates in image space not CRS space, therefore - * this special variant of identifyRasterLayer. - */ - void identifyRasterWmsLayer( const QgsPoint& point ); - - /** - * \brief function for identifying features at a coordinate in a vector layer - * - * \param point[in] The coordinate (as the CRS of the vector layer) - */ - void identifyVectorLayer( const QgsPoint& point ); - - //! show whatever error is exposed by the QgsMapLayer. - void showError(); - - //! edit a feature - void editFeature( QgsFeature &f ); + bool identifyLayer( QgsMapLayer *layer, int x, int y ); + bool identifyRasterLayer( QgsRasterLayer *layer, int x, int y ); + bool identifyVectorLayer( QgsVectorLayer *layer, int x, int y ); //! Pointer to the identify results dialog for name/value pairs QgsIdentifyResults *mResults; - //! Rubber band for highlighting identified feature - QgsRubberBand* mRubberBand; - - QgsMapLayer *mLayer; - - //! list of identified features - QgsFeatureList mFeatureList; - //! Private helper void convertMeasurement( QgsDistanceArea &calc, double &measure, QGis::UnitType &u, bool isArea ); + void addFeature( QgsMapLayer *layer, int fid, + QString displayField, QString displayValue, + const QMap< QString, QString > &attributes, + const QMap< QString, QString > &derivedAttributes ); + + /** Add an action to the feature display node */ + private slots: // Let us know when the QgsIdentifyResults dialog box has been closed void resultsDialogGone(); - - // layer was destroyed - void layerDestroyed(); }; #endif diff --git a/src/app/qgsoptions.cpp b/src/app/qgsoptions.cpp index 5f6c694a587..9666a921f3a 100644 --- a/src/app/qgsoptions.cpp +++ b/src/app/qgsoptions.cpp @@ -50,9 +50,14 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WFlags fl ) : connect( buttonBox, SIGNAL( rejected() ), this, SLOT( reject() ) ); connect( this, SIGNAL( accepted() ), this, SLOT( saveOptions() ) ); + cmbIdentifyMode->addItem( tr( "current layer" ), 0 ); + cmbIdentifyMode->addItem( tr( "top down, stop at first" ), 1 ); + cmbIdentifyMode->addItem( tr( "top down" ), 2 ); + // read the current browser and set it QSettings settings; - QgsDebugMsg( QString( "Standard Identify radius setting: %1" ).arg( QGis::DEFAULT_IDENTIFY_RADIUS ) ); + int identifyMode = settings.value( "/Map/identifyMode", 0 ).toInt(); + cmbIdentifyMode->setCurrentIndex( cmbIdentifyMode->findData( identifyMode ) ); double identifyValue = settings.value( "/Map/identifyRadius", QGis::DEFAULT_IDENTIFY_RADIUS ).toDouble(); QgsDebugMsg( QString( "Standard Identify radius setting read from settings file: %1" ).arg( identifyValue ) ); spinBoxIdentifyValue->setValue( identifyValue ); @@ -349,6 +354,7 @@ void QgsOptions::saveOptions() settings.setValue( "proxy/proxyExcludedUrls", proxyExcludeString ); //general settings + settings.setValue( "/Map/identifyMode", cmbIdentifyMode->itemData( cmbIdentifyMode->currentIndex() ).toInt() ); settings.setValue( "/Map/identifyRadius", spinBoxIdentifyValue->value() ); settings.setValue( "/qgis/showLegendClassifiers", cbxLegendClassifiers->isChecked() ); settings.setValue( "/qgis/hideSplash", cbxHideSplash->isChecked() ); diff --git a/src/app/qgsprojectproperties.cpp b/src/app/qgsprojectproperties.cpp index 45646cffcdb..6a2e18a1303 100644 --- a/src/app/qgsprojectproperties.cpp +++ b/src/app/qgsprojectproperties.cpp @@ -30,6 +30,7 @@ #include "qgsproject.h" #include "qgsrenderer.h" #include "qgssnappingdialog.h" +#include "qgsrasterlayer.h" //qt includes #include @@ -205,6 +206,56 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas* mapCanvas, QWidget *pa mSnappingLayerSettings.insert( *idIter, newEntry ); } } + + QStringList noIdentifyLayerIdList = QgsProject::instance()->readListEntry( "Identify", "/disabledLayers" ); + + const QMap &mapLayers = QgsMapLayerRegistry::instance()->mapLayers(); + + twIdentifyLayers->setColumnCount( 3 ); + twIdentifyLayers->horizontalHeader()->setVisible( true ); + twIdentifyLayers->setHorizontalHeaderItem( 0, new QTableWidgetItem( tr( "Layer" ) ) ); + twIdentifyLayers->setHorizontalHeaderItem( 1, new QTableWidgetItem( tr( "Type" ) ) ); + twIdentifyLayers->setHorizontalHeaderItem( 2, new QTableWidgetItem( tr( "Identifiable" ) ) ); + twIdentifyLayers->setRowCount( mapLayers.size() ); + twIdentifyLayers->verticalHeader()->setResizeMode( QHeaderView::ResizeToContents ); + + int i = 0; + for ( QMap::const_iterator it = mapLayers.constBegin(); it != mapLayers.constEnd(); it++, i++ ) + { + currentLayer = it.value(); + + QTableWidgetItem *twi = new QTableWidgetItem( QString::number( i ) ); + twi->setData( Qt::UserRole, it.key() ); + twIdentifyLayers->setVerticalHeaderItem( i, twi ); + + twIdentifyLayers->setItem( i, 0, new QTableWidgetItem( currentLayer->name() ) ); + + QString type; + if ( currentLayer->type() == QgsMapLayer::VectorLayer ) + { + type = tr( "Vector" ); + } + else if ( currentLayer->type() == QgsMapLayer::RasterLayer ) + { + QgsRasterLayer *rl = dynamic_cast( currentLayer ); + + if ( rl && rl->providerKey() == "wms" ) + { + type = tr( "WMS" ); + } + else + { + type = tr( "Raster" ); + } + } + + twIdentifyLayers->setItem( i, 1, new QTableWidgetItem( type ) ); + + QCheckBox *cb = new QCheckBox(); + cb->setChecked( !noIdentifyLayerIdList.contains( currentLayer->getLayerID() ) ); + twIdentifyLayers->setCellWidget( i, 2, cb ); + } + restoreState(); } @@ -387,6 +438,19 @@ void QgsProjectProperties::apply() QgsProject::instance()->writeEntry( "Digitizing", "/LayerSnappingEnabledList", enabledList ); } + QStringList noIdentifyLayerList; + for ( int i = 0; i < twIdentifyLayers->rowCount(); i++ ) + { + QCheckBox *cb = dynamic_cast( twIdentifyLayers->cellWidget( i, 2 ) ); + if ( cb && !cb->isChecked() ) + { + QString id = twIdentifyLayers->verticalHeaderItem( i )->data( Qt::UserRole ).toString(); + noIdentifyLayerList << id; + } + } + + QgsProject::instance()->writeEntry( "Identify", "/disabledLayers", noIdentifyLayerList ); + //todo XXX set canvas colour emit refresh(); } diff --git a/src/core/qgsrectangle.cpp b/src/core/qgsrectangle.cpp index 3c3080adf65..ee692533965 100644 --- a/src/core/qgsrectangle.cpp +++ b/src/core/qgsrectangle.cpp @@ -161,6 +161,11 @@ bool QgsRectangle::contains( const QgsRectangle& rect ) const return ( rect.xmin >= xmin && rect.xmax <= xmax && rect.ymin >= ymin && rect.ymax <= ymax ); } +bool QgsRectangle::contains( const QgsPoint &p ) const +{ + return xmin <= p.x() && p.x() <= xmax && + ymin <= p.y() && p.y() <= ymax; +} void QgsRectangle::combineExtentWith( QgsRectangle * rect ) { diff --git a/src/core/qgsrectangle.h b/src/core/qgsrectangle.h index b379fa50209..e7ee10094bc 100644 --- a/src/core/qgsrectangle.h +++ b/src/core/qgsrectangle.h @@ -87,6 +87,9 @@ class CORE_EXPORT QgsRectangle //! return true when rectangle contains other rectangle //! @note added in version 1.1 bool contains( const QgsRectangle& rect ) const; + //! return true when rectangle contains a point + //! @note added in version 1.3 + bool contains( const QgsPoint &p ) const; //! expand the rectangle so that covers both the original rectangle and the given rectangle void combineExtentWith( QgsRectangle *rect ); //! expand the rectangle so that covers both the original rectangle and the given point diff --git a/src/core/raster/qgsrasterlayer.cpp b/src/core/raster/qgsrasterlayer.cpp index dc58e3b3866..274a9c0f11a 100644 --- a/src/core/raster/qgsrasterlayer.cpp +++ b/src/core/raster/qgsrasterlayer.cpp @@ -1507,7 +1507,7 @@ bool QgsRasterLayer::draw( QgsRenderContext& rendererContext ) { QgsDebugMsg( "Wanting a '" + mProviderKey + "' provider to draw this." ); - emit statusChanged( tr( "Retrieving using %1" ).arg( mProviderKey ) ); + emit statusChanged( tr( "Retrieving %1 using %2" ).arg( name() ).arg( mProviderKey ) ); mDataProvider->setDpi( rendererContext.rasterScaleFactor() * 25.4 * rendererContext.scaleFactor() ); @@ -1594,6 +1594,7 @@ bool QgsRasterLayer::draw( QgsRenderContext& rendererContext ) delete image; } + emit statusChanged( tr( "%1 retrieved using %2" ).arg( name() ).arg( mProviderKey ) ); } else { @@ -1846,12 +1847,9 @@ bool QgsRasterLayer::identify( const QgsPoint& thePoint, QMap& return false; } - double x = thePoint.x(); - double y = thePoint.y(); + QgsDebugMsg( thePoint.toString() ); - QgsDebugMsg( QString::number( x ) + ", " + QString::number( y ) ); - - if ( x < mLayerExtent.xMinimum() || x > mLayerExtent.xMaximum() || y < mLayerExtent.yMinimum() || y > mLayerExtent.yMaximum() ) + if ( !mLayerExtent.contains( thePoint ) ) { // Outside the raster for ( int i = 1; i <= GDALGetRasterCount( mGdalDataset ); i++ ) @@ -1861,6 +1859,9 @@ bool QgsRasterLayer::identify( const QgsPoint& thePoint, QMap& } else { + double x = thePoint.x(); + double y = thePoint.y(); + /* Calculate the row / column where the point falls */ double xres = ( mLayerExtent.xMaximum() - mLayerExtent.xMinimum() ) / mWidth; double yres = ( mLayerExtent.yMaximum() - mLayerExtent.yMinimum() ) / mHeight; diff --git a/src/ui/qgsoptionsbase.ui b/src/ui/qgsoptionsbase.ui index 45fc5f26e1f..2c8a98471a4 100644 --- a/src/ui/qgsoptionsbase.ui +++ b/src/ui/qgsoptionsbase.ui @@ -6,8 +6,8 @@ 0 0 - 617 - 521 + 654 + 546 @@ -503,13 +503,13 @@ - Search radius + Identify 11 - + <b>Note:</b> Specify the search radius as a percentage of the map width @@ -519,7 +519,7 @@ - + Search radius for identifying features and displaying map tips @@ -529,7 +529,7 @@ - + % @@ -545,6 +545,16 @@ + + + + + + + Mode + + + diff --git a/src/ui/qgsprojectpropertiesbase.ui b/src/ui/qgsprojectpropertiesbase.ui index 4ca4ffa531f..0cf18ece526 100644 --- a/src/ui/qgsprojectpropertiesbase.ui +++ b/src/ui/qgsprojectpropertiesbase.ui @@ -1,81 +1,87 @@ - + + QgsProjectPropertiesBase - - + + 0 0 - 536 - 713 + 451 + 515 - + Project Properties - - - - - - + true - + true - - - - + + + + + Qt::Horizontal + + + QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok + + + + + + 0 - - + + General - - - - - Title and colors + + + + + General settings - - - - + + + + Project title - - - + + + Descriptive project name - + Default project title - - - + + + Selection color - + pbnSelectionColour - + - + Qt::Horizontal - + QSizePolicy::Expanding - + 40 20 @@ -83,35 +89,35 @@ - - - + + + 100 0 - + - - - + + + Background color - + pbnCanvasColor - + - + Qt::Horizontal - + 40 20 @@ -119,60 +125,58 @@ - - - + + + 100 0 - + + + + + Save absolute paths + + + - - - + + + Layer units (only used when CRS transformation is disabled) - - - 11 - - - 11 - - - 11 - - + + 11 - - + + Meters - + true - - + + Feet - - + + Decimal degrees @@ -180,60 +184,60 @@ - - - + + + Precision - - - - + + + + Automatically sets the number of decimal places in the mouse position display - + The number of decimal places that are used when displaying the mouse position is automatically set to be enough so that moving the mouse by one pixel gives a change in the position display - + Automatic - + true - - - + + + Sets the number of decimal places to use for the mouse position display - + Sets the number of decimal places to use for the mouse position display - + Manual - - - + + + The number of decimal places for the manual option - + The number of decimal places for the manual option - - - - + + + + 0 0 - + decimal places @@ -241,35 +245,35 @@ - - - - + + + + 0 0 - + Digitizing - - - - + + + + Enable topological editing - - - + + + Avoid intersections of new polygons - - - + + + Snapping options... @@ -277,59 +281,86 @@ - - - - Save absolute paths - - - - - + + Coordinate Reference System (CRS) - - + + 3 - + 11 - + 3 - + 11 - - - + + + Enable 'on the fly' CRS transformation - - + + + + + + + + Identifiable layers + + + + + + true + + + false + + + false + + + true + + + false + + + + Layer + + + + + Type + + + + + Identifiable + + + AlignHCenter|AlignVCenter|AlignCenter + + + - - - - Qt::Horizontal - - - QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::NoButton|QDialogButtonBox::Ok - - - - + QgsColorButton @@ -368,11 +399,11 @@ spinBoxDP setEnabled(bool) - + 229 280 - + 416 286 @@ -384,11 +415,11 @@ textLabel3 setEnabled(bool) - + 240 281 - + 583 290 @@ -400,11 +431,11 @@ spinBoxDP setDisabled(bool) - + 100 290 - + 395 285 @@ -416,11 +447,11 @@ textLabel3 setDisabled(bool) - + 87 284 - + 589 285