QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgsidentifymenu.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsidentifymenu.cpp - menu to be used in identify map tool
3  ---------------------
4  begin : August 2014
5  copyright : (C) 2014 by Denis Rouzaud
6  email : [email protected]
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include <QMouseEvent>
17 
18 #include "qgsidentifymenu.h"
19 #include "qgsapplication.h"
20 #include "qgsactionmanager.h"
21 #include "qgshighlight.h"
22 #include "qgsmapcanvas.h"
23 #include "qgsactionmenu.h"
24 #include "qgsvectorlayer.h"
25 #include "qgslogger.h"
26 #include "qgssettings.h"
27 #include "qgsgui.h"
29 #include "qgsiconutils.h"
30 #include "qgsmapmouseevent.h"
31 
32 //TODO 4.0 add explicitly qobject parent to constructor
34  : QMenu( canvas )
35  , mCanvas( canvas )
36  , mAllowMultipleReturn( true )
37  , mExecWithSingleResult( false )
38  , mShowFeatureActions( false )
39  , mResultsIfExternalAction( false )
40  , mMaxLayerDisplay( 10 )
41  , mMaxFeatureDisplay( 10 )
42  , mDefaultActionName( tr( "Identify" ) )
43 {
44 }
45 
47 {
48  deleteRubberBands();
49 }
50 
51 QList<QgsMapToolIdentify::IdentifyResult> QgsIdentifyMenu::findFeaturesOnCanvas( QgsMapMouseEvent *event, QgsMapCanvas *canvas, const QList<QgsWkbTypes::GeometryType> &geometryTypes )
52 {
53  QList<QgsMapToolIdentify::IdentifyResult> results;
54  const QMap< QString, QString > derivedAttributes;
55 
56  const QgsPointXY mapPoint = canvas->getCoordinateTransform()->toMapCoordinates( event->pos() ) ;
57  const double x = mapPoint.x();
58  const double y = mapPoint.y();
59  const double sr = QgsMapTool::searchRadiusMU( canvas );
60 
61  const QList<QgsMapLayer *> layers = canvas->layers( true );
62  for ( QgsMapLayer *layer : layers )
63  {
64  if ( layer->type() == QgsMapLayerType::VectorLayer )
65  {
66  QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layer );
67 
68  bool typeIsSelectable = false;
69  for ( QgsWkbTypes::GeometryType type : geometryTypes )
70  {
71  if ( vectorLayer->geometryType() == type )
72  {
73  typeIsSelectable = true;
74  break;
75  }
76  }
77  if ( typeIsSelectable )
78  {
79  QgsRectangle rect( x - sr, y - sr, x + sr, y + sr );
80  QgsCoordinateTransform transform = canvas->mapSettings().layerTransform( vectorLayer );
81  transform.setBallparkTransformsAreAppropriate( true );
82 
83  try
84  {
85  rect = transform.transformBoundingBox( rect, Qgis::TransformDirection::Reverse );
86  }
87  catch ( QgsCsException & )
88  {
89  QgsDebugMsg( QStringLiteral( "Could not transform geometry to layer CRS" ) );
90  }
91 
93  .setFilterRect( rect )
94  .setFlags( QgsFeatureRequest::ExactIntersect ) );
95  QgsFeature f;
96  while ( fit.nextFeature( f ) )
97  {
98  results << QgsMapToolIdentify::IdentifyResult( vectorLayer, f, derivedAttributes );
99  }
100  }
101  }
102  }
103 
104  return results;
105 }
106 
107 void QgsIdentifyMenu::setMaxLayerDisplay( int maxLayerDisplay )
108 {
109  if ( maxLayerDisplay < 0 )
110  {
111  QgsDebugMsg( QStringLiteral( "invalid value for number of layers displayed." ) );
112  }
113  mMaxLayerDisplay = maxLayerDisplay;
114 }
115 
116 
117 void QgsIdentifyMenu::setMaxFeatureDisplay( int maxFeatureDisplay )
118 {
119  if ( maxFeatureDisplay < 0 )
120  {
121  QgsDebugMsg( QStringLiteral( "invalid value for number of layers displayed." ) );
122  }
123  mMaxFeatureDisplay = maxFeatureDisplay;
124 }
125 
126 
127 QList<QgsMapToolIdentify::IdentifyResult> QgsIdentifyMenu::exec( const QList<QgsMapToolIdentify::IdentifyResult> &idResults, QPoint pos )
128 {
129  clear();
130  mLayerIdResults.clear();
131 
132  QList<QgsMapToolIdentify::IdentifyResult> returnResults = QList<QgsMapToolIdentify::IdentifyResult>();
133 
134  if ( idResults.isEmpty() )
135  {
136  return returnResults;
137  }
138  if ( idResults.count() == 1 && !mExecWithSingleResult )
139  {
140  returnResults << idResults[0];
141  return returnResults;
142  }
143 
144  // sort results by layer
145  const auto constIdResults = idResults;
146  for ( const QgsMapToolIdentify::IdentifyResult &result : constIdResults )
147  {
148  QgsMapLayer *layer = result.mLayer;
149  if ( mLayerIdResults.contains( layer ) )
150  {
151  mLayerIdResults[layer].append( result );
152  }
153  else
154  {
155  mLayerIdResults.insert( layer, QList<QgsMapToolIdentify::IdentifyResult>() << result );
156  }
157  }
158 
159  // add results to the menu
160  const bool singleLayer = mLayerIdResults.count() == 1;
161  int count = 0;
162  QMapIterator< QgsMapLayer *, QList<QgsMapToolIdentify::IdentifyResult> > it( mLayerIdResults );
163  while ( it.hasNext() )
164  {
165  if ( mMaxLayerDisplay != 0 && count > mMaxLayerDisplay )
166  break;
167  ++count;
168  it.next();
169  QgsMapLayer *layer = it.key();
170  switch ( layer->type() )
171  {
173  {
174  addRasterLayer( layer );
175  break;
176  }
178  {
179  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
180  if ( !vl )
181  continue;
182  addVectorLayer( vl, it.value(), singleLayer );
183  break;
184  }
185 
187  // TODO: add support
188  break;
189 
195  break;
196  }
197  }
198 
199  // add an "identify all" action on the top level
200  if ( !singleLayer && mAllowMultipleReturn && idResults.count() > 1 )
201  {
202  addSeparator();
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 );
207  }
208 
209  // exec
210  QAction *selectedAction = QMenu::exec( pos );
211  bool externalAction;
212  returnResults = results( selectedAction, externalAction );
213 
214  // delete actions
215  clear();
216  // also remove the QgsActionMenu
217  qDeleteAll( findChildren<QgsActionMenu *>() );
218 
219  if ( externalAction && !mResultsIfExternalAction )
220  {
221  return QList<QgsMapToolIdentify::IdentifyResult>();
222  }
223  else
224  {
225  return returnResults;
226  }
227 }
228 
229 void QgsIdentifyMenu::closeEvent( QCloseEvent *e )
230 {
231  deleteRubberBands();
232  QMenu::closeEvent( e );
233 }
234 
235 void QgsIdentifyMenu::addRasterLayer( QgsMapLayer *layer )
236 {
237  QAction *layerAction = nullptr;
238  QMenu *layerMenu = nullptr;
239 
240  QList<QgsMapLayerAction *> separators = QList<QgsMapLayerAction *>();
241  QList<QgsMapLayerAction *> layerActions = mCustomActionRegistry.mapLayerActions( layer, QgsMapLayerAction::Layer );
242  const int nCustomActions = layerActions.count();
243  if ( nCustomActions )
244  {
245  separators.append( layerActions[0] );
246  }
247  if ( mShowFeatureActions )
248  {
249  layerActions.append( QgsGui::mapLayerActionRegistry()->mapLayerActions( layer, QgsMapLayerAction::Layer ) );
250  if ( layerActions.count() > nCustomActions )
251  {
252  separators.append( layerActions[nCustomActions] );
253  }
254  }
255 
256  // use a menu only if actions will be listed
257  if ( layerActions.isEmpty() )
258  {
259  layerAction = new QAction( layer->name(), this );
260  }
261  else
262  {
263  layerMenu = new QMenu( layer->name(), this );
264  layerAction = layerMenu->menuAction();
265  }
266 
267  // add layer action to the top menu
268  layerAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconRasterLayer.svg" ) ) );
269  layerAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
270  connect( layerAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
271  addAction( layerAction );
272 
273  // no need to go further if there is no menu
274  if ( !layerMenu )
275  return;
276 
277  // add default identify action
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 );
282 
283  // add custom/layer actions
284  const auto constLayerActions = layerActions;
285  for ( QgsMapLayerAction *mapLayerAction : constLayerActions )
286  {
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 ) )
293  {
294  layerMenu->insertSeparator( action );
295  }
296  }
297 }
298 
299 void QgsIdentifyMenu::addVectorLayer( QgsVectorLayer *layer, const QList<QgsMapToolIdentify::IdentifyResult> &results, bool singleLayer )
300 {
301  QAction *layerAction = nullptr;
302  QMenu *layerMenu = nullptr;
303 
304  // do not add actions with MultipleFeatures as target if only 1 feature is found for this layer
305  // targets defines which actions will be shown
306  const QgsMapLayerAction::Targets targets = results.count() > 1 ? QgsMapLayerAction::Layer | QgsMapLayerAction::MultipleFeatures : QgsMapLayerAction::Layer;
307 
308  QList<QgsMapLayerAction *> separators = QList<QgsMapLayerAction *>();
309  QList<QgsMapLayerAction *> layerActions = mCustomActionRegistry.mapLayerActions( layer, targets );
310  const int nCustomActions = layerActions.count();
311  if ( nCustomActions )
312  {
313  separators << layerActions[0];
314  }
315  if ( mShowFeatureActions )
316  {
317  layerActions << QgsGui::mapLayerActionRegistry()->mapLayerActions( layer, targets );
318 
319  if ( layerActions.count() > nCustomActions )
320  {
321  separators << layerActions[nCustomActions];
322  }
323  }
324 
325  // determines if a menu should be created or not. Following cases:
326  // 1. only one result and no feature action to be shown => just create an action
327  // 2. several features (2a) or display feature actions (2b) => create a menu
328  // 3. case 2 but only one layer (singeLayer) => do not create a menu, but give the top menu instead
329 
330  bool createMenu = results.count() > 1 || !layerActions.isEmpty();
331 
332  // case 2b: still create a menu for layer, if there is a sub-level for features
333  // i.e custom actions or map layer actions at feature level
334  if ( !createMenu )
335  {
336  createMenu = !mCustomActionRegistry.mapLayerActions( layer, QgsMapLayerAction::SingleFeature ).isEmpty();
337  if ( !createMenu && mShowFeatureActions )
338  {
339  QgsActionMenu *featureActionMenu = new QgsActionMenu( layer, results[0].mFeature, QStringLiteral( "Feature" ), this );
340  featureActionMenu->setMode( QgsAttributeEditorContext::IdentifyMode );
341  createMenu = !featureActionMenu->actions().isEmpty();
342  delete featureActionMenu;
343  }
344  }
345 
347  QgsExpression exp( layer->displayExpression() );
348  exp.prepare( &context );
349  context.setFeature( results[0].mFeature );
350  // use a menu only if actions will be listed
351  if ( !createMenu )
352  {
353  // case 1
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 );
358  }
359  else
360  {
361  if ( singleLayer )
362  {
363  // case 3
364  layerMenu = this;
365  }
366  else
367  {
368  // case 2a
369  if ( results.count() > 1 )
370  {
371  layerMenu = new QMenu( layer->name(), this );
372  }
373  // case 2b
374  else
375  {
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 );
380  }
381  layerAction = layerMenu->menuAction();
382  }
383  }
384 
385  // case 1 or 2
386  if ( layerAction )
387  {
388  layerAction->setIcon( QgsIconUtils::iconForWkbType( layer->wkbType() ) );
389 
390  // add layer action to the top menu
391  layerAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
392  connect( layerAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
393  addAction( layerAction );
394  }
395 
396  // case 1. no need to go further
397  if ( !layerMenu )
398  return;
399 
400  // add results to the menu
401  int count = 0;
402  const auto constResults = results;
403  for ( const QgsMapToolIdentify::IdentifyResult &result : constResults )
404  {
405  if ( mMaxFeatureDisplay != 0 && count > mMaxFeatureDisplay )
406  break;
407  ++count;
408 
409  QAction *featureAction = nullptr;
410  QMenu *featureMenu = nullptr;
411  QgsActionMenu *featureActionMenu = nullptr;
412 
413  const QList<QgsMapLayerAction *> customFeatureActions = mCustomActionRegistry.mapLayerActions( layer, QgsMapLayerAction::SingleFeature );
414  if ( mShowFeatureActions )
415  {
416  featureActionMenu = new QgsActionMenu( layer, result.mFeature, QStringLiteral( "Feature" ), layerMenu );
417  featureActionMenu->setMode( QgsAttributeEditorContext::IdentifyMode );
418  featureActionMenu->setExpressionContextScope( mExpressionContextScope );
419  }
420 
421  // feature title
422  context.setFeature( result.mFeature );
423  QString featureTitle = exp.evaluate( &context ).toString();
424  if ( featureTitle.isEmpty() )
425  featureTitle = QString::number( result.mFeature.id() );
426 
427  if ( customFeatureActions.isEmpty() && ( !featureActionMenu || featureActionMenu->actions().isEmpty() ) )
428  {
429  featureAction = new QAction( featureTitle, layerMenu );
430  // add the feature action (or menu) to the layer menu
431  featureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
432  connect( featureAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
433  layerMenu->addAction( featureAction );
434  }
435  else if ( results.count() == 1 )
436  {
437  // if we are here with only one results, this means there is a sub-feature level (for actions)
438  // => skip the feature level since there would be only a single entry
439  // => give the layer menu as pointer instead of a new feature menu
440  featureMenu = layerMenu;
441  }
442  else
443  {
444  featureMenu = new QMenu( featureTitle, layerMenu );
445 
446  // get the action from the menu
447  featureAction = featureMenu->menuAction();
448  // add the feature action (or menu) to the layer menu
449  featureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
450  connect( featureAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
451  layerMenu->addAction( featureAction );
452  }
453 
454  // if no feature menu, no need to go further
455  if ( !featureMenu )
456  continue;
457 
458  // add default identify action
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();
464 
465  // custom action at feature level
466  const auto constCustomFeatureActions = customFeatureActions;
467  for ( QgsMapLayerAction *mapLayerAction : constCustomFeatureActions )
468  {
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 );
474  }
475  // use QgsActionMenu for feature actions
476  if ( featureActionMenu )
477  {
478  const auto constActions = featureActionMenu->actions();
479  for ( QAction *action : constActions )
480  {
481  connect( action, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
482  featureMenu->addAction( action );
483  }
484  }
485  }
486 
487  // back to layer level
488 
489  // identify all action
490  if ( mAllowMultipleReturn && results.count() > 1 )
491  {
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 );
497  }
498 
499  // add custom/layer actions
500  const auto constLayerActions = layerActions;
501  for ( QgsMapLayerAction *mapLayerAction : constLayerActions )
502  {
503  QString title = mapLayerAction->text();
504  if ( mapLayerAction->targets().testFlag( QgsMapLayerAction::MultipleFeatures ) )
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 ) )
512  {
513  layerMenu->insertSeparator( action );
514  }
515  }
516 }
517 
518 void QgsIdentifyMenu::triggerMapLayerAction()
519 {
520  QAction *action = qobject_cast<QAction *>( sender() );
521  if ( !action )
522  return;
523  const QVariant varData = action->data();
524  if ( !varData.isValid() || !varData.canConvert<ActionData>() )
525  return;
526 
527  ActionData actData = action->data().value<ActionData>();
528 
529  if ( actData.mIsValid && actData.mMapLayerAction )
530  {
531  // layer
532  if ( actData.mMapLayerAction->targets().testFlag( QgsMapLayerAction::Layer ) )
533  {
534  actData.mMapLayerAction->triggerForLayer( actData.mLayer );
535  }
536 
537  // multiples features
538  if ( actData.mMapLayerAction->targets().testFlag( QgsMapLayerAction::MultipleFeatures ) )
539  {
540  QList<QgsFeature> featureList;
541  const auto results { mLayerIdResults[actData.mLayer] };
542  for ( const QgsMapToolIdentify::IdentifyResult &result : results )
543  {
544  featureList << result.mFeature;
545  }
546  actData.mMapLayerAction->triggerForFeatures( actData.mLayer, featureList );
547  }
548 
549  // single feature
550  if ( actData.mMapLayerAction->targets().testFlag( QgsMapLayerAction::SingleFeature ) )
551  {
552  const auto results { mLayerIdResults[actData.mLayer] };
553  for ( const QgsMapToolIdentify::IdentifyResult &result : results )
554  {
555  if ( result.mFeature.id() == actData.mFeatureId )
556  {
557  actData.mMapLayerAction->triggerForFeature( actData.mLayer, result.mFeature );
558  return;
559  }
560  }
561  QgsDebugMsg( QStringLiteral( "Identify menu: could not retrieve feature for action %1" ).arg( action->text() ) );
562  }
563  }
564 }
565 
566 
567 QList<QgsMapToolIdentify::IdentifyResult> QgsIdentifyMenu::results( QAction *action, bool &externalAction )
568 {
569  QList<QgsMapToolIdentify::IdentifyResult> idResults = QList<QgsMapToolIdentify::IdentifyResult>();
570 
571  externalAction = false;
572 
573  ActionData actData;
574  bool hasData = false;
575 
576  if ( !action )
577  return idResults;
578 
579  const QVariant varData = action->data();
580  if ( !varData.isValid() )
581  {
582  QgsDebugMsg( QStringLiteral( "Identify menu: could not retrieve results from menu entry (invalid data)" ) );
583  return idResults;
584  }
585 
586  if ( varData.canConvert<ActionData>() )
587  {
588  actData = action->data().value<ActionData>();
589  if ( actData.mIsValid )
590  {
591  externalAction = actData.mIsExternalAction;
592  hasData = true;
593  }
594  }
595 
596  if ( !hasData && varData.canConvert<QgsActionMenu::ActionData>() )
597  {
598  const QgsActionMenu::ActionData dataSrc = action->data().value<QgsActionMenu::ActionData>();
599  if ( dataSrc.actionType != QgsActionMenu::Invalid )
600  {
601  externalAction = true;
602  actData = ActionData( dataSrc.mapLayer, dataSrc.featureId );
603  hasData = true;
604  }
605  }
606 
607  if ( !hasData )
608  {
609  QgsDebugMsg( QStringLiteral( "Identify menu: could not retrieve results from menu entry (no data found)" ) );
610  return idResults;
611  }
612 
613  // return all results
614  if ( actData.mAllResults )
615  {
616  // this means "All" action was triggered
617  QMapIterator< QgsMapLayer *, QList<QgsMapToolIdentify::IdentifyResult> > it( mLayerIdResults );
618  while ( it.hasNext() )
619  {
620  it.next();
621  idResults << it.value();
622  }
623  return idResults;
624  }
625 
626  if ( !mLayerIdResults.contains( actData.mLayer ) )
627  {
628  QgsDebugMsg( QStringLiteral( "Identify menu: could not retrieve results from menu entry (layer not found)" ) );
629  return idResults;
630  }
631 
632  if ( actData.mLevel == LayerLevel )
633  {
634  return mLayerIdResults[actData.mLayer];
635  }
636 
637  if ( actData.mLevel == FeatureLevel )
638  {
639  const auto results {mLayerIdResults[actData.mLayer]};
640  for ( const QgsMapToolIdentify::IdentifyResult &res : results )
641  {
642  if ( res.mFeature.id() == actData.mFeatureId )
643  {
644  idResults << res;
645  return idResults;
646  }
647  }
648  }
649 
650  QgsDebugMsg( QStringLiteral( "Identify menu: could not retrieve results from menu entry (don't know what happened')" ) );
651  return idResults;
652 }
653 
654 void QgsIdentifyMenu::handleMenuHover()
655 {
656  if ( !mCanvas )
657  return;
658 
659  deleteRubberBands();
660 
661  QAction *senderAction = qobject_cast<QAction *>( sender() );
662  if ( !senderAction )
663  return;
664 
665  bool externalAction;
666  const QList<QgsMapToolIdentify::IdentifyResult> idResults = results( senderAction, externalAction );
667 
668  const auto constIdResults = idResults;
669  for ( const QgsMapToolIdentify::IdentifyResult &result : constIdResults )
670  {
671  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( result.mLayer );
672  if ( !vl )
673  continue;
674 
675  QgsHighlight *hl = new QgsHighlight( mCanvas, result.mFeature.geometry(), vl );
676  styleHighlight( hl );
677  mRubberBands.append( hl );
678  connect( vl, &QObject::destroyed, this, &QgsIdentifyMenu::layerDestroyed );
679  }
680 }
681 
683 {
684  const QgsSettings settings;
685  QColor color = QColor( settings.value( QStringLiteral( "Map/highlight/color" ), Qgis::DEFAULT_HIGHLIGHT_COLOR.name() ).toString() );
686  const int alpha = settings.value( QStringLiteral( "Map/highlight/colorAlpha" ), Qgis::DEFAULT_HIGHLIGHT_COLOR.alpha() ).toInt();
687  const double buffer = settings.value( QStringLiteral( "Map/highlight/buffer" ), Qgis::DEFAULT_HIGHLIGHT_BUFFER_MM ).toDouble();
688  const double minWidth = settings.value( QStringLiteral( "Map/highlight/minWidth" ), Qgis::DEFAULT_HIGHLIGHT_MIN_WIDTH_MM ).toDouble();
689 
690  highlight->setColor( color ); // sets also fill with default alpha
691  color.setAlpha( alpha );
692  highlight->setFillColor( color ); // sets fill with alpha
693  highlight->setBuffer( buffer );
694  highlight->setMinWidth( minWidth );
695 }
696 
697 void QgsIdentifyMenu::deleteRubberBands()
698 {
699  QList<QgsHighlight *>::const_iterator it = mRubberBands.constBegin();
700  for ( ; it != mRubberBands.constEnd(); ++it )
701  delete *it;
702  mRubberBands.clear();
703 }
704 
705 void QgsIdentifyMenu::layerDestroyed()
706 {
707  QList<QgsHighlight *>::iterator it = mRubberBands.begin();
708  while ( it != mRubberBands.end() )
709  {
710  if ( ( *it )->layer() == sender() )
711  {
712  delete *it;
713  it = mRubberBands.erase( it );
714  }
715  else
716  {
717  ++it;
718  }
719  }
720 }
721 
723 {
724  mCustomActionRegistry.clear();
725 
726 }
727 
729 {
730  mExpressionContextScope = scope;
731 }
732 
734 {
735  return mExpressionContextScope;
736 }
QgsHighlight::setColor
void setColor(const QColor &color)
Set line/stroke to color, polygon fill to color with alpha = 63.
Definition: qgshighlight.cpp:109
QgsActionMenu::Invalid
@ Invalid
Invalid.
Definition: qgsactionmenu.h:44
QgsVectorLayer::getFeatures
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
Definition: qgsvectorlayer.cpp:1052
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:406
QgsIdentifyMenu::expressionContextScope
QgsExpressionContextScope expressionContextScope() const
Returns an expression context scope used to resolve underlying actions.
Definition: qgsidentifymenu.cpp:733
qgsexpressioncontextutils.h
QgsIdentifyMenu::removeCustomActions
void removeCustomActions()
remove all custom actions from the menu to be built
Definition: qgsidentifymenu.cpp:722
QgsPointXY::y
double y
Definition: qgspointxy.h:63
Qgis::DEFAULT_HIGHLIGHT_COLOR
static const QColor DEFAULT_HIGHLIGHT_COLOR
Default highlight color.
Definition: qgis.h:2002
QgsVectorLayer::wkbType
Q_INVOKABLE QgsWkbTypes::Type wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
Definition: qgsvectorlayer.cpp:725
QgsMapLayerAction
An action which can run on map layers The class can be used in two manners:
Definition: qgsmaplayeractionregistry.h:38
QgsIdentifyMenu::QgsIdentifyMenu
QgsIdentifyMenu(QgsMapCanvas *canvas)
QgsIdentifyMenu is a menu to be used to choose within a list of QgsMapTool::IdentifyReults.
Definition: qgsidentifymenu.cpp:33
QgsMapLayerAction::SingleFeature
@ SingleFeature
Definition: qgsmaplayeractionregistry.h:46
QgsActionMenu::setMode
void setMode(QgsAttributeEditorContext::Mode mode)
Change the mode of the actions.
Definition: qgsactionmenu.cpp:70
Qgis::DEFAULT_HIGHLIGHT_MIN_WIDTH_MM
static const double DEFAULT_HIGHLIGHT_MIN_WIDTH_MM
Default highlight line/stroke minimum width in mm.
Definition: qgis.h:2014
QgsSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Definition: qgssettings.cpp:161
QgsMapLayerType::MeshLayer
@ MeshLayer
Mesh layer. Added in QGIS 3.2.
QgsFeatureRequest::ExactIntersect
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
Definition: qgsfeaturerequest.h:117
QgsMapSettings::layerTransform
QgsCoordinateTransform layerTransform(const QgsMapLayer *layer) const
Returns the coordinate transform from layer's CRS to destination CRS.
Definition: qgsmapsettings.cpp:481
qgsmapcanvas.h
QgsMapLayerType::VectorLayer
@ VectorLayer
Vector layer.
QgsGui::mapLayerActionRegistry
static QgsMapLayerActionRegistry * mapLayerActionRegistry()
Returns the global map layer action registry, used for registering map layer actions.
Definition: qgsgui.cpp:125
QgsMapCanvas::mapSettings
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
Definition: qgsmapcanvas.cpp:437
QgsActionMenu::ActionData::mapLayer
QgsMapLayer * mapLayer
Definition: qgsactionmenu.h:62
qgsgui.h
QgsMapLayerType::AnnotationLayer
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
QgsMapLayerAction::MultipleFeatures
@ MultipleFeatures
Definition: qgsmaplayeractionregistry.h:47
QgsCoordinateTransform::transformBoundingBox
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
Definition: qgscoordinatetransform.cpp:560
QgsMapCanvas
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:89
QgsActionMenu::setExpressionContextScope
void setExpressionContextScope(const QgsExpressionContextScope &scope)
Sets an expression context scope used to resolve underlying actions.
Definition: qgsactionmenu.cpp:198
QgsExpressionContextUtils::globalProjectLayerScopes
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Definition: qgsexpressioncontextutils.cpp:377
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:61
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsMapToPixel::toMapCoordinates
QgsPointXY toMapCoordinates(int x, int y) const
Transforms device coordinates to map (world) coordinates.
Definition: qgsmaptopixel.h:173
QgsIdentifyMenu::maxLayerDisplay
int maxLayerDisplay()
Definition: qgsidentifymenu.h:168
QgsIdentifyMenu::exec
QList< QgsMapToolIdentify::IdentifyResult > exec(const QList< QgsMapToolIdentify::IdentifyResult > &idResults, QPoint pos)
exec
Definition: qgsidentifymenu.cpp:127
qgsidentifymenu.h
QgsHighlight::setBuffer
void setBuffer(double buffer)
Set line / stroke buffer in millimeters.
Definition: qgshighlight.h:159
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:41
QgsIdentifyMenu::setMaxLayerDisplay
void setMaxLayerDisplay(int maxLayerDisplay)
Defines the maximum number of layers displayed in the menu (default is 10).
Definition: qgsidentifymenu.cpp:107
QgsActionMenu::ActionData
Definition: qgsactionmenu.h:49
QgsHighlight
A class for highlight features on the map.
Definition: qgshighlight.h:61
qgsapplication.h
QgsIdentifyMenu::FeatureLevel
@ FeatureLevel
Definition: qgsidentifymenu.h:84
QgsHighlight::setMinWidth
void setMinWidth(double width)
Set minimum line / stroke width in millimeters.
Definition: qgshighlight.h:166
QgsFeatureRequest
This class wraps a request for features to a vector layer (or directly its vector data provider).
Definition: qgsfeaturerequest.h:83
QgsCsException
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
QgsCoordinateTransform::setBallparkTransformsAreAppropriate
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
Definition: qgscoordinatetransform.cpp:939
qgsactionmanager.h
QgsMapLayerType::GroupLayer
@ GroupLayer
Composite group layer. Added in QGIS 3.24.
QgsMapLayerAction::Layer
@ Layer
Definition: qgsmaplayeractionregistry.h:45
QgsIdentifyMenu::findFeaturesOnCanvas
static QList< QgsMapToolIdentify::IdentifyResult > findFeaturesOnCanvas(QgsMapMouseEvent *event, QgsMapCanvas *canvas, const QList< QgsWkbTypes::GeometryType > &geometryTypes)
Searches for features on the map canvas, which are located at the specified event point.
Definition: qgsidentifymenu.cpp:51
QgsMapLayerType::RasterLayer
@ RasterLayer
Raster layer.
QgsIdentifyMenu::~QgsIdentifyMenu
~QgsIdentifyMenu() override
Definition: qgsidentifymenu.cpp:46
QgsMapLayerActionRegistry::mapLayerActions
QList< QgsMapLayerAction * > mapLayerActions(QgsMapLayer *layer, QgsMapLayerAction::Targets targets=QgsMapLayerAction::AllActions)
Returns the map layer actions which can run on the specified layer.
Definition: qgsmaplayeractionregistry.cpp:125
QgsActionMenu
This class is a menu that is populated automatically with the actions defined for a given layer.
Definition: qgsactionmenu.h:37
QgsIdentifyMenu::setMaxFeatureDisplay
void setMaxFeatureDisplay(int maxFeatureDisplay)
Defines the maximum number of features displayed in the menu for vector layers (default is 10).
Definition: qgsidentifymenu.cpp:117
QgsIdentifyMenu::setExpressionContextScope
void setExpressionContextScope(const QgsExpressionContextScope &scope)
Sets an expression context scope used to resolve underlying actions.
Definition: qgsidentifymenu.cpp:728
QgsMapCanvas::getCoordinateTransform
const QgsMapToPixel * getCoordinateTransform()
Gets the current coordinate transform.
Definition: qgsmapcanvas.cpp:379
QgsExpressionContextScope
Single scope for storing variables and functions for use within a QgsExpressionContext....
Definition: qgsexpressioncontext.h:113
QgsHighlight::setFillColor
void setFillColor(const QColor &fillColor)
Fill color for the highlight.
Definition: qgshighlight.cpp:118
qgsvectorlayer.h
QgsPointXY
A class to represent a 2D point.
Definition: qgspointxy.h:58
qgsactionmenu.h
QgsMapMouseEvent
A QgsMapMouseEvent is the result of a user interaction with the mouse on a QgsMapCanvas....
Definition: qgsmapmouseevent.h:35
QgsMapCanvas::layers
QList< QgsMapLayer * > layers(bool expandGroupLayers=false) const
Returns the list of layers shown within the map canvas.
Definition: qgsmapcanvas.cpp:2607
qgshighlight.h
QgsWkbTypes::GeometryType
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:140
QgsFeatureIterator::nextFeature
bool nextFeature(QgsFeature &f)
Definition: qgsfeatureiterator.h:399
Qgis::DEFAULT_HIGHLIGHT_BUFFER_MM
static const double DEFAULT_HIGHLIGHT_BUFFER_MM
Default highlight buffer in mm.
Definition: qgis.h:2008
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:391
QgsIconUtils::iconForWkbType
static QIcon iconForWkbType(QgsWkbTypes::Type type)
Returns the icon for a vector layer whose geometry type is provided.
Definition: qgsiconutils.cpp:25
QgsMapLayer
Base class for all map layer types. This is the base class for all map layer types (vector,...
Definition: qgsmaplayer.h:72
QgsPointXY::x
double x
Definition: qgspointxy.h:62
qgssettings.h
QgsIdentifyMenu::closeEvent
void closeEvent(QCloseEvent *e) override
Definition: qgsidentifymenu.cpp:229
QgsMapLayerType::VectorTileLayer
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
QgsMapLayer::name
QString name
Definition: qgsmaplayer.h:76
qgsiconutils.h
QgsIdentifyMenu::LayerLevel
@ LayerLevel
Definition: qgsidentifymenu.h:83
QgsMapTool::searchRadiusMU
static double searchRadiusMU(const QgsRenderContext &context)
Gets search radius in map units for given context.
Definition: qgsmaptool.cpp:232
QgsFeature
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:55
QgsApplication::getThemeIcon
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Definition: qgsapplication.cpp:693
QgsIdentifyMenu::ActionData
Definition: qgsidentifymenu.h:74
qgslogger.h
QgsExpression
Class for parsing and evaluation of expressions (formerly called "search strings")....
Definition: qgsexpression.h:102
QgsVectorLayer::displayExpression
QString displayExpression
Definition: qgsvectorlayer.h:396
QgsCoordinateTransform
Class for doing transforms between two map coordinate systems.
Definition: qgscoordinatetransform.h:57
qgsmapmouseevent.h
QgsFeatureIterator
Wrapper for iterator of features from vector data provider or vector layer.
Definition: qgsfeatureiterator.h:289
QgsVectorLayer::geometryType
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
Definition: qgsvectorlayer.cpp:720
QgsMapLayerType::PointCloudLayer
@ PointCloudLayer
Point cloud layer. Added in QGIS 3.18.
QgsIdentifyMenu::styleHighlight
static void styleHighlight(QgsHighlight *highlight)
Applies style from the settings to the highlight.
Definition: qgsidentifymenu.cpp:682
QgsMapLayerType::PluginLayer
@ PluginLayer
Plugin based layer.
QgsActionMenu::ActionData::featureId
QgsFeatureId featureId
Definition: qgsactionmenu.h:61
QgsAttributeEditorContext::IdentifyMode
@ IdentifyMode
Identify the feature.
Definition: qgsattributeeditorcontext.h:56
QgsMapToolIdentify::IdentifyResult
Definition: qgsmaptoolidentify.h:81
QgsIdentifyMenu::maxFeatureDisplay
int maxFeatureDisplay()
Definition: qgsidentifymenu.h:175
QgsActionMenu::ActionData::actionType
QgsActionMenu::ActionType actionType
Definition: qgsactionmenu.h:59
QgsMapLayer::type
QgsMapLayerType type
Definition: qgsmaplayer.h:80