Add QgsMapLayerAction and QgsMapLayerActionRegistry, which can be

used as a generic api for registering actions which can apply to
a specific map layer or layer type.

Create a QgsMapLayerAction for setting features as the current
atlas feature for compositions.

This work was kindly sponsored by SIGE (www.sige.ch).
This commit is contained in:
Nyall Dawson 2014-01-19 23:04:24 +11:00
parent fc3094ffbe
commit 85a17c246a
22 changed files with 722 additions and 16 deletions

View File

@ -82,6 +82,9 @@ public:
/** Prepare the atlas map for the given feature. Sets the extent and context variables */
void prepareForFeature( int i );
/** Prepare the atlas map for the given feature. Sets the extent and context variables */
void prepareForFeature( QgsFeature * feat );
/** Returns the current filename. Must be called after prepareForFeature( i ) */
const QString& currentFilename() const;
@ -108,4 +111,11 @@ public:
signals:
/** emitted when one of the parameters changes */
void parameterChanged();
/** emitted when atlas is enabled or disabled */
void toggled( bool );
/**Is emitted when the coverage layer for an atlas changes*/
void coverageLayerChanged( QgsVectorLayer* layer );
};

View File

@ -2,6 +2,7 @@ class QgsAttributeTableModel : QAbstractTableModel
{
%TypeHeaderCode
#include <qgsattributetablemodel.h>
#include <qgsmaplayeractionregistry.h>
%End
public:
enum Role
@ -133,6 +134,11 @@ class QgsAttributeTableModel : QAbstractTableModel
*/
void executeAction( int action, const QModelIndex &idx ) const;
/**
* Execute a QgsMapLayerAction
*/
void executeMapLayerAction( QgsMapLayerAction* action, const QModelIndex &idx ) const;
/**
* Return the feature attributes at given model index
* @return feature attributes at given model index

View File

@ -132,3 +132,17 @@ class QgsAttributeTableAction : QAction
void execute();
void featureForm();
};
class QgsAttributeTableMapLayerAction : QAction
{
%TypeHeaderCode
#include <qgsdualview.h>
%End
public:
QgsAttributeTableMapLayerAction( const QString &name, QgsDualView *dualView, QgsMapLayerAction* action, const QModelIndex &fieldIdx );
public slots:
void execute();
};

View File

@ -42,6 +42,7 @@
%Include qgsmapcanvasitem.sip
%Include qgsmapcanvasmap.sip
%Include qgsmapcanvassnapper.sip
%Include qgsmaplayeractionregistry.sip
%Include qgsmapoverviewcanvas.sip
%Include qgsmaptip.sip
%Include qgsmaptool.sip

View File

@ -0,0 +1,72 @@
class QgsMapLayerAction : QAction
{
%TypeHeaderCode
#include <qgsmaplayeractionregistry.h>
%End
public:
/**Creates a map layer action which can run on any layer*/
QgsMapLayerAction( QString name, QObject *parent );
/**Creates a map layer action which can run only on a specific layer*/
QgsMapLayerAction( QString name, QObject *parent, QgsMapLayer* layer );
/**Creates a map layer action which can run on a specific type of layer*/
QgsMapLayerAction( QString name, QObject *parent, QgsMapLayer::LayerType layerType );
~QgsMapLayerAction();
/** True if action can run using the specified layer */
bool canRunUsingLayer( QgsMapLayer* layer ) const;
/** Triggers the action with the specified layer and feature. This also emits the triggeredForLayer( QgsMapLayer *)
* and triggered() slots */
void triggerForFeature( QgsMapLayer* layer, QgsFeature* feature );
/** Triggers the action with the specified layer. This also emits the triggered() slot. */
void triggerForLayer( QgsMapLayer* layer );
signals:
/** Triggered when action has been run for a specific feature */
void triggeredForFeature( QgsMapLayer* layer, QgsFeature* feature );
/** Triggered when action has been run for a specific layer */
void triggeredForLayer( QgsMapLayer* layer );
};
class QgsMapLayerActionRegistry : QObject
{
%TypeHeaderCode
#include <qgsmaplayeractionregistry.h>
%End
public:
//! Returns the instance pointer, creating the object on the first call
static QgsMapLayerActionRegistry * instance();
~QgsMapLayerActionRegistry();
/**Adds a map layer action to the registry*/
void addMapLayerAction( QgsMapLayerAction * action );
/**Returns the map layer actions which can run on the specified layer*/
QList<QgsMapLayerAction *> mapLayerActions( QgsMapLayer* layer );
/**Removes a map layer action from the registry*/
bool removeMapLayerAction( QgsMapLayerAction *action );
/**Sets the default action for a layer*/
void setDefaultActionForLayer( QgsMapLayer* layer, QgsMapLayerAction* action );
/**Returns the default action for a layer*/
QgsMapLayerAction * defaultActionForLayer( QgsMapLayer* layer );
protected:
//! protected constructor
QgsMapLayerActionRegistry( QObject * parent = 0 );
signals:
/** Triggered when an action is added or removed from the registry */
void changed();
};

