1 /***************************************************************************
2 qgsapplayertreeviewmenuprovider.cpp
3 ---------------------
4 begin : May 2014
5 copyright : (C) 2014 by Martin Dobias
6 email : wonder dot sk at gmail dot com
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 #include <QClipboard>
16
17 #include "qgsapplayertreeviewmenuprovider.h"
18
19 #include "qgisapp.h"
20 #include "qgsapplication.h"
21 #include "qgsclipboard.h"
22 #include "qgscolorwidgets.h"
23 #include "qgscolorschemeregistry.h"
24 #include "qgscolorswatchgrid.h"
25 #include "qgsgui.h"
26 #include "qgslayertree.h"
27 #include "qgslayertreemodel.h"
28 #include "qgslayertreemodellegendnode.h"
29 #include "qgslayertreeviewdefaultactions.h"
30 #include "qgsmapcanvas.h"
31 #include "qgsmaplayerstyleguiutils.h"
32 #include "qgsproject.h"
33 #include "qgsrasterlayer.h"
34 #include "qgsrenderer.h"
35 #include "qgssymbol.h"
36 #include "qgsstyle.h"
37 #include "qgsvectordataprovider.h"
38 #include "qgsvectorlayer.h"
39 #include "qgslayertreeregistrybridge.h"
40 #include "qgssymbolselectordialog.h"
41 #include "qgssinglesymbolrenderer.h"
42 #include "qgsmaplayerstylecategoriesmodel.h"
43 #include "qgssymbollayerutils.h"
44 #include "qgsxmlutils.h"
45 #include "qgsmessagebar.h"
46
47
QgsAppLayerTreeViewMenuProvider(QgsLayerTreeView * view,QgsMapCanvas * canvas)48 QgsAppLayerTreeViewMenuProvider::QgsAppLayerTreeViewMenuProvider( QgsLayerTreeView *view, QgsMapCanvas *canvas )
49 : mView( view )
50 , mCanvas( canvas )
51 {
52 }
53
createContextMenu()54 QMenu *QgsAppLayerTreeViewMenuProvider::createContextMenu()
55 {
56 QMenu *menu = new QMenu;
57
58 QgsLayerTreeViewDefaultActions *actions = mView->defaultActions();
59
60 QModelIndex idx = mView->currentIndex();
61 if ( !idx.isValid() )
62 {
63 // global menu
64 menu->addAction( actions->actionAddGroup( menu ) );
65 menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionExpandTree.svg" ) ), tr( "&Expand All" ), mView, &QgsLayerTreeView::expandAll );
66 menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionCollapseTree.svg" ) ), tr( "&Collapse All" ), mView, &QgsLayerTreeView::collapseAll );
67 menu->addSeparator();
68 if ( QgisApp::instance()->clipboard()->hasFormat( QGSCLIPBOARD_MAPLAYER_MIME ) )
69 {
70 QAction *actionPasteLayerOrGroup = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionEditPaste.svg" ) ), tr( "Paste Layer/Group" ), menu );
71 connect( actionPasteLayerOrGroup, &QAction::triggered, QgisApp::instance(), &QgisApp::pasteLayer );
72 menu->addAction( actionPasteLayerOrGroup );
73 }
74
75 // TODO: update drawing order
76 }
77 else if ( QgsLayerTreeNode *node = mView->layerTreeModel()->index2node( idx ) )
78 {
79 // layer or group selected
80 if ( QgsLayerTree::isGroup( node ) )
81 {
82 menu->addAction( actions->actionZoomToGroup( mCanvas, menu ) );
83
84 menu->addAction( tr( "Copy Group" ), QgisApp::instance(), &QgisApp::copyLayer );
85 if ( QgisApp::instance()->clipboard()->hasFormat( QGSCLIPBOARD_MAPLAYER_MIME ) )
86 {
87 QAction *actionPasteLayerOrGroup = new QAction( tr( "Paste Layer/Group" ), menu );
88 connect( actionPasteLayerOrGroup, &QAction::triggered, QgisApp::instance(), &QgisApp::pasteLayer );
89 menu->addAction( actionPasteLayerOrGroup );
90 }
91
92 menu->addAction( actions->actionRenameGroupOrLayer( menu ) );
93
94 menu->addSeparator();
95 menu->addAction( actions->actionAddGroup( menu ) );
96 QAction *removeAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionRemoveLayer.svg" ) ), tr( "&Remove Group…" ), QgisApp::instance(), &QgisApp::removeLayer );
97 removeAction->setEnabled( removeActionEnabled() );
98 menu->addSeparator();
99
100 menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSetCRS.png" ) ),
101 tr( "&Set Group CRS…" ), QgisApp::instance(), &QgisApp::legendGroupSetCrs );
102 menu->addAction( tr( "&Set Group WMS Data…" ), QgisApp::instance(), &QgisApp::legendGroupSetWmsData );
103
104 menu->addSeparator();
105
106 menu->addAction( actions->actionMutuallyExclusiveGroup( menu ) );
107
108 if ( QAction *checkAll = actions->actionCheckAndAllChildren( menu ) )
109 menu->addAction( checkAll );
110
111 if ( QAction *unCheckAll = actions->actionUncheckAndAllChildren( menu ) )
112 menu->addAction( unCheckAll );
113
114 if ( !( mView->selectedNodes( true ).count() == 1 && idx.row() == 0 ) )
115 {
116 menu->addAction( actions->actionMoveToTop( menu ) );
117 }
118
119 if ( !( mView->selectedNodes( true ).count() == 1 && idx.row() == idx.model()->rowCount() - 1 ) )
120 {
121 menu->addAction( actions->actionMoveToBottom( menu ) );
122 }
123
124 menu->addSeparator();
125
126 if ( mView->selectedNodes( true ).count() >= 2 )
127 menu->addAction( actions->actionGroupSelected( menu ) );
128
129 if ( QgisApp::instance()->clipboard()->hasFormat( QGSCLIPBOARD_STYLE_MIME ) )
130 {
131 menu->addAction( tr( "Paste Style" ), QgisApp::instance(), &QgisApp::applyStyleToGroup );
132 }
133
134 menu->addSeparator();
135
136 QMenu *menuExportGroup = new QMenu( tr( "Export" ), menu );
137 QAction *actionSaveAsDefinitionGroup = new QAction( tr( "Save as Layer Definition File…" ), menuExportGroup );
138 connect( actionSaveAsDefinitionGroup, &QAction::triggered, QgisApp::instance(), &QgisApp::saveAsLayerDefinition );
139 menuExportGroup->addAction( actionSaveAsDefinitionGroup );
140
141 menu->addMenu( menuExportGroup );
142 }
143 else if ( QgsLayerTree::isLayer( node ) )
144 {
145 QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
146 QgsRasterLayer *rlayer = qobject_cast<QgsRasterLayer *>( layer );
147 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
148
149 if ( layer && layer->isSpatial() )
150 {
151 QAction *zoomToLayer = actions->actionZoomToLayer( mCanvas, menu );
152 zoomToLayer->setEnabled( layer->isValid() );
153 menu->addAction( zoomToLayer );
154 if ( vlayer )
155 {
156 QAction *actionZoomSelected = actions->actionZoomToSelection( mCanvas, menu );
157 actionZoomSelected->setEnabled( vlayer->isValid() && !vlayer->selectedFeatureIds().isEmpty() );
158 menu->addAction( actionZoomSelected );
159 }
160 menu->addAction( actions->actionShowInOverview( menu ) );
161 }
162
163 if ( vlayer )
164 {
165 QAction *showFeatureCount = actions->actionShowFeatureCount( menu );
166 menu->addAction( showFeatureCount );
167 showFeatureCount->setEnabled( vlayer->isValid() );
168 }
169
170 QAction *actionCopyLayer = new QAction( tr( "Copy Layer" ), menu );
171 connect( actionCopyLayer, &QAction::triggered, QgisApp::instance(), &QgisApp::copyLayer );
172 menu->addAction( actionCopyLayer );
173
174 menu->addAction( actions->actionRenameGroupOrLayer( menu ) );
175
176 if ( rlayer )
177 {
178 QAction *zoomToNative = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionZoomActual.svg" ) ), tr( "&Zoom to Native Resolution (100%)" ), QgisApp::instance(), &QgisApp::legendLayerZoomNative );
179 zoomToNative->setEnabled( rlayer->isValid() );
180
181 if ( rlayer->rasterType() != QgsRasterLayer::Palette )
182 {
183 QAction *stretch = menu->addAction( tr( "&Stretch Using Current Extent" ), QgisApp::instance(), &QgisApp::legendLayerStretchUsingCurrentExtent );
184 stretch->setEnabled( rlayer->isValid() );
185 }
186 }
187
188 addCustomLayerActions( menu, layer );
189 if ( layer && layer->type() == QgsMapLayerType::VectorLayer && static_cast<QgsVectorLayer *>( layer )->providerType() == QLatin1String( "virtual" ) )
190 {
191 menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddVirtualLayer.svg" ) ), tr( "Edit Virtual Layer…" ), QgisApp::instance(), &QgisApp::addVirtualLayer );
192 }
193
194 menu->addSeparator();
195
196 // duplicate layer
197 QAction *duplicateLayersAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDuplicateLayer.svg" ) ), tr( "&Duplicate Layer" ), QgisApp::instance(), [] { QgisApp::instance()->duplicateLayers(); } );
198 QAction *removeAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionRemoveLayer.svg" ) ), tr( "&Remove Layer…" ), QgisApp::instance(), &QgisApp::removeLayer );
199 removeAction->setEnabled( removeActionEnabled() );
200
201 menu->addSeparator();
202
203 if ( node->parent() != mView->layerTreeModel()->rootGroup() )
204 menu->addAction( actions->actionMoveOutOfGroup( menu ) );
205
206 if ( !( mView->selectedNodes( true ).count() == 1 && idx.row() == 0 ) )
207 {
208 menu->addAction( actions->actionMoveToTop( menu ) );
209 }
210
211 if ( !( mView->selectedNodes( true ).count() == 1 && idx.row() == idx.model()->rowCount() - 1 ) )
212 {
213 menu->addAction( actions->actionMoveToBottom( menu ) );
214 }
215
216 QAction *checkAll = actions->actionCheckAndAllParents( menu );
217 if ( checkAll )
218 menu->addAction( checkAll );
219
220 if ( mView->selectedNodes( true ).count() >= 2 )
221 menu->addAction( actions->actionGroupSelected( menu ) );
222
223 menu->addSeparator();
224
225 if ( vlayer )
226 {
227 QAction *toggleEditingAction = QgisApp::instance()->actionToggleEditing();
228 QAction *saveLayerEditsAction = QgisApp::instance()->actionSaveActiveLayerEdits();
229 QAction *allEditsAction = QgisApp::instance()->actionAllEdits();
230
231 // attribute table
232 QgsSettings settings;
233 QgsAttributeTableFilterModel::FilterMode initialMode = settings.enumValue( QStringLiteral( "qgis/attributeTableBehavior" ), QgsAttributeTableFilterModel::ShowAll );
234 const auto lambdaOpenAttributeTable = [ = ] { QgisApp::instance()->attributeTable( initialMode ); };
235 QAction *attributeTableAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionOpenTable.svg" ) ), tr( "&Open Attribute Table" ),
236 QgisApp::instance(), lambdaOpenAttributeTable );
237 attributeTableAction->setEnabled( vlayer->isValid() );
238
239 // allow editing
240 const QgsVectorDataProvider *provider = vlayer->dataProvider();
241 if ( provider
242 && ( provider->capabilities() & QgsVectorDataProvider::EditingCapabilities )
243 && !vlayer->readOnly() )
244 {
245 if ( toggleEditingAction )
246 {
247 menu->addAction( toggleEditingAction );
248 toggleEditingAction->setChecked( vlayer->isEditable() );
249 toggleEditingAction->setEnabled( vlayer->isValid() );
250 }
251 if ( saveLayerEditsAction && vlayer->isModified() )
252 {
253 menu->addAction( saveLayerEditsAction );
254 }
255 }
256
257 if ( allEditsAction->isEnabled() )
258 menu->addAction( allEditsAction );
259
260 if ( provider && provider->supportsSubsetString() )
261 {
262 QAction *action = menu->addAction( tr( "&Filter…" ), QgisApp::instance(), qgis::overload<>::of( &QgisApp::layerSubsetString ) );
263 action->setEnabled( !vlayer->isEditable() );
264 }
265 }
266
267 if ( rlayer &&
268 rlayer->dataProvider() &&
269 rlayer->dataProvider()->supportsSubsetString() )
270 {
271 menu->addAction( tr( "&Filter…" ), QgisApp::instance(), qgis::overload<>::of( &QgisApp::layerSubsetString ) );
272 }
273
274 // change data source is only supported for vectors and rasters
275 if ( vlayer || rlayer )
276 {
277
278 QAction *a = new QAction( layer->isValid() ? tr( "Change Data Source…" ) : tr( "Repair Data Source…" ), menu );
279 if ( !layer->isValid() )
280 a->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconWarning.svg" ) ) );
281 // Disable when layer is editable
282 if ( layer->isEditable() )
283 {
284 a->setEnabled( false );
285 }
286 else
287 {
288 connect( a, &QAction::triggered, this, [ = ]
289 {
290 QgisApp::instance()->changeDataSource( layer );
291 } );
292 }
293 menu->addAction( a );
294 }
295
296 // actions on the selection
297 if ( vlayer && vlayer->selectedFeatureCount() > 0 )
298 {
299 int selectionCount = vlayer->selectedFeatureCount();
300 QgsMapLayerAction::Target target;
301 if ( selectionCount == 1 )
302 target = QgsMapLayerAction::Target::SingleFeature;
303 else
304 target = QgsMapLayerAction::Target::MultipleFeatures;
305
306 const QList<QgsMapLayerAction *> constRegisteredActions = QgsGui::mapLayerActionRegistry()->mapLayerActions( vlayer, target );
307 if ( !constRegisteredActions.isEmpty() )
308 {
309 QMenu *actionMenu = menu->addMenu( tr( "Actions on Selection (%1)" ).arg( selectionCount ) );
310 for ( QgsMapLayerAction *action : constRegisteredActions )
311 {
312 if ( target == QgsMapLayerAction::Target::SingleFeature )
313 {
314 actionMenu->addAction( action->text(), action, [ = ]() { action->triggerForFeature( vlayer, vlayer->selectedFeatures().at( 0 ) ); } );
315 }
316 else if ( target == QgsMapLayerAction::Target::MultipleFeatures )
317 {
318 actionMenu->addAction( action->text(), action, [ = ]() {action->triggerForFeatures( vlayer, vlayer->selectedFeatures() );} );
319 }
320 }
321 }
322 }
323
324 menu->addSeparator();
325
326 if ( layer && layer->isSpatial() )
327 {
328 // set layer scale visibility
329 menu->addAction( tr( "&Set Layer Scale Visibility…" ), QgisApp::instance(), &QgisApp::setLayerScaleVisibility );
330
331 if ( !layer->isInScaleRange( mCanvas->scale() ) )
332 menu->addAction( tr( "Zoom to &Visible Scale" ), QgisApp::instance(), &QgisApp::zoomToLayerScale );
333
334 QMenu *menuSetCRS = new QMenu( tr( "Layer CRS" ), menu );
335
336 const QList<QgsLayerTreeNode *> selectedNodes = mView->selectedNodes();
337 QgsCoordinateReferenceSystem layerCrs;
338 bool firstLayer = true;
339 bool allSameCrs = true;
340 for ( QgsLayerTreeNode *node : selectedNodes )
341 {
342 if ( QgsLayerTree::isLayer( node ) )
343 {
344 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
345 if ( nodeLayer->layer() )
346 {
347 if ( firstLayer )
348 {
349 layerCrs = nodeLayer->layer()->crs();
350 firstLayer = false;
351 }
352 else if ( nodeLayer->layer()->crs() != layerCrs )
353 {
354 allSameCrs = false;
355 break;
356 }
357 }
358 }
359 }
360
361 QAction *actionCurrentCrs = new QAction( !allSameCrs ? tr( "Mixed CRS" )
362 : layer->crs().isValid() ? layer->crs().userFriendlyIdentifier()
363 : tr( "No CRS" ), menuSetCRS );
364 actionCurrentCrs->setEnabled( false );
365 menuSetCRS->addAction( actionCurrentCrs );
366
367 if ( allSameCrs && layerCrs.isValid() )
368 {
369 // assign layer crs to project
370 QAction *actionSetProjectCrs = new QAction( tr( "Set &Project CRS from Layer" ), menuSetCRS );
371 connect( actionSetProjectCrs, &QAction::triggered, QgisApp::instance(), &QgisApp::setProjectCrsFromLayer );
372 menuSetCRS->addAction( actionSetProjectCrs );
373 }
374
375 const QList< QgsCoordinateReferenceSystem> recentProjections = QgsCoordinateReferenceSystem::recentCoordinateReferenceSystems();
376 if ( !recentProjections.isEmpty() )
377 {
378 menuSetCRS->addSeparator();
379 int i = 0;
380 for ( const QgsCoordinateReferenceSystem &crs : recentProjections )
381 {
382 if ( crs == layer->crs() )
383 continue;
384
385 QAction *action = menuSetCRS->addAction( tr( "Set to %1" ).arg( crs.userFriendlyIdentifier( QgsCoordinateReferenceSystem::ShortString ) ) );
386 connect( action, &QAction::triggered, this, [ = ]
387 {
388 setLayerCrs( crs );
389 } );
390
391 i++;
392 if ( i == 5 )
393 break;
394 }
395 }
396
397 // set layer crs
398 menuSetCRS->addSeparator();
399 QAction *actionSetLayerCrs = new QAction( tr( "Set Layer CRS…" ), menuSetCRS );
400 connect( actionSetLayerCrs, &QAction::triggered, QgisApp::instance(), &QgisApp::setLayerCrs );
401 menuSetCRS->addAction( actionSetLayerCrs );
402
403 menu->addMenu( menuSetCRS );
404 }
405
406 menu->addSeparator();
407
408 if ( vlayer )
409 {
410 if ( vlayer->isTemporary() )
411 {
412 QAction *actionMakePermanent = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "mActionFileSave.svg" ) ), tr( "Make Permanent…" ), menu );
413 connect( actionMakePermanent, &QAction::triggered, QgisApp::instance(), [ = ] { QgisApp::instance()->makeMemoryLayerPermanent( vlayer ); } );
414 menu->addAction( actionMakePermanent );
415 }
416 // save as vector file
417 QMenu *menuExportVector = new QMenu( tr( "Export" ), menu );
418 QAction *actionSaveAs = new QAction( tr( "Save Features As…" ), menuExportVector );
419 connect( actionSaveAs, &QAction::triggered, QgisApp::instance(), [ = ] { QgisApp::instance()->saveAsFile(); } );
420 actionSaveAs->setEnabled( vlayer->isValid() );
421 menuExportVector->addAction( actionSaveAs );
422 QAction *actionSaveSelectedFeaturesAs = new QAction( tr( "Save Selected Features As…" ), menuExportVector );
423 connect( actionSaveSelectedFeaturesAs, &QAction::triggered, QgisApp::instance(), [ = ] { QgisApp::instance()->saveAsFile( nullptr, true ); } );
424 actionSaveSelectedFeaturesAs->setEnabled( vlayer->isValid() && vlayer->selectedFeatureCount() > 0 );
425 menuExportVector->addAction( actionSaveSelectedFeaturesAs );
426 QAction *actionSaveAsDefinitionLayer = new QAction( tr( "Save as Layer Definition File…" ), menuExportVector );
427 connect( actionSaveAsDefinitionLayer, &QAction::triggered, QgisApp::instance(), &QgisApp::saveAsLayerDefinition );
428 menuExportVector->addAction( actionSaveAsDefinitionLayer );
429 if ( vlayer->isSpatial() )
430 {
431 QAction *actionSaveStyle = new QAction( tr( "Save as QGIS Layer Style File…" ), menuExportVector );
432 connect( actionSaveStyle, &QAction::triggered, QgisApp::instance(), [ = ] { QgisApp::instance()->saveStyleFile(); } );
433 menuExportVector->addAction( actionSaveStyle );
434 }
435 menu->addMenu( menuExportVector );
436 }
437 else if ( rlayer )
438 {
439 QMenu *menuExportRaster = new QMenu( tr( "Export" ), menu );
440 QAction *actionSaveAs = new QAction( tr( "Save As…" ), menuExportRaster );
441 QAction *actionSaveAsDefinitionLayer = new QAction( tr( "Save as Layer Definition File…" ), menuExportRaster );
442 QAction *actionSaveStyle = new QAction( tr( "Save as QGIS Layer Style File…" ), menuExportRaster );
443 connect( actionSaveAs, &QAction::triggered, QgisApp::instance(), [ = ] { QgisApp::instance()->saveAsFile(); } );
444 menuExportRaster->addAction( actionSaveAs );
445 actionSaveAs->setEnabled( rlayer->isValid() );
446 connect( actionSaveAsDefinitionLayer, &QAction::triggered, QgisApp::instance(), &QgisApp::saveAsLayerDefinition );
447 menuExportRaster->addAction( actionSaveAsDefinitionLayer );
448 connect( actionSaveStyle, &QAction::triggered, QgisApp::instance(), [ = ] { QgisApp::instance()->saveStyleFile(); } );
449 menuExportRaster->addAction( actionSaveStyle );
450 menu->addMenu( menuExportRaster );
451 }
452 else if ( layer && layer->type() == QgsMapLayerType::PluginLayer && mView->selectedLayerNodes().count() == 1 )
453 {
454 // disable duplication of plugin layers
455 duplicateLayersAction->setEnabled( false );
456 }
457
458 menu->addSeparator();
459
460 // style-related actions
461 if ( layer && mView->selectedLayerNodes().count() == 1 )
462 {
463 menu->addSeparator();
464 QMenu *menuStyleManager = new QMenu( tr( "Styles" ), menu );
465
466 QgisApp *app = QgisApp::instance();
467 if ( layer->type() == QgsMapLayerType::VectorLayer )
468 {
469 QMenu *copyStyleMenu = menuStyleManager->addMenu( tr( "Copy Style" ) );
470 copyStyleMenu->setToolTipsVisible( true );
471 QgsMapLayerStyleCategoriesModel *model = new QgsMapLayerStyleCategoriesModel( layer->type(), copyStyleMenu );
472 model->setShowAllCategories( true );
473 for ( int row = 0; row < model->rowCount(); ++row )
474 {
475 QModelIndex index = model->index( row, 0 );
476 QgsMapLayer::StyleCategory category = model->data( index, Qt::UserRole ).value<QgsMapLayer::StyleCategory>();
477 QString name = model->data( index, Qt::DisplayRole ).toString();
478 QString tooltip = model->data( index, Qt::ToolTipRole ).toString();
479 QIcon icon = model->data( index, Qt::DecorationRole ).value<QIcon>();
480 QAction *copyAction = new QAction( icon, name, copyStyleMenu );
481 copyAction->setToolTip( tooltip );
482 connect( copyAction, &QAction::triggered, this, [ = ]() {app->copyStyle( layer, category );} );
483 copyStyleMenu->addAction( copyAction );
484 if ( category == QgsMapLayer::AllStyleCategories )
485 copyStyleMenu->addSeparator();
486 }
487 }
488 else
489 {
490 menuStyleManager->addAction( tr( "Copy Style" ), app, [ = ] { app->copyStyle(); } );
491 }
492
493 if ( layer && app->clipboard()->hasFormat( QGSCLIPBOARD_STYLE_MIME ) )
494 {
495 if ( layer->type() == QgsMapLayerType::VectorLayer )
496 {
497 QDomDocument doc( QStringLiteral( "qgis" ) );
498 QString errorMsg;
499 int errorLine, errorColumn;
500 if ( doc.setContent( app->clipboard()->data( QGSCLIPBOARD_STYLE_MIME ), false, &errorMsg, &errorLine, &errorColumn ) )
501 {
502 QDomElement myRoot = doc.firstChildElement( QStringLiteral( "qgis" ) );
503 if ( !myRoot.isNull() )
504 {
505 QMenu *pasteStyleMenu = menuStyleManager->addMenu( tr( "Paste Style" ) );
506 pasteStyleMenu->setToolTipsVisible( true );
507
508 QgsMapLayer::StyleCategories sourceCategories = QgsXmlUtils::readFlagAttribute( myRoot, QStringLiteral( "styleCategories" ), QgsMapLayer::AllStyleCategories );
509
510 QgsMapLayerStyleCategoriesModel *model = new QgsMapLayerStyleCategoriesModel( layer->type(), pasteStyleMenu );
511 model->setShowAllCategories( true );
512 for ( int row = 0; row < model->rowCount(); ++row )
513 {
514 QModelIndex index = model->index( row, 0 );
515 QgsMapLayer::StyleCategory category = model->data( index, Qt::UserRole ).value<QgsMapLayer::StyleCategory>();
516 QString name = model->data( index, Qt::DisplayRole ).toString();
517 QString tooltip = model->data( index, Qt::ToolTipRole ).toString();
518 QIcon icon = model->data( index, Qt::DecorationRole ).value<QIcon>();
519 QAction *pasteAction = new QAction( icon, name, pasteStyleMenu );
520 pasteAction->setToolTip( tooltip );
521 connect( pasteAction, &QAction::triggered, this, [ = ]() {app->pasteStyle( layer, category );} );
522 pasteStyleMenu->addAction( pasteAction );
523 if ( category == QgsMapLayer::AllStyleCategories )
524 pasteStyleMenu->addSeparator();
525 else
526 pasteAction->setEnabled( sourceCategories.testFlag( category ) );
527 }
528 }
529 }
530 }
531 else
532 {
533 menuStyleManager->addAction( tr( "Paste Style" ), app, [ = ] { app->pasteStyle(); } );
534 }
535 }
536
537 menuStyleManager->addSeparator();
538 QgsMapLayerStyleGuiUtils::instance()->addStyleManagerActions( menuStyleManager, layer );
539
540 if ( vlayer )
541 {
542 const QgsSingleSymbolRenderer *singleRenderer = dynamic_cast< const QgsSingleSymbolRenderer * >( vlayer->renderer() );
543 if ( !singleRenderer && vlayer->renderer() && vlayer->renderer()->embeddedRenderer() )
544 {
545 singleRenderer = dynamic_cast< const QgsSingleSymbolRenderer * >( vlayer->renderer()->embeddedRenderer() );
546 }
547 if ( singleRenderer && singleRenderer->symbol() )
548 {
549 //single symbol renderer, so add set color/edit symbol actions
550 menuStyleManager->addSeparator();
551 QgsColorWheel *colorWheel = new QgsColorWheel( menuStyleManager );
552 colorWheel->setColor( singleRenderer->symbol()->color() );
553 QgsColorWidgetAction *colorAction = new QgsColorWidgetAction( colorWheel, menuStyleManager, menuStyleManager );
554 colorAction->setDismissOnColorSelection( false );
555 connect( colorAction, &QgsColorWidgetAction::colorChanged, this, &QgsAppLayerTreeViewMenuProvider::setVectorSymbolColor );
556 //store the layer id in action, so we can later retrieve the corresponding layer
557 colorAction->setProperty( "layerId", vlayer->id() );
558 menuStyleManager->addAction( colorAction );
559
560 //add recent colors action
561 QList<QgsRecentColorScheme *> recentSchemes;
562 QgsApplication::colorSchemeRegistry()->schemes( recentSchemes );
563 if ( !recentSchemes.isEmpty() )
564 {
565 QgsColorSwatchGridAction *recentColorAction = new QgsColorSwatchGridAction( recentSchemes.at( 0 ), menuStyleManager, QStringLiteral( "symbology" ), menuStyleManager );
566 recentColorAction->setProperty( "layerId", vlayer->id() );
567 recentColorAction->setDismissOnColorSelection( false );
568 menuStyleManager->addAction( recentColorAction );
569 connect( recentColorAction, &QgsColorSwatchGridAction::colorChanged, this, &QgsAppLayerTreeViewMenuProvider::setVectorSymbolColor );
570 }
571
572 menuStyleManager->addSeparator();
573 const QString layerId = vlayer->id();
574
575 QAction *editSymbolAction = new QAction( tr( "Edit Symbol…" ), menuStyleManager );
576 connect( editSymbolAction, &QAction::triggered, this, [this, layerId]
577 {
578 editVectorSymbol( layerId );
579 } );
580 menuStyleManager->addAction( editSymbolAction );
581
582 QAction *copySymbolAction = new QAction( tr( "Copy Symbol" ), menuStyleManager );
583 connect( copySymbolAction, &QAction::triggered, this, [this, layerId]
584 {
585 copyVectorSymbol( layerId );
586 } );
587 menuStyleManager->addAction( copySymbolAction );
588
589 bool enablePaste = false;
590 std::unique_ptr< QgsSymbol > tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
591 if ( tempSymbol )
592 enablePaste = true;
593
594 QAction *pasteSymbolAction = new QAction( tr( "Paste Symbol" ), menuStyleManager );
595 connect( pasteSymbolAction, &QAction::triggered, this, [this, layerId]
596 {
597 pasteVectorSymbol( layerId );
598 } );
599 pasteSymbolAction->setEnabled( enablePaste );
600 menuStyleManager->addAction( pasteSymbolAction );
601 }
602 }
603
604 menu->addMenu( menuStyleManager );
605 }
606 else
607 {
608 if ( QgisApp::instance()->clipboard()->hasFormat( QGSCLIPBOARD_STYLE_MIME ) )
609 {
610 menu->addAction( tr( "Paste Style" ), QgisApp::instance(), &QgisApp::applyStyleToGroup );
611 }
612 }
613
614 if ( layer && QgsProject::instance()->layerIsEmbedded( layer->id() ).isEmpty() )
615 menu->addAction( tr( "&Properties…" ), QgisApp::instance(), &QgisApp::layerProperties );
616 }
617 }
618 else if ( QgsLayerTreeModelLegendNode *node = mView->layerTreeModel()->index2legendNode( idx ) )
619 {
620 if ( QgsSymbolLegendNode *symbolNode = qobject_cast< QgsSymbolLegendNode * >( node ) )
621 {
622 // symbology item
623 if ( symbolNode->flags() & Qt::ItemIsUserCheckable )
624 {
625 menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionToggleAllLayers.svg" ) ), tr( "&Toggle Items" ),
626 symbolNode, &QgsSymbolLegendNode::toggleAllItems );
627 menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionShowAllLayers.svg" ) ), tr( "&Show All Items" ),
628 symbolNode, &QgsSymbolLegendNode::checkAllItems );
629 menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionHideAllLayers.svg" ) ), tr( "&Hide All Items" ),
630 symbolNode, &QgsSymbolLegendNode::uncheckAllItems );
631 menu->addSeparator();
632 }
633
634 if ( symbolNode->symbol() )
635 {
636 QgsColorWheel *colorWheel = new QgsColorWheel( menu );
637 colorWheel->setColor( symbolNode->symbol()->color() );
638 QgsColorWidgetAction *colorAction = new QgsColorWidgetAction( colorWheel, menu, menu );
639 colorAction->setDismissOnColorSelection( false );
640 connect( colorAction, &QgsColorWidgetAction::colorChanged, this, &QgsAppLayerTreeViewMenuProvider::setSymbolLegendNodeColor );
641 //store the layer id and rule key in action, so we can later retrieve the corresponding
642 //legend node, if it still exists
643 colorAction->setProperty( "layerId", symbolNode->layerNode()->layerId() );
644 colorAction->setProperty( "ruleKey", symbolNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString() );
645 menu->addAction( colorAction );
646
647 //add recent colors action
648 QList<QgsRecentColorScheme *> recentSchemes;
649 QgsApplication::colorSchemeRegistry()->schemes( recentSchemes );
650 if ( !recentSchemes.isEmpty() )
651 {
652 QgsColorSwatchGridAction *recentColorAction = new QgsColorSwatchGridAction( recentSchemes.at( 0 ), menu, QStringLiteral( "symbology" ), menu );
653 recentColorAction->setProperty( "layerId", symbolNode->layerNode()->layerId() );
654 recentColorAction->setProperty( "ruleKey", symbolNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString() );
655 recentColorAction->setDismissOnColorSelection( false );
656 menu->addAction( recentColorAction );
657 connect( recentColorAction, &QgsColorSwatchGridAction::colorChanged, this, &QgsAppLayerTreeViewMenuProvider::setSymbolLegendNodeColor );
658 }
659
660 menu->addSeparator();
661 }
662
663 const QString layerId = symbolNode->layerNode()->layerId();
664 const QString ruleKey = symbolNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString();
665
666 QAction *editSymbolAction = new QAction( tr( "Edit Symbol…" ), menu );
667 connect( editSymbolAction, &QAction::triggered, this, [this, layerId, ruleKey ]
668 {
669 editSymbolLegendNodeSymbol( layerId, ruleKey );
670 } );
671 menu->addAction( editSymbolAction );
672
673 QAction *copySymbolAction = new QAction( tr( "Copy Symbol" ), menu );
674 connect( copySymbolAction, &QAction::triggered, this, [this, layerId, ruleKey ]
675 {
676 copySymbolLegendNodeSymbol( layerId, ruleKey );
677 } );
678 menu->addAction( copySymbolAction );
679
680 bool enablePaste = false;
681 std::unique_ptr< QgsSymbol > tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
682 if ( tempSymbol )
683 enablePaste = true;
684
685 QAction *pasteSymbolAction = new QAction( tr( "Paste Symbol" ), menu );
686 connect( pasteSymbolAction, &QAction::triggered, this, [this, layerId, ruleKey]
687 {
688 pasteSymbolLegendNodeSymbol( layerId, ruleKey );
689 } );
690 pasteSymbolAction->setEnabled( enablePaste );
691 menu->addAction( pasteSymbolAction );
692 }
693 }
694
695 return menu;
696 }
697
addLegendLayerAction(QAction * action,const QString & menu,QgsMapLayerType type,bool allLayers)698 void QgsAppLayerTreeViewMenuProvider::addLegendLayerAction( QAction *action, const QString &menu,
699 QgsMapLayerType type, bool allLayers )
700 {
701 mLegendLayerActionMap[type].append( LegendLayerAction( action, menu, allLayers ) );
702 }
703
removeLegendLayerAction(QAction * action)704 bool QgsAppLayerTreeViewMenuProvider::removeLegendLayerAction( QAction *action )
705 {
706 QMap< QgsMapLayerType, QList< LegendLayerAction > >::iterator it;
707 for ( it = mLegendLayerActionMap.begin();
708 it != mLegendLayerActionMap.end(); ++it )
709 {
710 for ( int i = 0; i < it->count(); i++ )
711 {
712 if ( ( *it )[i].action == action )
713 {
714 ( *it ).removeAt( i );
715 return true;
716 }
717 }
718 }
719 return false;
720 }
721
addLegendLayerActionForLayer(QAction * action,QgsMapLayer * layer)722 void QgsAppLayerTreeViewMenuProvider::addLegendLayerActionForLayer( QAction *action, QgsMapLayer *layer )
723 {
724 if ( !action || !layer )
725 return;
726
727 legendLayerActions( layer->type() );
728 if ( !mLegendLayerActionMap.contains( layer->type() ) )
729 return;
730
731 QMap< QgsMapLayerType, QList< LegendLayerAction > >::iterator it
732 = mLegendLayerActionMap.find( layer->type() );
733 for ( int i = 0; i < it->count(); i++ )
734 {
735 if ( ( *it )[i].action == action )
736 {
737 ( *it )[i].layers.append( layer );
738 return;
739 }
740 }
741 }
742
removeLegendLayerActionsForLayer(QgsMapLayer * layer)743 void QgsAppLayerTreeViewMenuProvider::removeLegendLayerActionsForLayer( QgsMapLayer *layer )
744 {
745 if ( ! layer || ! mLegendLayerActionMap.contains( layer->type() ) )
746 return;
747
748 QMap< QgsMapLayerType, QList< LegendLayerAction > >::iterator it
749 = mLegendLayerActionMap.find( layer->type() );
750 for ( int i = 0; i < it->count(); i++ )
751 {
752 ( *it )[i].layers.removeAll( layer );
753 }
754 }
755
legendLayerActions(QgsMapLayerType type) const756 QList< LegendLayerAction > QgsAppLayerTreeViewMenuProvider::legendLayerActions( QgsMapLayerType type ) const
757 {
758 #ifdef QGISDEBUG
759 if ( mLegendLayerActionMap.contains( type ) )
760 {
761 QgsDebugMsgLevel( QStringLiteral( "legendLayerActions for layers of type %1:" ).arg( static_cast<int>( type ) ), 2 );
762
763 const auto legendLayerActions { mLegendLayerActionMap.value( type ) };
764 for ( const LegendLayerAction &lyrAction : legendLayerActions )
765 {
766 Q_UNUSED( lyrAction )
767 QgsDebugMsgLevel( QStringLiteral( "%1/%2 - %3 layers" ).arg( lyrAction.menu, lyrAction.action->text() ).arg( lyrAction.layers.count() ), 2 );
768 }
769 }
770 #endif
771
772 return mLegendLayerActionMap.contains( type ) ? mLegendLayerActionMap.value( type ) : QList< LegendLayerAction >();
773 }
774
addCustomLayerActions(QMenu * menu,QgsMapLayer * layer)775 void QgsAppLayerTreeViewMenuProvider::addCustomLayerActions( QMenu *menu, QgsMapLayer *layer )
776 {
777 if ( !layer )
778 return;
779
780 // add custom layer actions - should this go at end?
781 QList< LegendLayerAction > lyrActions = legendLayerActions( layer->type() );
782
783 if ( ! lyrActions.isEmpty() )
784 {
785 menu->addSeparator();
786 QList<QMenu *> menus;
787 for ( int i = 0; i < lyrActions.count(); i++ )
788 {
789 if ( lyrActions[i].allLayers || lyrActions[i].layers.contains( layer ) )
790 {
791 if ( lyrActions[i].menu.isEmpty() )
792 {
793 menu->addAction( lyrActions[i].action );
794 }
795 else
796 {
797 // find or create menu for given menu name
798 // adapted from QgisApp::getPluginMenu( QString menuName )
799 QString menuName = lyrActions[i].menu;
800 #ifdef Q_OS_MAC
801 // Mac doesn't have '&' keyboard shortcuts.
802 menuName.remove( QChar( '&' ) );
803 #endif
804 QAction *before = nullptr;
805 QMenu *newMenu = nullptr;
806 QString dst = menuName;
807 dst.remove( QChar( '&' ) );
808 const auto constMenus = menus;
809 for ( QMenu *menu : constMenus )
810 {
811 QString src = menu->title();
812 src.remove( QChar( '&' ) );
813 int comp = dst.localeAwareCompare( src );
814 if ( comp < 0 )
815 {
816 // Add item before this one
817 before = menu->menuAction();
818 break;
819 }
820 else if ( comp == 0 )
821 {
822 // Plugin menu item already exists
823 newMenu = menu;
824 break;
825 }
826 }
827 if ( ! newMenu )
828 {
829 // It doesn't exist, so create
830 newMenu = new QMenu( menuName );
831 menus.append( newMenu );
832 // Where to put it? - we worked that out above...
833 menu->insertMenu( before, newMenu );
834 }
835 // QMenu* menu = getMenu( lyrActions[i].menu, &beforeSep, &afterSep, &menu );
836 newMenu->addAction( lyrActions[i].action );
837 }
838 }
839 }
840 menu->addSeparator();
841 }
842 }
843
editVectorSymbol(const QString & layerId)844 void QgsAppLayerTreeViewMenuProvider::editVectorSymbol( const QString &layerId )
845 {
846 QgsVectorLayer *layer = QgsProject::instance()->mapLayer<QgsVectorLayer *>( layerId );
847 if ( !layer )
848 return;
849
850 QgsSingleSymbolRenderer *singleRenderer = dynamic_cast< QgsSingleSymbolRenderer * >( layer->renderer() );
851 if ( !singleRenderer )
852 return;
853
854 std::unique_ptr< QgsSymbol > symbol( singleRenderer->symbol() ? singleRenderer->symbol()->clone() : nullptr );
855 QgsSymbolSelectorDialog dlg( symbol.get(), QgsStyle::defaultStyle(), layer, mView->window() );
856 dlg.setWindowTitle( tr( "Symbol Selector" ) );
857 QgsSymbolWidgetContext context;
858 context.setMapCanvas( mCanvas );
859 context.setMessageBar( QgisApp::instance()->messageBar() );
860 dlg.setContext( context );
861 if ( dlg.exec() )
862 {
863 singleRenderer->setSymbol( symbol.release() );
864 layer->triggerRepaint();
865 mView->refreshLayerSymbology( layer->id() );
866 layer->emitStyleChanged();
867 QgsProject::instance()->setDirty( true );
868 }
869 }
870
copyVectorSymbol(const QString & layerId)871 void QgsAppLayerTreeViewMenuProvider::copyVectorSymbol( const QString &layerId )
872 {
873 QgsVectorLayer *layer = QgsProject::instance()->mapLayer<QgsVectorLayer *>( layerId );
874 if ( !layer )
875 return;
876
877 QgsSingleSymbolRenderer *singleRenderer = dynamic_cast< QgsSingleSymbolRenderer * >( layer->renderer() );
878 if ( !singleRenderer )
879 return;
880
881 QApplication::clipboard()->setMimeData( QgsSymbolLayerUtils::symbolToMimeData( singleRenderer->symbol() ) );
882 }
883
pasteVectorSymbol(const QString & layerId)884 void QgsAppLayerTreeViewMenuProvider::pasteVectorSymbol( const QString &layerId )
885 {
886 QgsVectorLayer *layer = QgsProject::instance()->mapLayer<QgsVectorLayer *>( layerId );
887 if ( !layer )
888 return;
889
890 QgsSingleSymbolRenderer *singleRenderer = dynamic_cast< QgsSingleSymbolRenderer * >( layer->renderer() );
891 if ( !singleRenderer )
892 return;
893
894 std::unique_ptr< QgsSymbol > tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
895 if ( !tempSymbol )
896 return;
897
898 if ( !singleRenderer->symbol() || singleRenderer->symbol()->type() != tempSymbol->type() )
899 return;
900
901 singleRenderer->setSymbol( tempSymbol.release() );
902 layer->triggerRepaint();
903 layer->emitStyleChanged();
904 mView->refreshLayerSymbology( layer->id() );
905 QgsProject::instance()->setDirty( true );
906 }
907
setVectorSymbolColor(const QColor & color)908 void QgsAppLayerTreeViewMenuProvider::setVectorSymbolColor( const QColor &color )
909 {
910 QAction *action = qobject_cast< QAction *>( sender() );
911 if ( !action )
912 return;
913
914 QString layerId = action->property( "layerId" ).toString();
915 QgsVectorLayer *layer = QgsProject::instance()->mapLayer<QgsVectorLayer *>( layerId );
916 if ( !layer )
917 return;
918
919 QgsSingleSymbolRenderer *singleRenderer = dynamic_cast< QgsSingleSymbolRenderer * >( layer->renderer() );
920 QgsSymbol *newSymbol = nullptr;
921
922 if ( singleRenderer && singleRenderer->symbol() )
923 newSymbol = singleRenderer->symbol()->clone();
924
925 const QgsSingleSymbolRenderer *embeddedRenderer = nullptr;
926 if ( !newSymbol && layer->renderer()->embeddedRenderer() )
927 {
928 embeddedRenderer = dynamic_cast< const QgsSingleSymbolRenderer * >( layer->renderer()->embeddedRenderer() );
929 if ( embeddedRenderer && embeddedRenderer->symbol() )
930 newSymbol = embeddedRenderer->symbol()->clone();
931 }
932
933 if ( !newSymbol )
934 return;
935
936 newSymbol->setColor( color );
937 if ( singleRenderer )
938 {
939 singleRenderer->setSymbol( newSymbol );
940 }
941 else if ( embeddedRenderer )
942 {
943 QgsSingleSymbolRenderer *newRenderer = embeddedRenderer->clone();
944 newRenderer->setSymbol( newSymbol );
945 layer->renderer()->setEmbeddedRenderer( newRenderer );
946 }
947
948 layer->triggerRepaint();
949 layer->emitStyleChanged();
950 mView->refreshLayerSymbology( layer->id() );
951 QgsProject::instance()->setDirty( true );
952 }
953
editSymbolLegendNodeSymbol(const QString & layerId,const QString & ruleKey)954 void QgsAppLayerTreeViewMenuProvider::editSymbolLegendNodeSymbol( const QString &layerId, const QString &ruleKey )
955 {
956 QgsSymbolLegendNode *node = qobject_cast<QgsSymbolLegendNode *>( mView->layerTreeModel()->findLegendNode( layerId, ruleKey ) );
957 if ( !node )
958 return;
959
960 const QgsSymbol *originalSymbol = node->symbol();
961 if ( !originalSymbol )
962 {
963 QgisApp::instance()->messageBar()->pushWarning( tr( "No Symbol" ), tr( "There is no symbol associated with the rule." ) );
964 return;
965 }
966
967 std::unique_ptr< QgsSymbol > symbol( originalSymbol->clone() );
968 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( node->layerNode()->layer() );
969 QgsSymbolSelectorDialog dlg( symbol.get(), QgsStyle::defaultStyle(), vlayer, mView->window() );
970 dlg.setWindowTitle( tr( "Symbol Selector" ) );
971 QgsSymbolWidgetContext context;
972 context.setMapCanvas( mCanvas );
973 context.setMessageBar( QgisApp::instance()->messageBar() );
974 dlg.setContext( context );
975 if ( dlg.exec() )
976 {
977 node->setSymbol( symbol.release() );
978 if ( vlayer )
979 {
980 vlayer->emitStyleChanged();
981 }
982 QgsProject::instance()->setDirty( true );
983 }
984 }
985
copySymbolLegendNodeSymbol(const QString & layerId,const QString & ruleKey)986 void QgsAppLayerTreeViewMenuProvider::copySymbolLegendNodeSymbol( const QString &layerId, const QString &ruleKey )
987 {
988 QgsSymbolLegendNode *node = qobject_cast<QgsSymbolLegendNode *>( mView->layerTreeModel()->findLegendNode( layerId, ruleKey ) );
989 if ( !node )
990 return;
991
992 const QgsSymbol *originalSymbol = node->symbol();
993 if ( !originalSymbol )
994 return;
995
996 QApplication::clipboard()->setMimeData( QgsSymbolLayerUtils::symbolToMimeData( originalSymbol ) );
997 }
998
pasteSymbolLegendNodeSymbol(const QString & layerId,const QString & ruleKey)999 void QgsAppLayerTreeViewMenuProvider::pasteSymbolLegendNodeSymbol( const QString &layerId, const QString &ruleKey )
1000 {
1001 QgsSymbolLegendNode *node = qobject_cast<QgsSymbolLegendNode *>( mView->layerTreeModel()->findLegendNode( layerId, ruleKey ) );
1002 if ( !node )
1003 return;
1004
1005 const QgsSymbol *originalSymbol = node->symbol();
1006 if ( !originalSymbol )
1007 return;
1008
1009 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( node->layerNode()->layer() );
1010
1011 std::unique_ptr< QgsSymbol > tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
1012 if ( tempSymbol && tempSymbol->type() == originalSymbol->type() )
1013 {
1014 node->setSymbol( tempSymbol.release() );
1015 if ( vlayer )
1016 {
1017 vlayer->emitStyleChanged();
1018 }
1019 QgsProject::instance()->setDirty( true );
1020 }
1021 }
1022
setSymbolLegendNodeColor(const QColor & color)1023 void QgsAppLayerTreeViewMenuProvider::setSymbolLegendNodeColor( const QColor &color )
1024 {
1025 QAction *action = qobject_cast< QAction *>( sender() );
1026 if ( !action )
1027 return;
1028
1029 QString layerId = action->property( "layerId" ).toString();
1030 QString ruleKey = action->property( "ruleKey" ).toString();
1031
1032 QgsSymbolLegendNode *node = qobject_cast<QgsSymbolLegendNode *>( mView->layerTreeModel()->findLegendNode( layerId, ruleKey ) );
1033 if ( !node )
1034 return;
1035
1036 const QgsSymbol *originalSymbol = node->symbol();
1037 if ( !originalSymbol )
1038 return;
1039
1040 std::unique_ptr< QgsSymbol > newSymbol( originalSymbol->clone() );
1041 newSymbol->setColor( color );
1042 node->setSymbol( newSymbol.release() );
1043 if ( QgsVectorLayer *layer = QgsProject::instance()->mapLayer<QgsVectorLayer *>( layerId ) )
1044 {
1045 layer->emitStyleChanged();
1046 }
1047 QgsProject::instance()->setDirty( true );
1048 }
1049
removeActionEnabled()1050 bool QgsAppLayerTreeViewMenuProvider::removeActionEnabled()
1051 {
1052 const QList<QgsLayerTreeLayer *> selectedLayers = mView->selectedLayerNodes();
1053 for ( QgsLayerTreeLayer *nodeLayer : selectedLayers )
1054 {
1055 // be careful with the logic here -- if nodeLayer->layer() is false, will still must return true
1056 // to allow the broken layer to be removed from the project
1057 if ( nodeLayer->layer() && !nodeLayer->layer()->flags().testFlag( QgsMapLayer::Removable ) )
1058 return false;
1059 }
1060 return true;
1061 }
1062
setLayerCrs(const QgsCoordinateReferenceSystem & crs)1063 void QgsAppLayerTreeViewMenuProvider::setLayerCrs( const QgsCoordinateReferenceSystem &crs )
1064 {
1065 const auto constSelectedNodes = mView->selectedNodes();
1066 for ( QgsLayerTreeNode *node : constSelectedNodes )
1067 {
1068 if ( QgsLayerTree::isLayer( node ) )
1069 {
1070 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
1071 if ( nodeLayer->layer() )
1072 {
1073 nodeLayer->layer()->setCrs( crs, true );
1074 nodeLayer->layer()->triggerRepaint();
1075 }
1076 }
1077 }
1078 }
1079