QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
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
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 "qgsidentifymenu.h"
17
18#include "qgsactionmenu.h"
19#include "qgsapplication.h"
21#include "qgsgui.h"
22#include "qgshighlight.h"
23#include "qgsiconutils.h"
24#include "qgslogger.h"
25#include "qgsmapcanvas.h"
26#include "qgsmaplayeraction.h"
28#include "qgsmapmouseevent.h"
29#include "qgsvectorlayer.h"
30
31#include <QMouseEvent>
32
33#include "moc_qgsidentifymenu.cpp"
34
35//TODO 4.0 add explicitly qobject parent to constructor
37 : QMenu( canvas )
38 , mCanvas( canvas )
39 , mDefaultActionName( tr( "Identify" ) )
40{
41}
42
44{
45 deleteRubberBands();
46}
47
48QList<QgsMapToolIdentify::IdentifyResult> QgsIdentifyMenu::findFeaturesOnCanvas( QgsMapMouseEvent *event, QgsMapCanvas *canvas, const QList<Qgis::GeometryType> &geometryTypes )
49{
50 QList<QgsMapToolIdentify::IdentifyResult> results;
51 const QMap<QString, QString> derivedAttributes;
52
53 const QgsPointXY mapPoint = canvas->getCoordinateTransform()->toMapCoordinates( event->pos() );
54 const double x = mapPoint.x();
55 const double y = mapPoint.y();
56 const double sr = QgsMapTool::searchRadiusMU( canvas );
57
58 const QList<QgsMapLayer *> layers = canvas->layers( true );
59 for ( QgsMapLayer *layer : layers )
60 {
61 if ( layer->type() == Qgis::LayerType::Vector )
62 {
63 QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layer );
64
65 bool typeIsSelectable = false;
66 for ( Qgis::GeometryType type : geometryTypes )
67 {
68 if ( vectorLayer->geometryType() == type )
69 {
70 typeIsSelectable = true;
71 break;
72 }
73 }
74 if ( typeIsSelectable )
75 {
76 QgsRectangle rect( x - sr, y - sr, x + sr, y + sr );
77 QgsCoordinateTransform transform = canvas->mapSettings().layerTransform( vectorLayer );
79
80 try
81 {
83 }
84 catch ( QgsCsException & )
85 {
86 QgsDebugError( QStringLiteral( "Could not transform geometry to layer CRS" ) );
87 }
88
90 .setFilterRect( rect )
92 QgsFeature f;
93 while ( fit.nextFeature( f ) )
94 {
95 results << QgsMapToolIdentify::IdentifyResult( vectorLayer, f, derivedAttributes );
96 }
97 }
98 }
99 }
100
101 return results;
102}
103
105{
106 if ( maxLayerDisplay < 0 )
107 {
108 QgsDebugError( QStringLiteral( "invalid value for number of layers displayed." ) );
109 }
110 mMaxLayerDisplay = maxLayerDisplay;
111}
112
113
115{
116 if ( maxFeatureDisplay < 0 )
117 {
118 QgsDebugError( QStringLiteral( "invalid value for number of layers displayed." ) );
119 }
120 mMaxFeatureDisplay = maxFeatureDisplay;
121}
122
123
124QList<QgsMapToolIdentify::IdentifyResult> QgsIdentifyMenu::exec( const QList<QgsMapToolIdentify::IdentifyResult> &idResults, QPoint pos )
125{
126 clear();
127 mLayerIdResults.clear();
128
129 QList<QgsMapToolIdentify::IdentifyResult> returnResults = QList<QgsMapToolIdentify::IdentifyResult>();
130
131 if ( idResults.isEmpty() )
132 {
133 return returnResults;
134 }
135 if ( idResults.count() == 1 && !mExecWithSingleResult )
136 {
137 returnResults << idResults[0];
138 return returnResults;
139 }
140
141 // sort results by layer
142 const auto constIdResults = idResults;
143 for ( const QgsMapToolIdentify::IdentifyResult &result : constIdResults )
144 {
145 QgsMapLayer *layer = result.mLayer;
146 if ( mLayerIdResults.contains( layer ) )
147 {
148 mLayerIdResults[layer].append( result );
149 }
150 else
151 {
152 mLayerIdResults.insert( layer, QList<QgsMapToolIdentify::IdentifyResult>() << result );
153 }
154 }
155
156 // add results to the menu
157 const bool singleLayer = mLayerIdResults.count() == 1;
158 int count = 0;
159 QMapIterator<QgsMapLayer *, QList<QgsMapToolIdentify::IdentifyResult>> it( mLayerIdResults );
160 while ( it.hasNext() )
161 {
162 if ( mMaxLayerDisplay != 0 && count > mMaxLayerDisplay )
163 break;
164 ++count;
165 it.next();
166 QgsMapLayer *layer = it.key();
167 switch ( layer->type() )
168 {
170 {
171 addRasterLayer( layer );
172 break;
173 }
175 {
176 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
177 if ( !vl )
178 continue;
179 addVectorLayer( vl, it.value(), singleLayer );
180 break;
181 }
182
184 // TODO: add support
185 break;
186
193 break;
194 }
195 }
196
197 // add an "identify all" action on the top level
198 if ( !singleLayer && mAllowMultipleReturn && idResults.count() > 1 )
199 {
200 addSeparator();
201 QAction *allAction = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionIdentify.svg" ) ), tr( "%1 All (%2)" ).arg( mDefaultActionName ).arg( idResults.count() ), this );
202 allAction->setData( QVariant::fromValue<ActionData>( ActionData( nullptr ) ) );
203 connect( allAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
204 addAction( allAction );
205 }
206
207 // exec
208 QAction *selectedAction = QMenu::exec( pos );
209 bool externalAction;
210 returnResults = results( selectedAction, externalAction );
211
212 // delete actions
213 clear();
214 // also remove the QgsActionMenu
215 qDeleteAll( findChildren<QgsActionMenu *>() );
216
217 if ( externalAction && !mResultsIfExternalAction )
218 {
219 return QList<QgsMapToolIdentify::IdentifyResult>();
220 }
221 else
222 {
223 return returnResults;
224 }
225}
226
227void QgsIdentifyMenu::closeEvent( QCloseEvent *e )
228{
229 deleteRubberBands();
230 QMenu::closeEvent( e );
231}
232
233void QgsIdentifyMenu::addRasterLayer( QgsMapLayer *layer )
234{
235 QAction *layerAction = nullptr;
236 QMenu *layerMenu = nullptr;
237
238 QList<QgsMapLayerAction *> separators = QList<QgsMapLayerAction *>();
240 QList<QgsMapLayerAction *> layerActions = mCustomActionRegistry.mapLayerActions( layer, Qgis::MapLayerActionTarget::Layer, context );
241 const int nCustomActions = layerActions.count();
242 if ( nCustomActions )
243 {
244 separators.append( layerActions[0] );
245 }
246 if ( mShowFeatureActions )
247 {
248 layerActions.append( QgsGui::mapLayerActionRegistry()->mapLayerActions( layer, Qgis::MapLayerActionTarget::Layer, context ) );
249 if ( layerActions.count() > nCustomActions )
250 {
251 separators.append( layerActions[nCustomActions] );
252 }
253 }
254
255 // use a menu only if actions will be listed
256 if ( layerActions.isEmpty() )
257 {
258 layerAction = new QAction( layer->name(), this );
259 }
260 else
261 {
262 layerMenu = new QMenu( layer->name(), this );
263 layerAction = layerMenu->menuAction();
264 }
265
266 // add layer action to the top menu
267 layerAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconRasterLayer.svg" ) ) );
268 layerAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
269 connect( layerAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
270 addAction( layerAction );
271
272 // no need to go further if there is no menu
273 if ( !layerMenu )
274 return;
275
276 // add default identify action
277 QAction *identifyFeatureAction = new QAction( mDefaultActionName, layerMenu );
278 connect( identifyFeatureAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
279 identifyFeatureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
280 layerMenu->addAction( identifyFeatureAction );
281
282 // add custom/layer actions
283 const auto constLayerActions = layerActions;
284 for ( QgsMapLayerAction *mapLayerAction : constLayerActions )
285 {
286 QAction *action = new QAction( mapLayerAction->icon(), mapLayerAction->text(), layerMenu );
287 action->setData( QVariant::fromValue<ActionData>( ActionData( layer, true ) ) );
288 connect( action, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
289 connect( action, &QAction::triggered, this, &QgsIdentifyMenu::triggerMapLayerAction );
290 layerMenu->addAction( action );
291 if ( separators.contains( mapLayerAction ) )
292 {
293 layerMenu->insertSeparator( action );
294 }
295 }
296}
297
298void QgsIdentifyMenu::addVectorLayer( QgsVectorLayer *layer, const QList<QgsMapToolIdentify::IdentifyResult> &results, bool singleLayer )
299{
300 QAction *layerAction = nullptr;
301 QMenu *layerMenu = nullptr;
302
303 // do not add actions with MultipleFeatures as target if only 1 feature is found for this layer
304 // targets defines which actions will be shown
305 const Qgis::MapLayerActionTargets targets = results.count() > 1 ? ( Qgis::MapLayerActionTarget::Layer | Qgis::MapLayerActionTarget::MultipleFeatures ) : Qgis::MapLayerActionTarget::Layer;
306
307 QList<QgsMapLayerAction *> separators = QList<QgsMapLayerAction *>();
308 QgsMapLayerActionContext actionContext;
309 QList<QgsMapLayerAction *> layerActions = mCustomActionRegistry.mapLayerActions( layer, targets, actionContext );
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, actionContext );
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, Qgis::MapLayerActionTarget::SingleFeature, actionContext ).isEmpty();
337 if ( !createMenu && mShowFeatureActions )
338 {
339 QgsActionMenu *featureActionMenu = new QgsActionMenu( layer, results[0].mFeature, QStringLiteral( "Feature" ), this );
340 connect( featureActionMenu, &QgsActionMenu::messageEmitted, this, &QgsIdentifyMenu::messageEmitted );
341 connect( featureActionMenu, &QgsActionMenu::messageDiscarded, this, &QgsIdentifyMenu::messageDiscarded );
343 createMenu = !featureActionMenu->actions().isEmpty();
344 delete featureActionMenu;
345 }
346 }
347
348 QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
349 QgsExpression exp( layer->displayExpression() );
350 exp.prepare( &context );
351 context.setFeature( results[0].mFeature );
352 // use a menu only if actions will be listed
353 if ( !createMenu )
354 {
355 // case 1
356 QString featureTitle = exp.evaluate( &context ).toString();
357 if ( featureTitle.isEmpty() )
358 featureTitle = QString::number( results[0].mFeature.id() );
359 layerAction = new QAction( QStringLiteral( "%1 (%2)" ).arg( layer->name(), featureTitle ), this );
360 }
361 else
362 {
363 if ( singleLayer )
364 {
365 // case 3
366 layerMenu = this;
367 }
368 else
369 {
370 // case 2a
371 if ( results.count() > 1 )
372 {
373 layerMenu = new QMenu( layer->name(), this );
374 }
375 // case 2b
376 else
377 {
378 QString featureTitle = exp.evaluate( &context ).toString();
379 if ( featureTitle.isEmpty() )
380 featureTitle = QString::number( results[0].mFeature.id() );
381 layerMenu = new QMenu( QStringLiteral( "%1 (%2)" ).arg( layer->name(), featureTitle ), this );
382 }
383 layerAction = layerMenu->menuAction();
384 }
385 }
386
387 // case 1 or 2
388 if ( layerAction )
389 {
390 layerAction->setIcon( QgsIconUtils::iconForWkbType( layer->wkbType() ) );
391
392 // add layer action to the top menu
393 layerAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
394 connect( layerAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
395 addAction( layerAction );
396 }
397
398 // case 1. no need to go further
399 if ( !layerMenu )
400 return;
401
402 // add results to the menu
403 int count = 0;
404 const auto constResults = results;
405 for ( const QgsMapToolIdentify::IdentifyResult &result : constResults )
406 {
407 if ( mMaxFeatureDisplay != 0 && count > mMaxFeatureDisplay )
408 break;
409 ++count;
410
411 QAction *featureAction = nullptr;
412 QMenu *featureMenu = nullptr;
413 QgsActionMenu *featureActionMenu = nullptr;
414
415 const QList<QgsMapLayerAction *> customFeatureActions = mCustomActionRegistry.mapLayerActions( layer, Qgis::MapLayerActionTarget::SingleFeature, actionContext );
416 if ( mShowFeatureActions )
417 {
418 featureActionMenu = new QgsActionMenu( layer, result.mFeature, QStringLiteral( "Feature" ), layerMenu );
419 connect( featureActionMenu, &QgsActionMenu::messageEmitted, this, &QgsIdentifyMenu::messageEmitted );
420 connect( featureActionMenu, &QgsActionMenu::messageDiscarded, this, &QgsIdentifyMenu::messageDiscarded );
422 featureActionMenu->setExpressionContextScope( mExpressionContextScope );
423 }
424
425 // feature title
426 context.setFeature( result.mFeature );
427 QString featureTitle = exp.evaluate( &context ).toString();
428 if ( featureTitle.isEmpty() )
429 featureTitle = QString::number( result.mFeature.id() );
430
431 // if results are from the same layer we still add layer name to the feature
432 // title to be consistent with other cases, see https://github.com/qgis/QGIS/issues/50049
433 if ( layerMenu == this )
434 featureTitle = QStringLiteral( "%1 (%2)" ).arg( layer->name(), featureTitle );
435
436 if ( customFeatureActions.isEmpty() && ( !featureActionMenu || featureActionMenu->actions().isEmpty() ) )
437 {
438 featureAction = new QAction( featureTitle, layerMenu );
439 // add the feature action (or menu) to the layer menu
440 featureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
441 connect( featureAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
442 layerMenu->addAction( featureAction );
443 }
444 else if ( results.count() == 1 )
445 {
446 // if we are here with only one results, this means there is a sub-feature level (for actions)
447 // => skip the feature level since there would be only a single entry
448 // => give the layer menu as pointer instead of a new feature menu
449 featureMenu = layerMenu;
450 }
451 else
452 {
453 featureMenu = new QMenu( featureTitle, layerMenu );
454
455 // get the action from the menu
456 featureAction = featureMenu->menuAction();
457 // add the feature action (or menu) to the layer menu
458 featureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
459 connect( featureAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
460 layerMenu->addAction( featureAction );
461 }
462
463 // if no feature menu, no need to go further
464 if ( !featureMenu )
465 continue;
466
467 // add default identify action
468 QAction *identifyFeatureAction = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionIdentify.svg" ) ), mDefaultActionName, featureMenu );
469 connect( identifyFeatureAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
470 identifyFeatureAction->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id() ) ) );
471 featureMenu->addAction( identifyFeatureAction );
472 featureMenu->addSeparator();
473
474 // custom action at feature level
475 const auto constCustomFeatureActions = customFeatureActions;
476 for ( QgsMapLayerAction *mapLayerAction : constCustomFeatureActions )
477 {
478 QAction *action = new QAction( mapLayerAction->icon(), mapLayerAction->text(), featureMenu );
479 action->setData( QVariant::fromValue<ActionData>( ActionData( layer, result.mFeature.id(), mapLayerAction ) ) );
480 connect( action, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
481 connect( action, &QAction::triggered, this, &QgsIdentifyMenu::triggerMapLayerAction );
482 featureMenu->addAction( action );
483 }
484 // use QgsActionMenu for feature actions
485 if ( featureActionMenu )
486 {
487 const auto constActions = featureActionMenu->actions();
488 for ( QAction *action : constActions )
489 {
490 connect( action, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
491 featureMenu->addAction( action );
492 }
493 }
494 }
495
496 // back to layer level
497
498 // identify all action
499 if ( mAllowMultipleReturn && results.count() > 1 )
500 {
501 layerMenu->addSeparator();
502 QAction *allAction = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionIdentify.svg" ) ), tr( "%1 All (%2)" ).arg( mDefaultActionName ).arg( results.count() ), layerMenu );
503 allAction->setData( QVariant::fromValue<ActionData>( ActionData( layer ) ) );
504 connect( allAction, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
505 layerMenu->addAction( allAction );
506 }
507
508 // add custom/layer actions
509 const auto constLayerActions = layerActions;
510 for ( QgsMapLayerAction *mapLayerAction : constLayerActions )
511 {
512 QString title = mapLayerAction->text();
513 if ( mapLayerAction->targets().testFlag( Qgis::MapLayerActionTarget::MultipleFeatures ) )
514 title.append( QStringLiteral( " (%1)" ).arg( results.count() ) );
515 QAction *action = new QAction( mapLayerAction->icon(), title, layerMenu );
516 action->setData( QVariant::fromValue<ActionData>( ActionData( layer, mapLayerAction ) ) );
517 connect( action, &QAction::hovered, this, &QgsIdentifyMenu::handleMenuHover );
518 connect( action, &QAction::triggered, this, &QgsIdentifyMenu::triggerMapLayerAction );
519 layerMenu->addAction( action );
520 if ( separators.contains( mapLayerAction ) )
521 {
522 layerMenu->insertSeparator( action );
523 }
524 }
525}
526
527void QgsIdentifyMenu::triggerMapLayerAction()
528{
529 QAction *action = qobject_cast<QAction *>( sender() );
530 if ( !action )
531 return;
532 const QVariant varData = action->data();
533 if ( !varData.isValid() || !varData.canConvert<ActionData>() )
534 return;
535
536 ActionData actData = action->data().value<ActionData>();
537
538 if ( actData.mIsValid && actData.mMapLayerAction )
539 {
540 QgsMapLayerActionContext context;
541
542 // layer
543 if ( actData.mMapLayerAction->targets().testFlag( Qgis::MapLayerActionTarget::Layer ) )
544 {
546 actData.mMapLayerAction->triggerForLayer( actData.mLayer );
548 actData.mMapLayerAction->triggerForLayer( actData.mLayer, context );
549 }
550
551 // multiples features
552 if ( actData.mMapLayerAction->targets().testFlag( Qgis::MapLayerActionTarget::MultipleFeatures ) )
553 {
554 QList<QgsFeature> featureList;
555 const auto results { mLayerIdResults[actData.mLayer] };
556 for ( const QgsMapToolIdentify::IdentifyResult &result : results )
557 {
558 featureList << result.mFeature;
559 }
561 actData.mMapLayerAction->triggerForFeatures( actData.mLayer, featureList );
563 actData.mMapLayerAction->triggerForFeatures( actData.mLayer, featureList, context );
564 }
565
566 // single feature
567 if ( actData.mMapLayerAction->targets().testFlag( Qgis::MapLayerActionTarget::SingleFeature ) )
568 {
569 const auto results { mLayerIdResults[actData.mLayer] };
570 for ( const QgsMapToolIdentify::IdentifyResult &result : results )
571 {
572 if ( result.mFeature.id() == actData.mFeatureId )
573 {
575 actData.mMapLayerAction->triggerForFeature( actData.mLayer, result.mFeature );
577 actData.mMapLayerAction->triggerForFeature( actData.mLayer, result.mFeature, context );
578 return;
579 }
580 }
581 QgsDebugError( QStringLiteral( "Identify menu: could not retrieve feature for action %1" ).arg( action->text() ) );
582 }
583 }
584}
585
586
587QList<QgsMapToolIdentify::IdentifyResult> QgsIdentifyMenu::results( QAction *action, bool &externalAction )
588{
589 QList<QgsMapToolIdentify::IdentifyResult> idResults = QList<QgsMapToolIdentify::IdentifyResult>();
590
591 externalAction = false;
592
593 ActionData actData;
594 bool hasData = false;
595
596 if ( !action )
597 return idResults;
598
599 const QVariant varData = action->data();
600 if ( !varData.isValid() )
601 {
602 QgsDebugError( QStringLiteral( "Identify menu: could not retrieve results from menu entry (invalid data)" ) );
603 return idResults;
604 }
605
606 if ( varData.canConvert<ActionData>() )
607 {
608 actData = action->data().value<ActionData>();
609 if ( actData.mIsValid )
610 {
611 externalAction = actData.mIsExternalAction;
612 hasData = true;
613 }
614 }
615
616 if ( !hasData && varData.canConvert<QgsActionMenu::ActionData>() )
617 {
618 const QgsActionMenu::ActionData dataSrc = action->data().value<QgsActionMenu::ActionData>();
619 if ( dataSrc.actionType != Qgis::ActionType::Invalid )
620 {
621 externalAction = true;
622 actData = ActionData( dataSrc.mapLayer, dataSrc.featureId );
623 hasData = true;
624 }
625 }
626
627 if ( !hasData )
628 {
629 QgsDebugError( QStringLiteral( "Identify menu: could not retrieve results from menu entry (no data found)" ) );
630 return idResults;
631 }
632
633 // return all results
634 if ( actData.mAllResults )
635 {
636 // this means "All" action was triggered
637 QMapIterator<QgsMapLayer *, QList<QgsMapToolIdentify::IdentifyResult>> it( mLayerIdResults );
638 while ( it.hasNext() )
639 {
640 it.next();
641 idResults << it.value();
642 }
643 return idResults;
644 }
645
646 if ( !mLayerIdResults.contains( actData.mLayer ) )
647 {
648 QgsDebugError( QStringLiteral( "Identify menu: could not retrieve results from menu entry (layer not found)" ) );
649 return idResults;
650 }
651
652 if ( actData.mLevel == LayerLevel )
653 {
654 return mLayerIdResults[actData.mLayer];
655 }
656
657 if ( actData.mLevel == FeatureLevel )
658 {
659 const auto results { mLayerIdResults[actData.mLayer] };
660 for ( const QgsMapToolIdentify::IdentifyResult &res : results )
661 {
662 if ( res.mFeature.id() == actData.mFeatureId )
663 {
664 idResults << res;
665 return idResults;
666 }
667 }
668 }
669
670 QgsDebugError( QStringLiteral( "Identify menu: could not retrieve results from menu entry (don't know what happened')" ) );
671 return idResults;
672}
673
674void QgsIdentifyMenu::handleMenuHover()
675{
676 if ( !mCanvas )
677 return;
678
679 deleteRubberBands();
680
681 QAction *senderAction = qobject_cast<QAction *>( sender() );
682 if ( !senderAction )
683 return;
684
685 bool externalAction;
686 const QList<QgsMapToolIdentify::IdentifyResult> idResults = results( senderAction, externalAction );
687
688 const auto constIdResults = idResults;
689 for ( const QgsMapToolIdentify::IdentifyResult &result : constIdResults )
690 {
691 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( result.mLayer );
692 if ( !vl )
693 continue;
694
695 QgsHighlight *hl = new QgsHighlight( mCanvas, result.mFeature.geometry(), vl );
696 hl->applyDefaultStyle();
697 mRubberBands.append( hl );
698 connect( vl, &QObject::destroyed, this, &QgsIdentifyMenu::layerDestroyed );
699 }
700}
701
703{
704 highlight->applyDefaultStyle();
705}
706
707void QgsIdentifyMenu::deleteRubberBands()
708{
709 QList<QgsHighlight *>::const_iterator it = mRubberBands.constBegin();
710 for ( ; it != mRubberBands.constEnd(); ++it )
711 delete *it;
712 mRubberBands.clear();
713}
714
715void QgsIdentifyMenu::layerDestroyed()
716{
717 QList<QgsHighlight *>::iterator it = mRubberBands.begin();
718 while ( it != mRubberBands.end() )
719 {
720 if ( ( *it )->layer() == sender() )
721 {
722 delete *it;
723 it = mRubberBands.erase( it );
724 }
725 else
726 {
727 ++it;
728 }
729 }
730}
731
733{
734 mCustomActionRegistry.clear();
735}
736
738{
739 mExpressionContextScope = scope;
740}
741
743{
744 return mExpressionContextScope;
745}
QFlags< MapLayerActionTarget > MapLayerActionTargets
Map layer action targets.
Definition qgis.h:4640
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
Definition qgis.h:2198
@ Invalid
Invalid.
Definition qgis.h:4611
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:358
@ MultipleFeatures
Action targets multiple features from a layer.
Definition qgis.h:4628
@ Layer
Action targets a complete layer.
Definition qgis.h:4626
@ SingleFeature
Action targets a single feature from a layer.
Definition qgis.h:4627
@ Group
Composite group layer. Added in QGIS 3.24.
Definition qgis.h:198
@ Plugin
Plugin based layer.
Definition qgis.h:193
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
Definition qgis.h:199
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
Definition qgis.h:196
@ Vector
Vector layer.
Definition qgis.h:191
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:195
@ Mesh
Mesh layer. Added in QGIS 3.2.
Definition qgis.h:194
@ Raster
Raster layer.
Definition qgis.h:192
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
Definition qgis.h:197
@ Reverse
Reverse/inverse transform (from destination to source).
Definition qgis.h:2673
void setExpressionContextScope(const QgsExpressionContextScope &scope)
Sets an expression context scope used to resolve underlying actions.
void messageEmitted(const QString &message, Qgis::MessageLevel level=Qgis::MessageLevel::Info)
Emitted when a message should be shown to the user in the application message bar.
void setMode(QgsAttributeEditorContext::Mode mode)
Change the mode of the actions.
void messageDiscarded()
Emitted when the previous message from the tool should be cleared from the application message bar.
bool isEmpty() const
Returns true if the menu has no valid actions.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
@ IdentifyMode
Identify the feature.
Handles coordinate transforms between two coordinate systems.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const
Transforms a rectangle from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
Single scope for storing variables and functions for use within a QgsExpressionContext.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
Wraps a request for features to a vector layer (or directly its vector data provider).
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
static QgsMapLayerActionRegistry * mapLayerActionRegistry()
Returns the global map layer action registry, used for registering map layer actions.
Definition qgsgui.cpp:146
Highlights features on the map.
void applyDefaultStyle()
Applies the default style from the user settings to the highlight.
static QIcon iconForWkbType(Qgis::WkbType type)
Returns the icon for a vector layer whose geometry type is provided.
QList< QgsMapToolIdentify::IdentifyResult > exec(const QList< QgsMapToolIdentify::IdentifyResult > &idResults, QPoint pos)
exec
void setMaxLayerDisplay(int maxLayerDisplay)
Defines the maximum number of layers displayed in the menu (default is 10).
~QgsIdentifyMenu() override
QgsExpressionContextScope expressionContextScope() const
Returns an expression context scope used to resolve underlying actions.
void setMaxFeatureDisplay(int maxFeatureDisplay)
Defines the maximum number of features displayed in the menu for vector layers (default is 10).
QgsIdentifyMenu(QgsMapCanvas *canvas)
QgsIdentifyMenu is a menu to be used to choose within a list of QgsMapTool::IdentifyReults.
void messageDiscarded()
Emitted when the previous message from the tool should be cleared from the application message bar.
void closeEvent(QCloseEvent *e) override
static QList< QgsMapToolIdentify::IdentifyResult > findFeaturesOnCanvas(QgsMapMouseEvent *event, QgsMapCanvas *canvas, const QList< Qgis::GeometryType > &geometryTypes)
Searches for features on the map canvas, which are located at the specified event point.
static Q_DECL_DEPRECATED void styleHighlight(QgsHighlight *highlight)
Applies style from the settings to the highlight.
void removeCustomActions()
remove all custom actions from the menu to be built
void messageEmitted(const QString &message, Qgis::MessageLevel level=Qgis::MessageLevel::Info)
Emitted when a message should be shown to the user in the application message bar.
void setExpressionContextScope(const QgsExpressionContextScope &scope)
Sets an expression context scope used to resolve underlying actions.
Map canvas is a class for displaying all GIS data types on a canvas.
QList< QgsMapLayer * > layers(bool expandGroupLayers=false) const
Returns the list of layers shown within the map canvas.
const QgsMapToPixel * getCoordinateTransform()
Gets the current coordinate transform.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
Encapsulates the context in which a QgsMapLayerAction action is executed.
QList< QgsMapLayerAction * > mapLayerActions(QgsMapLayer *layer, Qgis::MapLayerActionTargets targets=Qgis::MapLayerActionTarget::AllActions, const QgsMapLayerActionContext &context=QgsMapLayerActionContext())
Returns the map layer actions which can run on the specified layer.
Base class for all map layer types.
Definition qgsmaplayer.h:80
QString name
Definition qgsmaplayer.h:84
Qgis::LayerType type
Definition qgsmaplayer.h:90
A mouse event which is the result of a user interaction with a QgsMapCanvas.
QgsCoordinateTransform layerTransform(const QgsMapLayer *layer) const
Returns the coordinate transform from layer's CRS to destination CRS.
QgsPointXY toMapCoordinates(int x, int y) const
Transforms device coordinates to map (world) coordinates.
static double searchRadiusMU(const QgsRenderContext &context)
Gets search radius in map units for given context.
Represents a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
A rectangle specified with double values.
Represents a vector layer which manages a vector based dataset.
Q_INVOKABLE Qgis::WkbType wkbType() const final
Returns the WKBType or WKBUnknown in case of error.
QString displayExpression
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:7170
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7169
#define QgsDebugError(str)
Definition qgslogger.h:57
Qgis::ActionType actionType