View File

@ -54,6 +54,8 @@
#include "qgsmessageviewer.h"
#include "qgscontexthelp.h"
#include "qgscursors.h"
#include "qgsmaplayeractionregistry.h"
#include "qgsgeometry.h"
#include <QCloseEvent>
#include <QCheckBox>
@ -94,6 +96,7 @@ QgsComposer::QgsComposer( QgisApp *qgis, const QString& title )
, mTitle( title )
, mQgis( qgis )
, mUndoView( 0 )
, mAtlasFeatureAction( 0 )
{
setupUi( this );
setWindowTitle( mTitle );
@ -509,6 +512,7 @@ QgsComposer::QgsComposer( QgisApp *qgis, const QString& title )
mActionExportAtlasAsPDF->setEnabled( false );
QgsAtlasComposition* atlasMap = &mComposition->atlasComposition();
connect( atlasMap, SIGNAL( toggled( bool ) ), this, SLOT( toggleAtlasControls( bool ) ) );
connect( atlasMap, SIGNAL( coverageLayerChanged( QgsVectorLayer* ) ), this, SLOT( updateAtlasMapLayerAction( QgsVectorLayer * ) ) );
// Create size grip (needed by Mac OS X for QMainWindow if QStatusBar is not visible)
//should not be needed now that composer has a status bar?
@ -704,6 +708,12 @@ void QgsComposer::setTitle( const QString& title )
{
mWindowAction->setText( title );
}
//update atlas map layer action name if required
if ( mAtlasFeatureAction )
{
mAtlasFeatureAction->setText( QString( tr( "Set as atlas feature for %1" ) ).arg( mTitle ) );
}
}
void QgsComposer::updateStatusCursorPos( QPointF cursorPosition )
@ -820,6 +830,8 @@ void QgsComposer::toggleAtlasControls( bool atlasEnabled )
mActionExportAtlasAsImage->setEnabled( atlasEnabled );
mActionExportAtlasAsSVG->setEnabled( atlasEnabled );
mActionExportAtlasAsPDF->setEnabled( atlasEnabled );
updateAtlasMapLayerAction( atlasEnabled );
}
void QgsComposer::on_mActionAtlasPreview_triggered( bool checked )
@ -2655,6 +2667,8 @@ void QgsComposer::readXML( const QDomElement& composerElem, const QDomDocument&
mActionExportAtlasAsSVG->setEnabled( atlasMap->enabled() );
mActionExportAtlasAsPDF->setEnabled( atlasMap->enabled() );
connect( atlasMap, SIGNAL( toggled( bool ) ), this, SLOT( toggleAtlasControls( bool ) ) );
connect( atlasMap, SIGNAL( coverageLayerChanged( QgsVectorLayer* ) ), this, SLOT( updateAtlasMapLayerAction( QgsVectorLayer * ) ) );
updateAtlasMapLayerAction( atlasMap->enabled() );
setSelectionTool();
}
@ -3083,3 +3097,57 @@ void QgsComposer::writeWorldFile( QString worldFileName, double a, double b, dou
fout << QString::number( c, 'f' ) << "\r\n";
fout << QString::number( f, 'f' ) << "\r\n";
}
void QgsComposer::setAtlasFeature( QgsMapLayer* layer, QgsFeature * feat )
{
//update expression variables
QgsExpression::setSpecialColumn( "$atlasfeatureid", feat->id() );
QgsExpression::setSpecialColumn( "$atlasgeometry", QVariant::fromValue( *( feat->geometry() ) ) );
emit atlasPreviewFeatureChanged();
//check if composition has atlas preview
QgsAtlasComposition& atlas = mComposition->atlasComposition();
if ( ! atlas.enabled() || ! mComposition->atlasMode() == QgsComposition::PreviewAtlas || atlas.coverageLayer() != layer )
{
//either atlas preview isn't enabled, or layer doesn't match
return;
}
//set current preview feature id
atlas.prepareForFeature( feat );
}
void QgsComposer::updateAtlasMapLayerAction( QgsVectorLayer *coverageLayer )
{
if ( mAtlasFeatureAction )
{
delete mAtlasFeatureAction;
mAtlasFeatureAction = 0;
}
if ( coverageLayer )
{
mAtlasFeatureAction = new QgsMapLayerAction( QString( tr( "Set as atlas feature for %1" ) ).arg( mTitle ), this, coverageLayer );
QgsMapLayerActionRegistry::instance()->addMapLayerAction( mAtlasFeatureAction );
connect( mAtlasFeatureAction, SIGNAL( triggeredForFeature( QgsMapLayer*, QgsFeature* ) ), this, SLOT( setAtlasFeature( QgsMapLayer*, QgsFeature* ) ) );
}
}
void QgsComposer::updateAtlasMapLayerAction( bool atlasEnabled )
{
if ( mAtlasFeatureAction )
{
delete mAtlasFeatureAction;
mAtlasFeatureAction = 0;
}
if ( atlasEnabled )
{
QgsAtlasComposition& atlas = mComposition->atlasComposition();
mAtlasFeatureAction = new QgsMapLayerAction( QString( tr( "Set as atlas feature for %1" ) ).arg( mTitle ), this, atlas.coverageLayer() );
QgsMapLayerActionRegistry::instance()->addMapLayerAction( mAtlasFeatureAction );
connect( mAtlasFeatureAction, SIGNAL( triggeredForFeature( QgsMapLayer*, QgsFeature* ) ), this, SLOT( setAtlasFeature( QgsMapLayer*, QgsFeature* ) ) );
}
}

