16 #include <QMouseEvent>
36 , mAllowMultipleReturn( true )
37 , mExecWithSingleResult( false )
38 , mShowFeatureActions( false )
39 , mResultsIfExternalAction( false )
40 , mMaxLayerDisplay( 10 )
41 , mMaxFeatureDisplay( 10 )
42 , mDefaultActionName( tr(
"Identify" ) )
53 QList<QgsMapToolIdentify::IdentifyResult> results;
54 const QMap< QString, QString > derivedAttributes;
57 const double x = mapPoint.
x();
58 const double y = mapPoint.
y();
61 const QList<QgsMapLayer *> layers = canvas->
layers(
true );
66 QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layer );
68 bool typeIsSelectable =
false;
73 typeIsSelectable =
true;
77 if ( typeIsSelectable )
89 QgsDebugMsg( QStringLiteral(
"Could not transform geometry to layer CRS" ) );
93 .setFilterRect( rect )
111 QgsDebugMsg( QStringLiteral(
"invalid value for number of layers displayed." ) );
121 QgsDebugMsg( QStringLiteral(
"invalid value for number of layers displayed." ) );
127 QList<QgsMapToolIdentify::IdentifyResult>
QgsIdentifyMenu::exec(
const QList<QgsMapToolIdentify::IdentifyResult> &idResults, QPoint pos )
130 mLayerIdResults.clear();
132 QList<QgsMapToolIdentify::IdentifyResult> returnResults = QList<QgsMapToolIdentify::IdentifyResult>();
134 if ( idResults.isEmpty() )
136 return returnResults;
138 if ( idResults.count() == 1 && !mExecWithSingleResult )
140 returnResults << idResults[0];
141 return returnResults;
145 const auto constIdResults = idResults;
149 if ( mLayerIdResults.contains( layer ) )
151 mLayerIdResults[layer].append( result );
155 mLayerIdResults.insert( layer, QList<QgsMapToolIdentify::IdentifyResult>() << result );
160 const bool singleLayer = mLayerIdResults.count() == 1;
162 QMapIterator< QgsMapLayer *, QList<QgsMapToolIdentify::IdentifyResult> > it( mLayerIdResults );
163 while ( it.hasNext() )
165 if ( mMaxLayerDisplay != 0 && count > mMaxLayerDisplay )
170 switch ( layer->
type() )
174 addRasterLayer( layer );
182 addVectorLayer( vl, it.value(), singleLayer );
200 if ( !singleLayer && mAllowMultipleReturn && idResults.count() > 1 )
203 QAction *allAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionIdentify.svg" ) ), tr(
"%1 All (%2)" ).arg( mDefaultActionName ).arg( idResults.count() ),
this );
204 allAction->setData( QVariant::fromValue<ActionData>(
ActionData(
nullptr ) ) );
205 connect( allAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
206 addAction( allAction );
210 QAction *selectedAction = QMenu::exec( pos );
212 returnResults = results( selectedAction, externalAction );
217 qDeleteAll( findChildren<QgsActionMenu *>() );
219 if ( externalAction && !mResultsIfExternalAction )
221 return QList<QgsMapToolIdentify::IdentifyResult>();
225 return returnResults;
232 QMenu::closeEvent( e );
235 void QgsIdentifyMenu::addRasterLayer(
QgsMapLayer *layer )
237 QAction *layerAction =
nullptr;
238 QMenu *layerMenu =
nullptr;
240 QList<QgsMapLayerAction *> separators = QList<QgsMapLayerAction *>();
242 const int nCustomActions = layerActions.count();
243 if ( nCustomActions )
245 separators.append( layerActions[0] );
247 if ( mShowFeatureActions )
250 if ( layerActions.count() > nCustomActions )
252 separators.append( layerActions[nCustomActions] );
257 if ( layerActions.isEmpty() )
259 layerAction =
new QAction( layer->
name(),
this );
263 layerMenu =
new QMenu( layer->
name(),
this );
264 layerAction = layerMenu->menuAction();
269 layerAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
270 connect( layerAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
271 addAction( layerAction );
278 QAction *identifyFeatureAction =
new QAction( mDefaultActionName, layerMenu );
279 connect( identifyFeatureAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
280 identifyFeatureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
281 layerMenu->addAction( identifyFeatureAction );
284 const auto constLayerActions = layerActions;
287 QAction *action =
new QAction( mapLayerAction->icon(), mapLayerAction->text(), layerMenu );
288 action->setData( QVariant::fromValue<ActionData>( ActionData( layer,
true ) ) );
289 connect( action, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
290 connect( action, &QAction::triggered,
this, &QgsIdentifyMenu::triggerMapLayerAction );
291 layerMenu->addAction( action );
292 if ( separators.contains( mapLayerAction ) )
294 layerMenu->insertSeparator( action );
299 void QgsIdentifyMenu::addVectorLayer(
QgsVectorLayer *layer,
const QList<QgsMapToolIdentify::IdentifyResult> &results,
bool singleLayer )
301 QAction *layerAction =
nullptr;
302 QMenu *layerMenu =
nullptr;
308 QList<QgsMapLayerAction *> separators = QList<QgsMapLayerAction *>();
309 QList<QgsMapLayerAction *> layerActions = mCustomActionRegistry.mapLayerActions( layer, targets );
310 const int nCustomActions = layerActions.count();
311 if ( nCustomActions )
313 separators << layerActions[0];
315 if ( mShowFeatureActions )
319 if ( layerActions.count() > nCustomActions )
321 separators << layerActions[nCustomActions];
330 bool createMenu = results.count() > 1 || !layerActions.isEmpty();
337 if ( !createMenu && mShowFeatureActions )
341 createMenu = !featureActionMenu->actions().isEmpty();
342 delete featureActionMenu;
348 exp.prepare( &context );
349 context.setFeature( results[0].mFeature );
354 QString featureTitle = exp.evaluate( &context ).toString();
355 if ( featureTitle.isEmpty() )
356 featureTitle = QString::number( results[0].mFeature.id() );
357 layerAction =
new QAction( QStringLiteral(
"%1 (%2)" ).arg( layer->
name(), featureTitle ),
this );
369 if ( results.count() > 1 )
371 layerMenu =
new QMenu( layer->
name(),
this );
376 QString featureTitle = exp.evaluate( &context ).toString();
377 if ( featureTitle.isEmpty() )
378 featureTitle = QString::number( results[0].mFeature.id() );
379 layerMenu =
new QMenu( QStringLiteral(
"%1 (%2)" ).arg( layer->
name(), featureTitle ),
this );
381 layerAction = layerMenu->menuAction();
391 layerAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
392 connect( layerAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
393 addAction( layerAction );
402 const auto constResults = results;
405 if ( mMaxFeatureDisplay != 0 && count > mMaxFeatureDisplay )
409 QAction *featureAction =
nullptr;
410 QMenu *featureMenu =
nullptr;
414 if ( mShowFeatureActions )
416 featureActionMenu =
new QgsActionMenu( layer, result.mFeature, QStringLiteral(
"Feature" ), layerMenu );
422 context.setFeature( result.mFeature );
423 QString featureTitle = exp.evaluate( &context ).toString();
424 if ( featureTitle.isEmpty() )
425 featureTitle = QString::number( result.mFeature.id() );
427 if ( customFeatureActions.isEmpty() && ( !featureActionMenu || featureActionMenu->actions().isEmpty() ) )
429 featureAction =
new QAction( featureTitle, layerMenu );
431 featureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
432 connect( featureAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
433 layerMenu->addAction( featureAction );
435 else if ( results.count() == 1 )
440 featureMenu = layerMenu;
444 featureMenu =
new QMenu( featureTitle, layerMenu );
447 featureAction = featureMenu->menuAction();
449 featureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
450 connect( featureAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
451 layerMenu->addAction( featureAction );
459 QAction *identifyFeatureAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionIdentify.svg" ) ), mDefaultActionName, featureMenu );
460 connect( identifyFeatureAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
461 identifyFeatureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
462 featureMenu->addAction( identifyFeatureAction );
463 featureMenu->addSeparator();
466 const auto constCustomFeatureActions = customFeatureActions;
469 QAction *action =
new QAction( mapLayerAction->icon(), mapLayerAction->text(), featureMenu );
470 action->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id(), mapLayerAction ) ) );
471 connect( action, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
472 connect( action, &QAction::triggered,
this, &QgsIdentifyMenu::triggerMapLayerAction );
473 featureMenu->addAction( action );
476 if ( featureActionMenu )
478 const auto constActions = featureActionMenu->actions();
479 for ( QAction *action : constActions )
481 connect( action, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
482 featureMenu->addAction( action );
490 if ( mAllowMultipleReturn && results.count() > 1 )
492 layerMenu->addSeparator();
493 QAction *allAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionIdentify.svg" ) ), tr(
"%1 All (%2)" ).arg( mDefaultActionName ).arg( results.count() ), layerMenu );
494 allAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
495 connect( allAction, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
496 layerMenu->addAction( allAction );
500 const auto constLayerActions = layerActions;
503 QString title = mapLayerAction->text();
505 title.append( QStringLiteral(
" (%1)" ).arg( results.count() ) );
506 QAction *action =
new QAction( mapLayerAction->icon(), title, layerMenu );
507 action->setData( QVariant::fromValue<ActionData>( ActionData( layer, mapLayerAction ) ) );
508 connect( action, &QAction::hovered,
this, &QgsIdentifyMenu::handleMenuHover );
509 connect( action, &QAction::triggered,
this, &QgsIdentifyMenu::triggerMapLayerAction );
510 layerMenu->addAction( action );
511 if ( separators.contains( mapLayerAction ) )
513 layerMenu->insertSeparator( action );
518 void QgsIdentifyMenu::triggerMapLayerAction()
520 QAction *action = qobject_cast<QAction *>( sender() );
523 const QVariant varData = action->data();
524 if ( !varData.isValid() || !varData.canConvert<ActionData>() )
527 ActionData actData = action->data().value<ActionData>();
529 if ( actData.mIsValid && actData.mMapLayerAction )
534 actData.mMapLayerAction->triggerForLayer( actData.mLayer );
540 QList<QgsFeature> featureList;
541 const auto results { mLayerIdResults[actData.mLayer] };
544 featureList << result.mFeature;
546 actData.mMapLayerAction->triggerForFeatures( actData.mLayer, featureList );
552 const auto results { mLayerIdResults[actData.mLayer] };
555 if ( result.mFeature.id() == actData.mFeatureId )
557 actData.mMapLayerAction->triggerForFeature( actData.mLayer, result.mFeature );
561 QgsDebugMsg( QStringLiteral(
"Identify menu: could not retrieve feature for action %1" ).arg( action->text() ) );
567 QList<QgsMapToolIdentify::IdentifyResult> QgsIdentifyMenu::results( QAction *action,
bool &externalAction )
569 QList<QgsMapToolIdentify::IdentifyResult> idResults = QList<QgsMapToolIdentify::IdentifyResult>();
571 externalAction =
false;
574 bool hasData =
false;
579 const QVariant varData = action->data();
580 if ( !varData.isValid() )
582 QgsDebugMsg( QStringLiteral(
"Identify menu: could not retrieve results from menu entry (invalid data)" ) );
586 if ( varData.canConvert<ActionData>() )
588 actData = action->data().value<ActionData>();
589 if ( actData.mIsValid )
591 externalAction = actData.mIsExternalAction;
601 externalAction =
true;
609 QgsDebugMsg( QStringLiteral(
"Identify menu: could not retrieve results from menu entry (no data found)" ) );
614 if ( actData.mAllResults )
617 QMapIterator< QgsMapLayer *, QList<QgsMapToolIdentify::IdentifyResult> > it( mLayerIdResults );
618 while ( it.hasNext() )
621 idResults << it.value();
626 if ( !mLayerIdResults.contains( actData.mLayer ) )
628 QgsDebugMsg( QStringLiteral(
"Identify menu: could not retrieve results from menu entry (layer not found)" ) );
634 return mLayerIdResults[actData.mLayer];
639 const auto results {mLayerIdResults[actData.mLayer]};
642 if ( res.mFeature.id() == actData.mFeatureId )
650 QgsDebugMsg( QStringLiteral(
"Identify menu: could not retrieve results from menu entry (don't know what happened')" ) );
654 void QgsIdentifyMenu::handleMenuHover()
661 QAction *senderAction = qobject_cast<QAction *>( sender() );
666 const QList<QgsMapToolIdentify::IdentifyResult> idResults = results( senderAction, externalAction );
668 const auto constIdResults = idResults;
671 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( result.mLayer );
677 mRubberBands.append( hl );
678 connect( vl, &QObject::destroyed,
this, &QgsIdentifyMenu::layerDestroyed );
691 color.setAlpha( alpha );
697 void QgsIdentifyMenu::deleteRubberBands()
699 QList<QgsHighlight *>::const_iterator it = mRubberBands.constBegin();
700 for ( ; it != mRubberBands.constEnd(); ++it )
702 mRubberBands.clear();
705 void QgsIdentifyMenu::layerDestroyed()
707 QList<QgsHighlight *>::iterator it = mRubberBands.begin();
708 while ( it != mRubberBands.end() )
710 if ( ( *it )->layer() == sender() )
713 it = mRubberBands.erase( it );
724 mCustomActionRegistry.clear();
730 mExpressionContextScope = scope;
735 return mExpressionContextScope;