PageRenderTime 171ms CodeModel.GetById 26ms RepoModel.GetById 22ms app.codeStats 0ms

/src/core/qgsactionmanager.cpp

http://github.com/qgis/Quantum-GIS
C++ | 290 lines | 222 code | 39 blank | 29 comment | 47 complexity | a2783f0331cc8a21249f0560b8ae59d3 MD5 | raw file
Possible License(s): LGPL-2.0, GPL-3.0, GPL-2.0, CC-BY-SA-3.0, MIT, 0BSD, BSD-3-Clause
  1. /***************************************************************************
  2. qgsactionmanager.cpp
  3. A class that stores and controls the management and execution of actions
  4. associated. Actions are defined to be external programs that are run
  5. with user-specified inputs that can depend on the value of layer
  6. attributes.
  7. -------------------
  8. begin : Oct 24 2004
  9. copyright : (C) 2004 by Gavin Macaulay
  10. email : gavin at macaulay dot co dot nz
  11. ***************************************************************************/
  12. /***************************************************************************
  13. * *
  14. * This program is free software; you can redistribute it and/or modify *
  15. * it under the terms of the GNU General Public License as published by *
  16. * the Free Software Foundation; either version 2 of the License, or *
  17. * (at your option) any later version. *
  18. * *
  19. ***************************************************************************/
  20. #include "qgsactionmanager.h"
  21. #include "qgspythonrunner.h"
  22. #include "qgsrunprocess.h"
  23. #include "qgsvectorlayer.h"
  24. #include "qgsproject.h"
  25. #include "qgslogger.h"
  26. #include "qgsexpression.h"
  27. #include "qgsdataprovider.h"
  28. #include "qgsexpressioncontextutils.h"
  29. #include <QList>
  30. #include <QStringList>
  31. #include <QDomElement>
  32. #include <QSettings>
  33. #include <QDesktopServices>
  34. #include <QUrl>
  35. #include <QDir>
  36. #include <QFileInfo>
  37. #include <QRegularExpression>
  38. QUuid QgsActionManager::addAction( QgsAction::ActionType type, const QString &name, const QString &command, bool capture )
  39. {
  40. QgsAction action( type, name, command, capture );
  41. addAction( action );
  42. return action.id();
  43. }
  44. QUuid QgsActionManager::addAction( QgsAction::ActionType type, const QString &name, const QString &command, const QString &icon, bool capture )
  45. {
  46. QgsAction action( type, name, command, icon, capture );
  47. addAction( action );
  48. return action.id();
  49. }
  50. void QgsActionManager::addAction( const QgsAction &action )
  51. {
  52. QgsDebugMsg( "add action " + action.name() );
  53. mActions.append( action );
  54. if ( mLayer && mLayer->dataProvider() && !action.notificationMessage().isEmpty() )
  55. {
  56. mLayer->dataProvider()->setListening( true );
  57. if ( !mOnNotifyConnected )
  58. {
  59. QgsDebugMsg( QStringLiteral( "connecting to notify" ) );
  60. connect( mLayer->dataProvider(), &QgsDataProvider::notify, this, &QgsActionManager::onNotifyRunActions );
  61. mOnNotifyConnected = true;
  62. }
  63. }
  64. }
  65. void QgsActionManager::onNotifyRunActions( const QString &message )
  66. {
  67. for ( const QgsAction &act : qgis::as_const( mActions ) )
  68. {
  69. if ( !act.notificationMessage().isEmpty() && QRegularExpression( act.notificationMessage() ).match( message ).hasMatch() )
  70. {
  71. if ( !act.isValid() || !act.runable() )
  72. continue;
  73. QgsExpressionContext context = createExpressionContext();
  74. Q_ASSERT( mLayer ); // if there is no layer, then where is the notification coming from ?
  75. context << QgsExpressionContextUtils::layerScope( mLayer );
  76. context << QgsExpressionContextUtils::notificationScope( message );
  77. QString expandedAction = QgsExpression::replaceExpressionText( act.command(), &context );
  78. if ( expandedAction.isEmpty() )
  79. continue;
  80. runAction( QgsAction( act.type(), act.name(), expandedAction, act.capture() ) );
  81. }
  82. }
  83. }
  84. void QgsActionManager::removeAction( QUuid actionId )
  85. {
  86. int i = 0;
  87. for ( const QgsAction &action : qgis::as_const( mActions ) )
  88. {
  89. if ( action.id() == actionId )
  90. {
  91. mActions.removeAt( i );
  92. break;
  93. }
  94. ++i;
  95. }
  96. if ( mOnNotifyConnected )
  97. {
  98. bool hasActionOnNotify = false;
  99. for ( const QgsAction &action : qgis::as_const( mActions ) )
  100. hasActionOnNotify |= !action.notificationMessage().isEmpty();
  101. if ( !hasActionOnNotify && mLayer && mLayer->dataProvider() )
  102. {
  103. // note that there is no way of knowing if the provider is listening only because
  104. // this class has hasked it to, so we do not reset the provider listening state here
  105. disconnect( mLayer->dataProvider(), &QgsDataProvider::notify, this, &QgsActionManager::onNotifyRunActions );
  106. mOnNotifyConnected = false;
  107. }
  108. }
  109. }
  110. void QgsActionManager::doAction( QUuid actionId, const QgsFeature &feature, int defaultValueIndex, const QgsExpressionContextScope &scope )
  111. {
  112. QgsExpressionContext context = createExpressionContext();
  113. QgsExpressionContextScope *actionScope = new QgsExpressionContextScope( scope );
  114. actionScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "field_index" ), defaultValueIndex, true ) );
  115. if ( defaultValueIndex >= 0 && defaultValueIndex < feature.fields().size() )
  116. actionScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "field_name" ), feature.fields().at( defaultValueIndex ).name(), true ) );
  117. actionScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "field_value" ), feature.attribute( defaultValueIndex ), true ) );
  118. context << actionScope;
  119. doAction( actionId, feature, context );
  120. }
  121. void QgsActionManager::doAction( QUuid actionId, const QgsFeature &feat, const QgsExpressionContext &context )
  122. {
  123. QgsAction act = action( actionId );
  124. if ( !act.isValid() || !act.runable() )
  125. return;
  126. QgsExpressionContext actionContext( context );
  127. if ( mLayer )
  128. actionContext << QgsExpressionContextUtils::layerScope( mLayer );
  129. actionContext.setFeature( feat );
  130. QString expandedAction = QgsExpression::replaceExpressionText( act.command(), &actionContext );
  131. if ( expandedAction.isEmpty() )
  132. return;
  133. QgsAction newAction( act.type(), act.name(), expandedAction, act.capture() );
  134. runAction( newAction );
  135. }
  136. void QgsActionManager::clearActions()
  137. {
  138. mActions.clear();
  139. if ( mOnNotifyConnected && mLayer && mLayer->dataProvider() )
  140. {
  141. // note that there is no way of knowing if the provider is listening only because
  142. // this class has hasked it to, so we do not reset the provider listening state here
  143. disconnect( mLayer->dataProvider(), &QgsDataProvider::notify, this, &QgsActionManager::onNotifyRunActions );
  144. mOnNotifyConnected = false;
  145. }
  146. }
  147. QList<QgsAction> QgsActionManager::actions( const QString &actionScope ) const
  148. {
  149. if ( actionScope.isNull() )
  150. return mActions;
  151. else
  152. {
  153. QList<QgsAction> actions;
  154. for ( const QgsAction &action : qgis::as_const( mActions ) )
  155. {
  156. if ( action.actionScopes().contains( actionScope ) )
  157. actions.append( action );
  158. }
  159. return actions;
  160. }
  161. }
  162. void QgsActionManager::runAction( const QgsAction &action )
  163. {
  164. if ( action.type() == QgsAction::OpenUrl )
  165. {
  166. QFileInfo finfo( action.command() );
  167. if ( finfo.exists() && finfo.isFile() )
  168. QDesktopServices::openUrl( QUrl::fromLocalFile( action.command() ) );
  169. else
  170. QDesktopServices::openUrl( QUrl( action.command(), QUrl::TolerantMode ) );
  171. }
  172. else if ( action.type() == QgsAction::GenericPython )
  173. {
  174. // TODO: capture output from QgsPythonRunner (like QgsRunProcess does)
  175. QgsPythonRunner::run( action.command() );
  176. }
  177. else
  178. {
  179. // The QgsRunProcess instance created by this static function
  180. // deletes itself when no longer needed.
  181. QgsRunProcess::create( action.command(), action.capture() );
  182. }
  183. }
  184. QgsExpressionContext QgsActionManager::createExpressionContext() const
  185. {
  186. QgsExpressionContext context;
  187. context << QgsExpressionContextUtils::globalScope()
  188. << QgsExpressionContextUtils::projectScope( QgsProject::instance() );
  189. if ( mLayer )
  190. context << QgsExpressionContextUtils::layerScope( mLayer );
  191. return context;
  192. }
  193. bool QgsActionManager::writeXml( QDomNode &layer_node ) const
  194. {
  195. QDomElement aActions = layer_node.ownerDocument().createElement( QStringLiteral( "attributeactions" ) );
  196. for ( QMap<QString, QUuid>::const_iterator defaultAction = mDefaultActions.constBegin(); defaultAction != mDefaultActions.constEnd(); ++ defaultAction )
  197. {
  198. QDomElement defaultActionElement = layer_node.ownerDocument().createElement( QStringLiteral( "defaultAction" ) );
  199. defaultActionElement.setAttribute( QStringLiteral( "key" ), defaultAction.key() );
  200. defaultActionElement.setAttribute( QStringLiteral( "value" ), defaultAction.value().toString() );
  201. aActions.appendChild( defaultActionElement );
  202. }
  203. for ( const QgsAction &action : qgis::as_const( mActions ) )
  204. {
  205. action.writeXml( aActions );
  206. }
  207. layer_node.appendChild( aActions );
  208. return true;
  209. }
  210. bool QgsActionManager::readXml( const QDomNode &layer_node )
  211. {
  212. clearActions();
  213. QDomNode aaNode = layer_node.namedItem( QStringLiteral( "attributeactions" ) );
  214. if ( !aaNode.isNull() )
  215. {
  216. QDomNodeList actionsettings = aaNode.toElement().elementsByTagName( QStringLiteral( "actionsetting" ) );
  217. for ( int i = 0; i < actionsettings.size(); ++i )
  218. {
  219. QgsAction action;
  220. action.readXml( actionsettings.item( i ) );
  221. addAction( action );
  222. }
  223. QDomNodeList defaultActionNodes = aaNode.toElement().elementsByTagName( QStringLiteral( "defaultAction" ) );
  224. for ( int i = 0; i < defaultActionNodes.size(); ++i )
  225. {
  226. QDomElement defaultValueElem = defaultActionNodes.at( i ).toElement();
  227. mDefaultActions.insert( defaultValueElem.attribute( QStringLiteral( "key" ) ), defaultValueElem.attribute( QStringLiteral( "value" ) ) );
  228. }
  229. }
  230. return true;
  231. }
  232. QgsAction QgsActionManager::action( QUuid id )
  233. {
  234. for ( const QgsAction &action : qgis::as_const( mActions ) )
  235. {
  236. if ( action.id() == id )
  237. return action;
  238. }
  239. return QgsAction();
  240. }
  241. void QgsActionManager::setDefaultAction( const QString &actionScope, QUuid actionId )
  242. {
  243. mDefaultActions[ actionScope ] = actionId;
  244. }
  245. QgsAction QgsActionManager::defaultAction( const QString &actionScope )
  246. {
  247. return action( mDefaultActions.value( actionScope ) );
  248. }