View File

@ -38,6 +38,7 @@ class QgsComposerView;
class QgsComposition;
class QgsMapCanvas;
class QgsAtlasComposition;
class QgsMapLayerAction;
class QGridLayout;
class QDomNode;
@ -475,6 +476,9 @@ class QgsComposer: public QMainWindow, private Ui::QgsComposerBase
//! Exports either either the whole atlas or just the current feature as a PDF, depending on mode
void exportCompositionAsPDF( QgsComposer::OutputMode mode );
//! Updates the "set as atlas feature" map layer action, removing it if atlas is disabled
void updateAtlasMapLayerAction( bool atlasEnabled );
/**Composer title*/
QString mTitle;
@ -551,6 +555,8 @@ class QgsComposer: public QMainWindow, private Ui::QgsComposerBase
//! @note added in 1.9
QMenu* mHelpMenu;
QgsMapLayerAction* mAtlasFeatureAction;
signals:
void printAsRasterChanged( bool state );
@ -579,6 +585,13 @@ class QgsComposer: public QMainWindow, private Ui::QgsComposerBase
//! Toggles the state of the atlas preview and navigation controls
//! @note added in 2.1
void toggleAtlasControls( bool atlasEnabled );
//! Sets the specified feature as the current atlas feature
//! @note added in 2.1
void setAtlasFeature( QgsMapLayer* layer, QgsFeature * feat );
//! Updates the "set as atlas feature" map layer action when atlas coverage layer changes
void updateAtlasMapLayerAction( QgsVectorLayer* coverageLayer );
};
#endif

View File

@ -192,6 +192,7 @@
#include "qgsvectorlayerproperties.h"
#include "qgsmessagelogviewer.h"
#include "qgsdataitem.h"
#include "qgsmaplayeractionregistry.h"
#include "qgssublayersdialog.h"
#include "ogr/qgsopenvectorlayerdialog.h"
@ -609,6 +610,8 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, QWidget * parent,
activateDeactivateLayerRelatedActions( NULL ); // after members were created
connect( QgsMapLayerActionRegistry::instance(), SIGNAL( changed() ), this, SLOT( refreshActionFeatureAction() ) );
// set application's caption
QString caption = tr( "QGIS - %1 ('%2')" ).arg( QGis::QGIS_VERSION ).arg( QGis::QGIS_RELEASE_NAME );
setWindowTitle( caption );
@ -4276,7 +4279,27 @@ void QgisApp::updateDefaultFeatureAction( QAction *action )
mFeatureActionMenu->setActiveAction( action );
int index = mFeatureActionMenu->actions().indexOf( action );
vlayer->actions()->setDefaultAction( index );
if ( vlayer->actions()->size() > 0 && index < vlayer->actions()->size() )
{
vlayer->actions()->setDefaultAction( index );
QgsMapLayerActionRegistry::instance()->setDefaultActionForLayer( vlayer, 0 );
}
else
{
//action is from QgsMapLayerActionRegistry
vlayer->actions()->setDefaultAction( -1 );
QgsMapLayerAction * mapLayerAction = dynamic_cast<QgsMapLayerAction *>( action );
if ( mapLayerAction )
{
QgsMapLayerActionRegistry::instance()->setDefaultActionForLayer( vlayer, mapLayerAction );
}
else
{
QgsMapLayerActionRegistry::instance()->setDefaultActionForLayer( vlayer, 0 );
}
}
doFeatureAction();
}
@ -4298,6 +4321,24 @@ void QgisApp::refreshFeatureActions()
mFeatureActionMenu->setActiveAction( action );
}
}
//add actions registered in QgsMapLayerActionRegistry
QList<QgsMapLayerAction *> registeredActions = QgsMapLayerActionRegistry::instance()->mapLayerActions( vlayer );
if ( actions->size() > 0 && registeredActions.size() > 0 )
{
//add a seperator between user defined and standard actions
mFeatureActionMenu->addSeparator();
}
for ( int i = 0; i < registeredActions.size(); i++ )
{
mFeatureActionMenu->addAction( registeredActions.at( i ) );
if ( registeredActions.at( i ) == QgsMapLayerActionRegistry::instance()->defaultActionForLayer( vlayer ) )
{
mFeatureActionMenu->setActiveAction( registeredActions.at( i ) );
}
}
}
void QgisApp::measure()
@ -8489,7 +8530,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
bool isEditable = vlayer->isEditable();
bool layerHasSelection = vlayer->selectedFeatureCount() > 0;
bool layerHasActions = vlayer->actions()->size() > 0;
bool layerHasActions = vlayer->actions()->size() + QgsMapLayerActionRegistry::instance()->mapLayerActions( vlayer ).size() > 0;
bool canChangeAttributes = dprovider->capabilities() & QgsVectorDataProvider::ChangeAttributeValues;
bool canDeleteFeatures = dprovider->capabilities() & QgsVectorDataProvider::DeleteFeatures;
@ -8733,8 +8774,20 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
}
}
void QgisApp::refreshActionFeatureAction()
{
QgsMapLayer* layer = activeLayer();
if ( layer->type() != QgsMapLayer::VectorLayer )
{
return;
}
QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer *>( layer );
bool layerHasActions = vlayer->actions()->size() + QgsMapLayerActionRegistry::instance()->mapLayerActions( vlayer ).size() > 0;
mActionFeatureAction->setEnabled( layerHasActions );
}
/////////////////////////////////////////////////////////////////
//

