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;