diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index d279b1d5307d..acec5667e801 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -50,6 +50,7 @@ SET(QGIS_APP_SRCS qgsmaptooldeletepart.cpp qgsmaptooldeletevertex.cpp qgsmaptooledit.cpp + qgsmaptoolfeatureaction.cpp qgsmaptoolformannotation.cpp qgsmaptoolidentify.cpp qgsmaptoollabel.cpp @@ -189,6 +190,7 @@ SET (QGIS_APP_MOC_HDRS qgsmaptooldeletepart.h qgsmaptooldeletering.h qgsmaptooldeletevertex.h + qgsmaptoolfeatureaction.h qgsmaptoolidentify.h qgsmaptoolmeasureangle.h qgsmaptoolmovefeature.h diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index bb43541e1adf..13369d4cb454 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -205,6 +205,7 @@ #include "qgsmaptooldeletering.h" #include "qgsmaptooldeletepart.h" #include "qgsmaptooldeletevertex.h" +#include "qgsmaptoolfeatureaction.h" #include "qgsmaptoolformannotation.h" #include "qgsmaptoolidentify.h" #include "qgsmaptoolmeasureangle.h" @@ -640,6 +641,7 @@ QgisApp::~QgisApp() delete mMapTools.mZoomOut; delete mMapTools.mPan; delete mMapTools.mIdentify; + delete mMapTools.mFeatureAction; delete mMapTools.mMeasureDist; delete mMapTools.mMeasureArea; delete mMapTools.mMeasureAngle; @@ -814,6 +816,7 @@ void QgisApp::createActions() connect( mActionSelectRadius, SIGNAL( triggered() ), this, SLOT( selectByRadius() ) ); connect( mActionDeselectAll, SIGNAL( triggered() ), this, SLOT( deselectAll() ) ); connect( mActionIdentify, SIGNAL( triggered() ), this, SLOT( identify() ) ); + connect( mActionFeatureAction, SIGNAL( triggered() ), this, SLOT( doFeatureAction() ) ); connect( mActionMeasure, SIGNAL( triggered() ), this, SLOT( measure() ) ); connect( mActionMeasureArea, SIGNAL( triggered() ), this, SLOT( measureArea() ) ); connect( mActionMeasureAngle, SIGNAL( triggered() ), this, SLOT( measureAngle() ) ); @@ -989,6 +992,7 @@ void QgisApp::createActionGroups() mMapToolGroup->addAction( mActionZoomIn ); mMapToolGroup->addAction( mActionZoomOut ); mMapToolGroup->addAction( mActionIdentify ); + mMapToolGroup->addAction( mActionFeatureAction ); mMapToolGroup->addAction( mActionSelect ); mMapToolGroup->addAction( mActionSelectRectangle ); mMapToolGroup->addAction( mActionSelectPolygon ); @@ -1172,6 +1176,18 @@ void QgisApp::createToolBars() selectAction->setObjectName( "ActionSelect" ); connect( bt, SIGNAL( triggered( QAction * ) ), this, SLOT( toolButtonActionTriggered( QAction * ) ) ); + // feature action tool button + + bt = new QToolButton( mAttributesToolBar ); + bt->setPopupMode( QToolButton::MenuButtonPopup ); + bt->setDefaultAction( mActionFeatureAction ); + mFeatureActionMenu = new QMenu( bt ); + connect( mFeatureActionMenu, SIGNAL( triggered( QAction * ) ), this, SLOT( updateDefaultFeatureAction( QAction * ) ) ); + connect( mFeatureActionMenu, SIGNAL( aboutToShow() ), this, SLOT( refreshFeatureActions() ) ); + bt->setMenu( mFeatureActionMenu ); + QAction* featureActionAction = mAttributesToolBar->insertWidget( selectAction, bt ); + featureActionAction->setObjectName( "ActionFeatureAction" ); + // measure tool button bt = new QToolButton( mAttributesToolBar ); @@ -1478,6 +1494,7 @@ void QgisApp::setTheme( QString theThemeName ) mActionZoomToLayer->setIcon( getThemeIcon( "/mActionZoomToLayer.png" ) ); mActionZoomActualSize->setIcon( getThemeIcon( "/mActionZoomActual.png" ) ); mActionIdentify->setIcon( getThemeIcon( "/mActionIdentify.png" ) ); + mActionFeatureAction->setIcon( getThemeIcon( "/mAction.png" ) ); mActionSelect->setIcon( getThemeIcon( "/mActionSelect.png" ) ); mActionSelectRectangle->setIcon( getThemeIcon( "/mActionSelectRectangle.png" ) ); mActionSelectPolygon->setIcon( getThemeIcon( "/mActionSelectPolygon.png" ) ); @@ -1604,6 +1621,8 @@ void QgisApp::createCanvasTools() mMapTools.mPan->setAction( mActionPan ); mMapTools.mIdentify = new QgsMapToolIdentify( mMapCanvas ); mMapTools.mIdentify->setAction( mActionIdentify ); + mMapTools.mFeatureAction = new QgsMapToolFeatureAction( mMapCanvas ); + mMapTools.mFeatureAction->setAction( mActionFeatureAction ); mMapTools.mMeasureDist = new QgsMeasureTool( mMapCanvas, false /* area */ ); mMapTools.mMeasureDist->setAction( mActionMeasure ); mMapTools.mMeasureArea = new QgsMeasureTool( mMapCanvas, true /* area */ ); @@ -3274,6 +3293,48 @@ void QgisApp::identify() mMapCanvas->setMapTool( mMapTools.mIdentify ); } +void QgisApp::doFeatureAction() +{ + mMapCanvas->setMapTool( mMapTools.mFeatureAction ); +} + +void QgisApp::updateDefaultFeatureAction( QAction *action ) +{ + foreach( QAction *a, mFeatureActionMenu->actions() ) + { + a->setChecked( a == action ); + } + + QgsVectorLayer *vlayer = qobject_cast( activeLayer() ); + if ( !vlayer ) + return; + + int index = mFeatureActionMenu->actions().indexOf( action ); + vlayer->actions()->setDefaultAction( index ); + + doFeatureAction(); +} + +void QgisApp::refreshFeatureActions() +{ + mFeatureActionMenu->clear(); + + QgsVectorLayer *vlayer = qobject_cast( activeLayer() ); + if ( !vlayer ) + return; + + QgsAttributeAction *actions = vlayer->actions(); + for ( int i = 0; i < actions->size(); i++ ) + { + QAction *action = mFeatureActionMenu->addAction( actions->at( i ).name() ); + action->setCheckable( true ); + if ( i == actions->defaultAction() ) + { + action->setChecked( true ); + } + } +} + void QgisApp::measure() { mMapCanvas->setMapTool( mMapTools.mMeasureDist ); @@ -6019,7 +6080,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer ) mActionLayerProperties->setEnabled( false ); mActionLayerSubsetString->setEnabled( false ); mActionAddToOverview->setEnabled( false ); - + mActionFeatureAction->setEnabled( false ); mActionAddFeature->setEnabled( false ); mActionMoveFeature->setEnabled( false ); mActionNodeTool->setEnabled( false ); @@ -6077,6 +6138,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer ) mActionLayerSaveAs->setEnabled( true ); mActionLayerSelectionSaveAs->setEnabled( true ); mActionCopyFeatures->setEnabled( layerHasSelection ); + mActionFeatureAction->setEnabled( vlayer->actions()->size() > 0 ); if ( !vlayer->isEditable() && mMapCanvas->mapTool() && mMapCanvas->mapTool()->isEditTool() ) { @@ -6244,6 +6306,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer ) mActionFullHistogramStretch->setEnabled( false ); } mActionLayerSubsetString->setEnabled( false ); + mActionFeatureAction->setEnabled( false ); mActionSelect->setEnabled( false ); mActionSelectRectangle->setEnabled( false ); mActionSelectPolygon->setEnabled( false ); diff --git a/src/app/qgisapp.h b/src/app/qgisapp.h index a1fd0c4f1185..88af51e34bf8 100644 --- a/src/app/qgisapp.h +++ b/src/app/qgisapp.h @@ -246,6 +246,7 @@ class QgisApp : public QMainWindow, private Ui::MainWindow QAction *actionSelectFreehand() { return mActionSelectFreehand; } QAction *actionSelectRadius() { return mActionSelectRadius; } QAction *actionIdentify() { return mActionIdentify; } + QAction *actionFeatureAction() { return mActionFeatureAction; } QAction *actionMeasure() { return mActionMeasure; } QAction *actionMeasureArea() { return mActionMeasureArea; } QAction *actionZoomFullExtent() { return mActionZoomFullExtent; } @@ -757,6 +758,13 @@ class QgisApp : public QMainWindow, private Ui::MainWindow //! Measure angle void measureAngle(); + //! Run the default feature action on the current layer + void doFeatureAction(); + //! Set the default feature action for the current layer + void updateDefaultFeatureAction( QAction *action ); + //! Refresh the list of feature actions of the current layer + void refreshFeatureActions(); + //annotations void addFormAnnotation(); void addTextAnnotation(); @@ -969,6 +977,7 @@ class QgisApp : public QMainWindow, private Ui::MainWindow QgsMapTool* mZoomOut; QgsMapTool* mPan; QgsMapTool* mIdentify; + QgsMapTool* mFeatureAction; QgsMapTool* mMeasureDist; QgsMapTool* mMeasureArea; QgsMapTool* mMeasureAngle; @@ -1025,6 +1034,8 @@ class QgisApp : public QMainWindow, private Ui::MainWindow QLabel * mOnTheFlyProjectionStatusLabel; //! Widget in status bar used to show status of on the fly projection QToolButton * mOnTheFlyProjectionStatusButton; + //! Menu that contains the list of actions of the selected vector layer + QMenu *mFeatureActionMenu; //! Popup menu QMenu * mPopupMenu; //! Top level database menu diff --git a/src/app/qgisappinterface.cpp b/src/app/qgisappinterface.cpp index bb0406f6246d..f36ff8774f15 100644 --- a/src/app/qgisappinterface.cpp +++ b/src/app/qgisappinterface.cpp @@ -421,6 +421,7 @@ QAction *QgisAppInterface::actionSelectPolygon() { return qgis->actionSelectPoly QAction *QgisAppInterface::actionSelectFreehand() { return qgis->actionSelectFreehand(); } QAction *QgisAppInterface::actionSelectRadius() { return qgis->actionSelectRadius(); } QAction *QgisAppInterface::actionIdentify() { return qgis->actionIdentify(); } +QAction *QgisAppInterface::actionFeatureAction() { return qgis->actionFeatureAction(); } QAction *QgisAppInterface::actionMeasure() { return qgis->actionMeasure(); } QAction *QgisAppInterface::actionMeasureArea() { return qgis->actionMeasureArea(); } QAction *QgisAppInterface::actionViewSeparator1() { return 0; } diff --git a/src/app/qgisappinterface.h b/src/app/qgisappinterface.h index d782cb5c1f62..95e86b76a305 100644 --- a/src/app/qgisappinterface.h +++ b/src/app/qgisappinterface.h @@ -269,6 +269,7 @@ class QgisAppInterface : public QgisInterface virtual QAction *actionSelectFreehand(); virtual QAction *actionSelectRadius(); virtual QAction *actionIdentify(); + virtual QAction *actionFeatureAction(); virtual QAction *actionMeasure(); virtual QAction *actionMeasureArea(); virtual QAction *actionViewSeparator1(); diff --git a/src/app/qgsmaptoolfeatureaction.cpp b/src/app/qgsmaptoolfeatureaction.cpp new file mode 100644 index 000000000000..72e3232c1d0d --- /dev/null +++ b/src/app/qgsmaptoolfeatureaction.cpp @@ -0,0 +1,156 @@ +/*************************************************************************** + qgsmaptoolfeatureaction.h - map tool for running feature actions + --------------------- + begin : January 2012 + copyright : (C) 2012 by Giuseppe Sucameli + email : brush.tyler at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsmaptoolfeatureaction.h" + +#include "qgsdistancearea.h" +#include "qgsfeature.h" +#include "qgsfield.h" +#include "qgsgeometry.h" +#include "qgslogger.h" +#include "qgsmapcanvas.h" +#include "qgsmaptopixel.h" +#include "qgsmessageviewer.h" +#include "qgsattributeaction.h" +#include "qgscoordinatereferencesystem.h" +#include "qgsvectordataprovider.h" +#include "qgsvectorlayer.h" +#include "qgsproject.h" +#include "qgsmaplayerregistry.h" +#include "qgisapp.h" + +#include +#include +#include +#include + +QgsMapToolFeatureAction::QgsMapToolFeatureAction( QgsMapCanvas* canvas ) + : QgsMapTool( canvas ) +{ +} + +QgsMapToolFeatureAction::~QgsMapToolFeatureAction() +{ +} + +void QgsMapToolFeatureAction::canvasMoveEvent( QMouseEvent *e ) +{ + Q_UNUSED( e ); +} + +void QgsMapToolFeatureAction::canvasPressEvent( QMouseEvent *e ) +{ + Q_UNUSED( e ); +} + +void QgsMapToolFeatureAction::canvasReleaseEvent( QMouseEvent *e ) +{ + if ( !mCanvas || mCanvas->isDrawing() ) + { + return; + } + + QgsMapLayer *layer = mCanvas->currentLayer(); + + if ( !layer || layer->type() != QgsMapLayer::VectorLayer ) + { + QMessageBox::warning( mCanvas, + tr( "No active vector layer" ), + tr( "To run an action, you must choose a vector layer by clicking on its name in the legend" ) ); + return; + } + + QgsVectorLayer *vlayer = qobject_cast( layer ); + if ( vlayer->actions()->size() == 0 ) + { + QMessageBox::warning( mCanvas, + tr( "No actions available" ), + tr( "The active vector layer has no defined actions" ) ); + return; + } + + if ( !doAction( vlayer, e->x(), e->y() ) ) + QgisApp::instance()->statusBar()->showMessage( tr( "No features at this position found." ) ); +} + +void QgsMapToolFeatureAction::activate() +{ + QgsMapTool::activate(); +} + +void QgsMapToolFeatureAction::deactivate() +{ + QgsMapTool::deactivate(); +} + +bool QgsMapToolFeatureAction::doAction( QgsVectorLayer *layer, int x, int y ) +{ + if ( !layer ) + return false; + + QgsPoint point = mCanvas->getCoordinateTransform()->toMapCoordinates( x, y ); + + // load identify radius from settings + QSettings settings; + double identifyValue = settings.value( "/Map/identifyRadius", QGis::DEFAULT_IDENTIFY_RADIUS ).toDouble(); + QString ellipsoid = settings.value( "/qgis/measure/ellipsoid", "WGS84" ).toString(); + + if ( identifyValue <= 0.0 ) + identifyValue = QGis::DEFAULT_IDENTIFY_RADIUS; + + QgsFeature feat; + + // 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. + try + { + // create the search rectangle + double searchRadius = mCanvas->extent().width() * ( identifyValue / 100.0 ); + + QgsRectangle r; + r.setXMinimum( point.x() - searchRadius ); + r.setXMaximum( point.x() + searchRadius ); + r.setYMinimum( point.y() - searchRadius ); + r.setYMaximum( point.y() + searchRadius ); + + r = toLayerCoordinates( layer, r ); + + layer->select( layer->pendingAllAttributesList(), r, true, true ); + QgsFeature f; + if ( layer->nextFeature( f ) ) + feat = QgsFeature( f ); + else + return false; + } + catch ( QgsCsException & cse ) + { + Q_UNUSED( cse ); + // catch exception for 'invalid' point and proceed with no features found + QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) ); + } + + int action = layer->actions()->defaultAction(); + + // define custom substitutions: layer id and clicked coords + QMap substitutionMap; + substitutionMap.insert( "$layerid", layer->id() ); + point = toLayerCoordinates( layer, point ); + substitutionMap.insert( "$clickx", point.x() ); + substitutionMap.insert( "$clicky", point.y() ); + + layer->actions()->doAction( action, feat, &substitutionMap ); + return true; +} diff --git a/src/app/qgsmaptoolfeatureaction.h b/src/app/qgsmaptoolfeatureaction.h new file mode 100644 index 000000000000..36fc46bda818 --- /dev/null +++ b/src/app/qgsmaptoolfeatureaction.h @@ -0,0 +1,56 @@ +/*************************************************************************** + qgsmaptoolfeatureaction.h - map tool for running feature actions + --------------------- + begin : January 2012 + copyright : (C) 2012 by Giuseppe Sucameli + email : brush.tyler at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSMAPTOOLFEATUREACTION_H +#define QGSMAPTOOLFEATUREACTION_H + +#include "qgis.h" +#include "qgsmaptool.h" + +#include +#include + +class QgsVectorLayer; + +/** + \brief Map tool for running feature actions on the current layer +*/ +class QgsMapToolFeatureAction : public QgsMapTool +{ + Q_OBJECT + + public: + QgsMapToolFeatureAction( QgsMapCanvas* canvas ); + + ~QgsMapToolFeatureAction(); + + //! Overridden mouse move event + virtual void canvasMoveEvent( QMouseEvent * e ); + + //! Overridden mouse press event + virtual void canvasPressEvent( QMouseEvent * e ); + + //! Overridden mouse release event + virtual void canvasReleaseEvent( QMouseEvent * e ); + + virtual void activate(); + + virtual void deactivate(); + + private: + bool doAction( QgsVectorLayer *layer, int x, int y ); +}; + +#endif diff --git a/src/core/qgsattributeaction.h b/src/core/qgsattributeaction.h index 02ad8fa8bf8c..63c6e3dc3e7e 100644 --- a/src/core/qgsattributeaction.h +++ b/src/core/qgsattributeaction.h @@ -178,6 +178,10 @@ class CORE_EXPORT QgsAttributeAction //! @deprecated Initialize QgsPythonRunner instead static void setPythonExecute( void ( * )( const QString & ) ); + //! Whether the action is the default action + int defaultAction() const { return mDefaultAction < 0 || mDefaultAction >= size() ? 0 : mDefaultAction; } + void setDefaultAction( int actionNumber ) { mDefaultAction = actionNumber ; } + private: QList mActions; QgsVectorLayer *mLayer; @@ -185,6 +189,8 @@ class CORE_EXPORT QgsAttributeAction void runAction( const QgsAction &action, void ( *executePython )( const QString & ) = 0 ); + + int mDefaultAction; }; #endif diff --git a/src/ui/qgisapp.ui b/src/ui/qgisapp.ui index 7b7caadc76f6..6693784c85ef 100644 --- a/src/ui/qgisapp.ui +++ b/src/ui/qgisapp.ui @@ -17,7 +17,7 @@ 0 0 1052 - 28 + 25 @@ -1575,6 +1575,21 @@ Add WFS Layer + + + true + + + + :/images/themes/default/mAction.png:/images/themes/default/mAction.png + + + Feature Action + + + Run feature action + +