View File

@ -622,6 +622,10 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
//! Opens the options dialog
void showOptionsDialog( QWidget *parent = 0, QString currentPage = QString() );
/** Refreshes the state of the layer actions toolbar action
* @note added in 2.1 */
void refreshActionFeatureAction();
protected:
//! Handle state changes (WindowTitleChange)

View File

@ -418,7 +418,10 @@ void QgsIdentifyResultsDialog::addFeature( QgsVectorLayer *vlayer, const QgsFeat
}
}
if ( vlayer->pendingFields().size() > 0 || vlayer->actions()->size() )
//get valid QgsMapLayerActions for this layer
mMapLayerActions = QgsMapLayerActionRegistry::instance()->mapLayerActions( vlayer );
if ( vlayer->pendingFields().size() > 0 || vlayer->actions()->size() || mMapLayerActions.size() )
{
QTreeWidgetItem *actionItem = new QTreeWidgetItem( QStringList() << tr( "(Actions)" ) );
actionItem->setData( 0, Qt::UserRole, "actions" );
@ -445,6 +448,17 @@ void QgsIdentifyResultsDialog::addFeature( QgsVectorLayer *vlayer, const QgsFeat
twi->setData( 0, Qt::UserRole + 1, QVariant::fromValue( i ) );
actionItem->addChild( twi );
}
//add actions from QgsMapLayerActionRegistry
for ( int i = 0; i < mMapLayerActions.size(); i++ )
{
QgsMapLayerAction* action = mMapLayerActions.at( i );
QTreeWidgetItem *twi = new QTreeWidgetItem( QStringList() << "" << action->text() );
twi->setIcon( 0, QgsApplication::getThemeIcon( "/mAction.svg" ) );
twi->setData( 0, Qt::UserRole, "map_layer_action" );
twi->setData( 0, Qt::UserRole + 1, QVariant::fromValue( i ) );
actionItem->addChild( twi );
}
}
highlightFeature( featItem );
@ -674,6 +688,14 @@ void QgsIdentifyResultsDialog::itemClicked( QTreeWidgetItem *item, int column )
{
doAction( item, item->data( 0, Qt::UserRole + 1 ).toInt() );
}
else if ( item->data( 0, Qt::UserRole ).toString() == "map_layer_action" )
{
QgsMapLayerAction* action = mMapLayerActions.at( item->data( 0, Qt::UserRole + 1 ).toInt() );
if ( action )
{
doMapLayerAction( item, action );
}
}
}
// Popup (create if necessary) a context menu that contains a list of
@ -779,6 +801,27 @@ void QgsIdentifyResultsDialog::contextMenuEvent( QContextMenuEvent* event )
}
}
if ( featItem && vlayer )
{
//get valid QgsMapLayerActions for this layer
QList< QgsMapLayerAction* > registeredActions = QgsMapLayerActionRegistry::instance()->mapLayerActions( vlayer );
if ( registeredActions.size() > 0 )
{
//add a seperator between user defined and standard actions
mActionPopup->addSeparator();
int featIdx = featItem->data( 0, Qt::UserRole + 1 ).toInt();
QList<QgsMapLayerAction*>::iterator actionIt;
for ( actionIt = registeredActions.begin(); actionIt != registeredActions.end(); ++actionIt )
{
QgsIdentifyResultsDialogMapLayerAction *a = new QgsIdentifyResultsDialogMapLayerAction(( *actionIt )->text(), this, ( *actionIt ), vlayer, &( mFeatures[ featIdx ] ) );
mActionPopup->addAction( QgsApplication::getThemeIcon( "/mAction.svg" ), ( *actionIt )->text(), a, SLOT( execute() ) );
}
}
}
mActionPopup->popup( event->globalPos() );
}
@ -884,6 +927,20 @@ void QgsIdentifyResultsDialog::doAction( QTreeWidgetItem *item, int action )
layer->actions()->doAction( action, mFeatures[ featIdx ], idx );
}
void QgsIdentifyResultsDialog::doMapLayerAction( QTreeWidgetItem *item, QgsMapLayerAction* action )
{
QTreeWidgetItem *featItem = featureItem( item );
if ( !featItem )
return;
QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( featItem->parent()->data( 0, Qt::UserRole ).value<QObject *>() );
if ( !layer )
return;
int featIdx = featItem->data( 0, Qt::UserRole + 1 ).toInt();
action->triggerForFeature( layer, &mFeatures[ featIdx ] );
}
QTreeWidgetItem *QgsIdentifyResultsDialog::featureItem( QTreeWidgetItem *item )
{
if ( !item )
@ -1496,3 +1553,12 @@ void QgsIdentifyResultsDialog::formatChanged( int index )
}
}
}
/*
* QgsIdentifyResultsDialogMapLayerAction
*/
void QgsIdentifyResultsDialogMapLayerAction::execute()
{
mAction->triggerForFeature( mLayer, mFeature );
}

View File

@ -26,6 +26,7 @@
#include "qgsfield.h"
#include "qgsmaptoolidentify.h"
#include "qgscoordinatereferencesystem.h"
#include "qgsmaplayeractionregistry.h"
#include <QWidget>
#include <QList>
@ -199,6 +200,8 @@ class APP_EXPORT QgsIdentifyResultsDialog: public QDialog, private Ui::QgsIdenti
QgsMapCanvas *mCanvas;
QList<QgsFeature> mFeatures;
QList< QgsMapLayerAction* > mMapLayerActions;
QgsMapLayer *layer( QTreeWidgetItem *item );
QgsVectorLayer *vectorLayer( QTreeWidgetItem *item );
QgsRasterLayer *rasterLayer( QTreeWidgetItem *item );
@ -218,7 +221,27 @@ class APP_EXPORT QgsIdentifyResultsDialog: public QDialog, private Ui::QgsIdenti
void doAction( QTreeWidgetItem *item, int action );
void doMapLayerAction( QTreeWidgetItem *item, QgsMapLayerAction* action );
QDockWidget *mDock;
};
class QgsIdentifyResultsDialogMapLayerAction : public QAction
{
Q_OBJECT
public:
QgsIdentifyResultsDialogMapLayerAction( const QString &name, QObject *parent, QgsMapLayerAction* action, QgsMapLayer* layer, QgsFeature * f ) :
QAction( name, parent ), mAction( action ), mFeature( f ), mLayer( layer )
{}
public slots:
void execute();
private:
QgsMapLayerAction* mAction;
QgsFeature* mFeature;
QgsMapLayer* mLayer;
};
#endif

View File

@ -28,6 +28,7 @@
#include "qgsvectorlayer.h"
#include "qgsproject.h"
#include "qgsmaplayerregistry.h"
#include "qgsmaplayeractionregistry.h"
#include "qgisapp.h"
#include <QSettings>
@ -78,7 +79,7 @@ void QgsMapToolFeatureAction::canvasReleaseEvent( QMouseEvent *e )
}
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
if ( vlayer->actions()->size() == 0 )
if ( vlayer->actions()->size() == 0 && QgsMapLayerActionRegistry::instance()->mapLayerActions( vlayer ).size() == 0 )
{
QMessageBox::warning( mCanvas,
tr( "No actions available" ),
@ -149,16 +150,26 @@ bool QgsMapToolFeatureAction::doAction( QgsVectorLayer *layer, int x, int y )
foreach ( QgsFeature feat, featList )
{
int actionIdx = layer->actions()->defaultAction();
if ( layer->actions()->defaultAction() >= 0 )
{
// define custom substitutions: layer id and clicked coords
QMap<QString, QVariant> substitutionMap;
substitutionMap.insert( "$layerid", layer->id() );
point = toLayerCoordinates( layer, point );
substitutionMap.insert( "$clickx", point.x() );
substitutionMap.insert( "$clicky", point.y() );
// define custom substitutions: layer id and clicked coords
QMap<QString, QVariant> substitutionMap;
substitutionMap.insert( "$layerid", layer->id() );
point = toLayerCoordinates( layer, point );
substitutionMap.insert( "$clickx", point.x() );
substitutionMap.insert( "$clicky", point.y() );
layer->actions()->doAction( actionIdx, feat, &substitutionMap );
int actionIdx = layer->actions()->defaultAction();
layer->actions()->doAction( actionIdx, feat, &substitutionMap );
}
else
{
QgsMapLayerAction* mapLayerAction = QgsMapLayerActionRegistry::instance()->defaultActionForLayer( layer );
if ( mapLayerAction )
{
mapLayerAction->triggerForFeature( layer, &feat );
}
}
}
return true;

View File

@ -372,6 +372,12 @@ void QgsAtlasComposition::lastFeature()
prepareForFeature( mCurrentFeatureNo );
}
void QgsAtlasComposition::prepareForFeature( QgsFeature * feat )
{
int featureI = mFeatureIds.indexOf( feat->id() );
prepareForFeature( featureI );
}
void QgsAtlasComposition::prepareForFeature( int featureI )
{
if ( !mCoverageLayer )

View File

@ -111,6 +111,9 @@ class CORE_EXPORT QgsAtlasComposition : public QObject
/** Prepare the atlas map for the given feature. Sets the extent and context variables */
void prepareForFeature( int i );
/** Prepare the atlas map for the given feature. Sets the extent and context variables */
void prepareForFeature( QgsFeature * feat );
/** Returns the current filename. Must be called after prepareForFeature( i ) */
const QString& currentFilename() const;
@ -174,6 +177,7 @@ class CORE_EXPORT QgsAtlasComposition : public QObject
public:
typedef QMap< QgsFeatureId, QVariant > SorterKeys;
private:
// value of field that is used for ordering of features
SorterKeys mFeatureKeys;

View File

@ -97,7 +97,7 @@ class CORE_EXPORT QgsAttributeAction
{
public:
//! Constructor
QgsAttributeAction( QgsVectorLayer *layer ) : mLayer( layer ) {}
QgsAttributeAction( QgsVectorLayer *layer ) : mLayer( layer ), mDefaultAction( -1 ) {}
//! Destructor
virtual ~QgsAttributeAction() {}
@ -175,7 +175,7 @@ class CORE_EXPORT QgsAttributeAction
static void setPythonExecute( void ( * )( const QString & ) );
//! Whether the action is the default action
int defaultAction() const { return mDefaultAction < 0 || mDefaultAction >= size() ? 0 : mDefaultAction; }
int defaultAction() const { return mDefaultAction < 0 || mDefaultAction >= size() ? -1 : mDefaultAction; }
void setDefaultAction( int actionNumber ) { mDefaultAction = actionNumber ; }
private:

View File

@ -101,6 +101,7 @@ qgsmapcanvas.cpp
qgsmapcanvasitem.cpp
qgsmapcanvasmap.cpp
qgsmapcanvassnapper.cpp
qgsmaplayeractionregistry.cpp
qgsmapoverviewcanvas.cpp
qgsmaptip.cpp
qgsmaptool.cpp
@ -236,6 +237,7 @@ qgslonglongvalidator.h
qgsludialog.h
qgsmanageconnectionsdialog.h
qgsmapcanvas.h
qgsmaplayeractionregistry.h
qgsmapoverviewcanvas.h
qgsmaptoolemitpoint.h
qgsmaptoolidentify.h

View File

@ -25,6 +25,7 @@
#include "qgsrendererv2.h"
#include "qgsmaplayerregistry.h"
#include "qgsexpression.h"
#include "qgsmaplayeractionregistry.h"
#include <QtGui>
#include <QVariant>
@ -629,6 +630,12 @@ void QgsAttributeTableModel::executeAction( int action, const QModelIndex &idx )
layer()->actions()->doAction( action, f, fieldIdx( idx.column() ) );
}
void QgsAttributeTableModel::executeMapLayerAction( QgsMapLayerAction* action, const QModelIndex &idx ) const
{
QgsFeature f = feature( idx );
action->triggerForFeature( layer(), &f );
}
QgsFeature QgsAttributeTableModel::feature( const QModelIndex &idx ) const
{
QgsFeature f;

View File

@ -28,6 +28,7 @@
#include "qgsvectorlayercache.h"
class QgsMapCanvas;
class QgsMapLayerAction;
/**
* A model backed by a {@link QgsVectorLayerCache} which is able to provide
@ -173,6 +174,11 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel
*/
void executeAction( int action, const QModelIndex &idx ) const;
/**
* Execute a QgsMapLayerAction
*/
void executeMapLayerAction( QgsMapLayerAction* action, const QModelIndex &idx ) const;
/**
* Return the feature attributes at given model index
* @return feature attributes at given model index

View File

@ -26,6 +26,7 @@
#include "qgsmapcanvas.h"
#include "qgsvectordataprovider.h"
#include "qgsvectorlayercache.h"
#include "qgsmaplayeractionregistry.h"
#include <QDialog>
#include <QMenu>
@ -418,10 +419,11 @@ void QgsDualView::viewWillShowContextMenu( QMenu* menu, QModelIndex atIndex )
{
QModelIndex sourceIndex = mFilterModel->mapToSource( atIndex );
//add user-defined actions to context menu
if ( mLayerCache->layer()->actions()->size() != 0 )
{
QAction *a = menu->addAction( tr( "Run action" ) );
QAction *a = menu->addAction( tr( "Run layer action" ) );
a->setEnabled( false );
for ( int i = 0; i < mLayerCache->layer()->actions()->size(); i++ )
@ -436,6 +438,22 @@ void QgsDualView::viewWillShowContextMenu( QMenu* menu, QModelIndex atIndex )
}
}
//add actions from QgsMapLayerActionRegistry to context menu
QList<QgsMapLayerAction *> registeredActions = QgsMapLayerActionRegistry::instance()->mapLayerActions( mLayerCache->layer() );
if ( registeredActions.size() > 0 )
{
//add a seperator between user defined and standard actions
menu->addSeparator();
QList<QgsMapLayerAction*>::iterator actionIt;
for ( actionIt = registeredActions.begin(); actionIt != registeredActions.end(); ++actionIt )
{
QgsAttributeTableMapLayerAction *a = new QgsAttributeTableMapLayerAction(( *actionIt )->text(), this, ( *actionIt ), sourceIndex );
menu->addAction(( *actionIt )->text(), a, SLOT( execute() ) );
}
}
menu->addSeparator();
QgsAttributeTableAction *a = new QgsAttributeTableAction( tr( "Open form" ), this, -1, sourceIndex );
menu->addAction( tr( "Open form" ), a, SLOT( featureForm() ) );
}
@ -612,3 +630,12 @@ void QgsAttributeTableAction::featureForm()
mDualView->setCurrentEditSelection( editedIds );
mDualView->setView( QgsDualView::AttributeEditor );
}
/*
* QgsAttributeTableMapLayerAction
*/
void QgsAttributeTableMapLayerAction::execute()
{
mDualView->masterModel()->executeMapLayerAction( mAction, mFieldIdx );
}

View File

@ -28,6 +28,7 @@
class QgsAttributeDialog;
class QgsFeatureRequest;
class QSignalMapper;
class QgsMapLayerAction;
/**
* This widget is used to show the attributes of a set of features of a {@link QgsVectorLayer}.
@ -277,4 +278,22 @@ class GUI_EXPORT QgsAttributeTableAction : public QAction
QModelIndex mFieldIdx;
};
class GUI_EXPORT QgsAttributeTableMapLayerAction : public QAction
{
Q_OBJECT
public:
QgsAttributeTableMapLayerAction( const QString &name, QgsDualView *dualView, QgsMapLayerAction* action, const QModelIndex &fieldIdx ) :
QAction( name, dualView ), mDualView( dualView ), mAction( action ), mFieldIdx( fieldIdx )
{}
public slots:
void execute();
private:
QgsDualView* mDualView;
QgsMapLayerAction* mAction;
QModelIndex mFieldIdx;
};
#endif // QGSFEATURELIST_H

View File

@ -0,0 +1,172 @@
/***************************************************************************
qgsmaplayeractionregistry.cpp
-----------------------------
begin : January 2014
copyright : (C) 2014 by Nyall Dawson
email : nyall dot dawson 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 "qgsmaplayeractionregistry.h"
QgsMapLayerAction::QgsMapLayerAction( QString name, QObject* parent ) : QAction( name, parent ),
mSingleLayer( false ),
mActionLayer( 0 ),
mSpecificLayerType( false )
{
}
/**Creates a map layer action which can run only on a specific layer*/
QgsMapLayerAction::QgsMapLayerAction( QString name, QObject* parent, QgsMapLayer* layer ) : QAction( name, parent ),
mSingleLayer( true ),
mActionLayer( layer ),
mSpecificLayerType( false )
{
}
/**Creates a map layer action which can run on a specific type of layer*/
QgsMapLayerAction::QgsMapLayerAction( QString name, QObject* parent, QgsMapLayer::LayerType layerType ) : QAction( name, parent ),
mSingleLayer( false ),
mActionLayer( 0 ),
mSpecificLayerType( true ),
mLayerType( layerType )
{
}
QgsMapLayerAction::~QgsMapLayerAction()
{
//remove action from registry
QgsMapLayerActionRegistry::instance()->removeMapLayerAction( this );
}
bool QgsMapLayerAction::canRunUsingLayer( QgsMapLayer* layer ) const
{
//check layer details
if ( !mSingleLayer && !mSpecificLayerType )
{
//action is not a single layer of specific layer type action,
//so return true
return true;
}
if ( mSingleLayer && layer == mActionLayer )
{
//action is a single layer type and layer matches
return true;
}
else if ( mSpecificLayerType && layer->type() == mLayerType )
{
//action is for a layer type and layer type matches
return true;
}
return false;
}
void QgsMapLayerAction::triggerForFeature( QgsMapLayer* layer, QgsFeature* feature )
{
emit triggeredForFeature( layer, feature );
//also trigger this action for the specified layer
triggerForLayer( layer );
}
void QgsMapLayerAction::triggerForLayer( QgsMapLayer* layer )
{
emit triggeredForLayer( layer );
//also emit triggered signal
emit triggered();
}
//
// Static calls to enforce singleton behaviour
//
QgsMapLayerActionRegistry *QgsMapLayerActionRegistry::mInstance = 0;
QgsMapLayerActionRegistry *QgsMapLayerActionRegistry::instance()
{
if ( mInstance == 0 )
{
mInstance = new QgsMapLayerActionRegistry();
}
return mInstance;
}
//
// Main class begins now...
//
QgsMapLayerActionRegistry::QgsMapLayerActionRegistry( QObject *parent ) : QObject( parent )
{
// constructor does nothing
}
QgsMapLayerActionRegistry::~QgsMapLayerActionRegistry()
{
}
void QgsMapLayerActionRegistry::addMapLayerAction( QgsMapLayerAction * action )
{
mMapLayerActionList.append( action );
emit changed();
}
QList< QgsMapLayerAction* > QgsMapLayerActionRegistry::mapLayerActions( QgsMapLayer* layer )
{
QList< QgsMapLayerAction* > validActions;
QList<QgsMapLayerAction*>::iterator actionIt;
for ( actionIt = mMapLayerActionList.begin(); actionIt != mMapLayerActionList.end(); ++actionIt )
{
if (( *actionIt )->canRunUsingLayer( layer ) )
{
validActions.append(( *actionIt ) );
}
}
return validActions;
}
bool QgsMapLayerActionRegistry::removeMapLayerAction( QgsMapLayerAction* action )
{
if ( mMapLayerActionList.indexOf( action ) != -1 )
{
mMapLayerActionList.removeAll( action );
//also remove this action from the default layer action map
QMap<QgsMapLayer*, QgsMapLayerAction*>::iterator defaultIt;
for ( defaultIt = mDefaultLayerActionMap.begin(); defaultIt != mDefaultLayerActionMap.end(); ++defaultIt )
{
if ( defaultIt.value() == action )
{
defaultIt.value() = 0;
}
}
emit changed();
return true;
}
//not found
return false;
}
void QgsMapLayerActionRegistry::setDefaultActionForLayer( QgsMapLayer* layer, QgsMapLayerAction* action )
{
mDefaultLayerActionMap[ layer ] = action;
}
QgsMapLayerAction * QgsMapLayerActionRegistry::defaultActionForLayer( QgsMapLayer* layer )
{
if ( !mDefaultLayerActionMap.contains( layer ) )
{
return 0;
}
return mDefaultLayerActionMap[ layer ];
}

View File

@ -0,0 +1,122 @@
/***************************************************************************
qgsmaplayeractionregistry.h
---------------------------
begin : January 2014
copyright : (C) 2014 by Nyall Dawson
email : nyall dot dawson 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 QGSMAPLAYERACTIONREGISTRY_H
#define QGSMAPLAYERACTIONREGISTRY_H
#include <QObject>
#include <QList>
#include <QMap>
#include <QAction>
#include "qgsmaplayer.h"
class QgsFeature;
/**
* An action which can run on map layers
*/
class GUI_EXPORT QgsMapLayerAction : public QAction
{
Q_OBJECT
public:
/**Creates a map layer action which can run on any layer*/
QgsMapLayerAction( QString name, QObject *parent );
/**Creates a map layer action which can run only on a specific layer*/
QgsMapLayerAction( QString name, QObject *parent, QgsMapLayer* layer );
/**Creates a map layer action which can run on a specific type of layer*/
QgsMapLayerAction( QString name, QObject *parent, QgsMapLayer::LayerType layerType );
~QgsMapLayerAction();
/** True if action can run using the specified layer */
bool canRunUsingLayer( QgsMapLayer* layer ) const;
/** Triggers the action with the specified layer and feature. This also emits the triggeredForLayer( QgsMapLayer *)
* and triggered() slots */
void triggerForFeature( QgsMapLayer* layer, QgsFeature* feature );
/** Triggers the action with the specified layer. This also emits the triggered() slot. */
void triggerForLayer( QgsMapLayer* layer );
signals:
/** Triggered when action has been run for a specific feature */
void triggeredForFeature( QgsMapLayer* layer, QgsFeature* feature );
/** Triggered when action has been run for a specific layer */
void triggeredForLayer( QgsMapLayer* layer );
private:
//true if action is only valid for a single layer
bool mSingleLayer;
//layer if action is only valid for a single layer
QgsMapLayer* mActionLayer;
//true if action is only valid for a specific layer type
bool mSpecificLayerType;
//layer type if action is only valid for a specific layer type
QgsMapLayer::LayerType mLayerType;
};
/**
* This class tracks map layer actions
*/
class GUI_EXPORT QgsMapLayerActionRegistry : public QObject
{
Q_OBJECT
public:
//! Returns the instance pointer, creating the object on the first call
static QgsMapLayerActionRegistry * instance();
~QgsMapLayerActionRegistry();
/**Adds a map layer action to the registry*/
void addMapLayerAction( QgsMapLayerAction * action );
/**Returns the map layer actions which can run on the specified layer*/
QList<QgsMapLayerAction *> mapLayerActions( QgsMapLayer* layer );
/**Removes a map layer action from the registry*/
bool removeMapLayerAction( QgsMapLayerAction *action );
/**Sets the default action for a layer*/
void setDefaultActionForLayer( QgsMapLayer* layer, QgsMapLayerAction* action );
/**Returns the default action for a layer*/
QgsMapLayerAction * defaultActionForLayer( QgsMapLayer* layer );
protected:
//! protected constructor
QgsMapLayerActionRegistry( QObject * parent = 0 );
signals:
/** Triggered when an action is added or removed from the registry */
void changed();
private:
static QgsMapLayerActionRegistry *mInstance;
QList< QgsMapLayerAction* > mMapLayerActionList;
QMap< QgsMapLayer*, QgsMapLayerAction* > mDefaultLayerActionMap;
};
#endif // QGSMAPLAYERACTIONREGISTRY_H