コード例 #1
0
ファイル: qgslegendlayer.cpp プロジェクト: LukasGalliker/QGIS
void QgsLegendLayer::updateIcon()
{
  QPixmap newIcon( getOriginalPixmap() );

  QgsMapLayer* theLayer = layer();

  //overview
  // FIXME: overview icon is missing
  /*
  if ( theFile->isInOverview() )
  {
    // Overlay the overview icon on the default icon
    QPixmap myPixmap = QgsApplication::getThemePixmap(  "/mIconOverview.png" );
    QPainter p( &newIcon );
    p.drawPixmap( 0, 0, myPixmap );
    p.end();
  }*/

  //editable
  if ( theLayer->isEditable() )
  {
    QPixmap myPixmap;
    QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer *>( theLayer );
    if ( vlayer->isModified() )
    {
      myPixmap = QgsApplication::getThemePixmap( "/mIconEditableEdits.png" );
    }
    else
    {
      myPixmap = QgsApplication::getThemePixmap( "/mIconEditable.png" );
    }
    // use editable icon instead of the layer's type icon
    newIcon = myPixmap;

    // Overlay the editable icon on the default icon
    /*QPainter p( &newIcon );
    p.drawPixmap( 0, 0, myPixmap );
    p.end();*/
  }

  // TODO: projection error icon?

  QIcon theIcon( newIcon );
  QgsLegend* l = legend();
  if ( l )
  {
    l->blockSignals( true ); //prevent unnecessary canvas redraw
  }
  setIcon( 0, theIcon );
  if ( l )
  {
    l->blockSignals( false );
  }
}
コード例 #2
0
bool QgsLayerTreeUtils::layersModified( const QList<QgsLayerTreeLayer *> &layerNodes )
{
  const auto constLayerNodes = layerNodes;
  for ( QgsLayerTreeLayer *layerNode : constLayerNodes )
  {
    QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerNode->layer() );
    if ( !vl )
      continue;

    if ( vl->isEditable() && vl->isModified() )
      return true;
  }
  return false;
}
コード例 #3
0
ファイル: qgslegendlayer.cpp プロジェクト: LukasGalliker/QGIS
void QgsLegendLayer::addToPopupMenu( QMenu& theMenu )
{
  QgsMapLayer *lyr = layer();
  QAction *toggleEditingAction = QgisApp::instance()->actionToggleEditing();
  QAction *saveLayerEditsAction = QgisApp::instance()->actionSaveActiveLayerEdits();
  QAction *allEditsAction = QgisApp::instance()->actionAllEdits();

  // zoom to layer extent
  theMenu.addAction( QgsApplication::getThemeIcon( "/mActionZoomToLayer.svg" ),
                     tr( "&Zoom to Layer Extent" ), legend(), SLOT( legendLayerZoom() ) );
  if ( lyr->type() == QgsMapLayer::RasterLayer )
  {
    theMenu.addAction( tr( "&Zoom to Best Scale (100%)" ), legend(), SLOT( legendLayerZoomNative() ) );

    QgsRasterLayer *rasterLayer =  qobject_cast<QgsRasterLayer *>( lyr );
    if ( rasterLayer && rasterLayer->rasterType() != QgsRasterLayer::Palette )
    {
      theMenu.addAction( tr( "&Stretch Using Current Extent" ), legend(), SLOT( legendLayerStretchUsingCurrentExtent() ) );
    }
  }

  // show in overview
  QAction* showInOverviewAction = theMenu.addAction( tr( "&Show in Overview" ), this, SLOT( showInOverview() ) );
  showInOverviewAction->setCheckable( true );
  showInOverviewAction->blockSignals( true );
  showInOverviewAction->setChecked( mLyr.isInOverview() );
  showInOverviewAction->blockSignals( false );

  // remove from canvas
  theMenu.addAction( QgsApplication::getThemeIcon( "/mActionRemoveLayer.svg" ), tr( "&Remove" ), QgisApp::instance(), SLOT( removeLayer() ) );

  // duplicate layer
  QAction* duplicateLayersAction = theMenu.addAction( QgsApplication::getThemeIcon( "/mActionDuplicateLayer.svg" ), tr( "&Duplicate" ), QgisApp::instance(), SLOT( duplicateLayers() ) );

  // set layer crs
  theMenu.addAction( QgsApplication::getThemeIcon( "/mActionSetCRS.png" ), tr( "&Set Layer CRS" ), QgisApp::instance(), SLOT( setLayerCRS() ) );

  // assign layer crs to project
  theMenu.addAction( QgsApplication::getThemeIcon( "/mActionSetProjectCRS.png" ), tr( "Set &Project CRS from Layer" ), QgisApp::instance(), SLOT( setProjectCRSFromLayer() ) );

  theMenu.addSeparator();

  if ( lyr->type() == QgsMapLayer::VectorLayer )
  {
    QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer *>( lyr );

    // attribute table
    theMenu.addAction( QgsApplication::getThemeIcon( "/mActionOpenTable.png" ), tr( "&Open Attribute Table" ),
                       QgisApp::instance(), SLOT( attributeTable() ) );

    // allow editing
    int cap = vlayer->dataProvider()->capabilities();
    if ( cap & QgsVectorDataProvider::EditingCapabilities )
    {
      if ( toggleEditingAction )
      {
        theMenu.addAction( toggleEditingAction );
        toggleEditingAction->setChecked( vlayer->isEditable() );
      }
      if ( saveLayerEditsAction && vlayer->isModified() )
      {
        theMenu.addAction( saveLayerEditsAction );
      }
    }

    if ( allEditsAction->isEnabled() )
    {
      theMenu.addAction( allEditsAction );
    }

    // disable duplication of memory layers
    if ( vlayer->storageType() == "Memory storage" && legend()->selectedLayers().count() == 1 )
    {
      duplicateLayersAction->setEnabled( false );
    }

    // save as vector file
    theMenu.addAction( tr( "Save As..." ), QgisApp::instance(), SLOT( saveAsFile() ) );

    // save selection as vector file
    QAction* saveSelectionAsAction = theMenu.addAction( tr( "Save Selection As..." ), QgisApp::instance(), SLOT( saveSelectionAsVectorFile() ) );
    if ( vlayer->selectedFeatureCount() == 0 )
    {
      saveSelectionAsAction->setEnabled( false );
    }

    if ( !vlayer->isEditable() && vlayer->dataProvider()->supportsSubsetString() && vlayer->vectorJoins().isEmpty() )
      theMenu.addAction( tr( "&Filter..." ), QgisApp::instance(), SLOT( layerSubsetString() ) );

    //show number of features in legend if requested
    QAction* showNFeaturesAction = new QAction( tr( "Show Feature Count" ), &theMenu );
    showNFeaturesAction->setCheckable( true );
    showNFeaturesAction->setChecked( mShowFeatureCount );
    QObject::connect( showNFeaturesAction, SIGNAL( toggled( bool ) ), this, SLOT( setShowFeatureCount( bool ) ) );
    theMenu.addAction( showNFeaturesAction );
    theMenu.addSeparator();
  }
コード例 #4
0
ファイル: qgslayertreemodel.cpp プロジェクト: saintbae/QGIS
QVariant QgsLayerTreeModel::data( const QModelIndex &index, int role ) const
{
  if ( !index.isValid() )
    return QVariant();

  if ( QgsLayerTreeModelLegendNode* sym = index2symnode( index ) )
  {
    if ( role == Qt::CheckStateRole && !testFlag( AllowSymbologyChangeState ) )
      return QVariant();
    return sym->data( role );
  }

  QgsLayerTreeNode* node = index2node( index );
  if ( role == Qt::DisplayRole || role == Qt::EditRole )
  {
    if ( QgsLayerTree::isGroup( node ) )
      return QgsLayerTree::toGroup( node )->name();
    else if ( QgsLayerTree::isLayer( node ) )
    {
      QgsLayerTreeLayer* nodeLayer = QgsLayerTree::toLayer( node );
      QString name = nodeLayer->layerName();
      if ( nodeLayer->customProperty( "showFeatureCount", 0 ).toInt() && role == Qt::DisplayRole )
      {
        QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer*>( nodeLayer->layer() );
        if ( vlayer && vlayer->pendingFeatureCount() >= 0 )
          name += QString( " [%1]" ).arg( vlayer->pendingFeatureCount() );
      }
      return name;
    }
  }
  else if ( role == Qt::DecorationRole && index.column() == 0 )
  {
    if ( QgsLayerTree::isGroup( node ) )
      return iconGroup();
    else if ( QgsLayerTree::isLayer( node ) )
    {
      QgsLayerTreeLayer* nodeLayer = QgsLayerTree::toLayer( node );

      // if there's just on legend entry that should be embedded in layer - do that!
      if ( testFlag( ShowSymbology ) && mSymbologyNodes[nodeLayer].count() == 1 && mSymbologyNodes[nodeLayer][0]->isEmbeddedInParent() )
        return mSymbologyNodes[nodeLayer][0]->data( Qt::DecorationRole );

      QgsMapLayer* layer = QgsLayerTree::toLayer( node )->layer();
      if ( !layer )
        return QVariant();
      if ( layer->type() == QgsMapLayer::RasterLayer )
      {
        if ( testFlag( ShowRasterPreviewIcon ) )
        {
          QgsRasterLayer* rlayer = qobject_cast<QgsRasterLayer *>( layer );
          return QIcon( rlayer->previewAsPixmap( QSize( 32, 32 ) ) );
        }
        else
          return QgsLayerItem::iconRaster();
      }
      else if ( layer->type() == QgsMapLayer::VectorLayer )
      {
        QgsVectorLayer* vlayer = static_cast<QgsVectorLayer*>( layer );
        if ( vlayer->isEditable() )
        {
          if ( vlayer->isModified() )
            return QIcon( QgsApplication::getThemePixmap( "/mIconEditableEdits.png" ) );
          else
            return QIcon( QgsApplication::getThemePixmap( "/mIconEditable.png" ) );
        }

        if ( vlayer->geometryType() == QGis::Point )
          return QgsLayerItem::iconPoint();
        else if ( vlayer->geometryType() == QGis::Line )
          return QgsLayerItem::iconLine();
        else if ( vlayer->geometryType() == QGis::Polygon )
          return QgsLayerItem::iconPolygon();
        else if ( vlayer->geometryType() == QGis::NoGeometry )
          return QgsLayerItem::iconTable();
      }
      return QgsLayerItem::iconDefault();
    }
  }
  else if ( role == Qt::CheckStateRole )
  {
    if ( !testFlag( AllowNodeChangeVisibility ) )
      return QVariant();

    if ( QgsLayerTree::isLayer( node ) )
    {
      QgsLayerTreeLayer* nodeLayer = QgsLayerTree::toLayer( node );
      if ( nodeLayer->layer() && nodeLayer->layer()->type() == QgsMapLayer::VectorLayer )
      {
        if ( qobject_cast<QgsVectorLayer*>( nodeLayer->layer() )->geometryType() == QGis::NoGeometry )
          return QVariant(); // do not show checkbox for non-spatial tables
      }
      return nodeLayer->isVisible();
    }
    else if ( QgsLayerTree::isGroup( node ) )
    {
      QgsLayerTreeGroup* nodeGroup = QgsLayerTree::toGroup( node );
      return nodeGroup->isVisible();
    }
  }
  else if ( role == Qt::FontRole )
  {
    QFont f( QgsLayerTree::isLayer( node ) ? mFontLayer : ( QgsLayerTree::isGroup( node ) ? mFontGroup : QFont() ) );
    if ( node->customProperty( "embedded" ).toInt() )
      f.setItalic( true );
    if ( index == mCurrentIndex )
      f.setUnderline( true );
    return f;
  }
  else if ( role == Qt::ToolTipRole )
  {
    if ( QgsLayerTree::isLayer( node ) )
    {
      if ( QgsMapLayer* layer = QgsLayerTree::toLayer( node )->layer() )
        return layer->publicSource();
    }
  }

  return QVariant();
}
コード例 #5
0
ファイル: qgslayertreemodel.cpp プロジェクト: ruester/QGIS
QVariant QgsLayerTreeModel::data( const QModelIndex &index, int role ) const
{
    if ( !index.isValid() || index.column() > 1 )
        return QVariant();

    if ( QgsLayerTreeModelLegendNode* sym = index2legendNode( index ) )
        return legendNodeData( sym, role );

    QgsLayerTreeNode* node = index2node( index );
    if ( role == Qt::DisplayRole || role == Qt::EditRole )
    {
        if ( QgsLayerTree::isGroup( node ) )
            return QgsLayerTree::toGroup( node )->name();

        if ( QgsLayerTree::isLayer( node ) )
        {
            QgsLayerTreeLayer* nodeLayer = QgsLayerTree::toLayer( node );
            QString name = nodeLayer->layerName();
            if ( nodeLayer->customProperty( "showFeatureCount", 0 ).toInt() && role == Qt::DisplayRole )
            {
                QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer*>( nodeLayer->layer() );
                if ( vlayer && vlayer->featureCount() >= 0 )
                    name += QString( " [%1]" ).arg( vlayer->featureCount() );
            }
            return name;
        }
    }
    else if ( role == Qt::DecorationRole && index.column() == 0 )
    {
        if ( QgsLayerTree::isGroup( node ) )
            return iconGroup();

        if ( QgsLayerTree::isLayer( node ) )
        {
            QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );

            QgsMapLayer *layer = nodeLayer->layer();
            if ( !layer )
                return QVariant();

            // icons possibly overriding default icon
            if ( layer->type() == QgsMapLayer::RasterLayer )
            {
                if ( testFlag( ShowRasterPreviewIcon ) )
                {
                    QgsRasterLayer* rlayer = qobject_cast<QgsRasterLayer *>( layer );
                    return QIcon( QPixmap::fromImage( rlayer->previewAsImage( QSize( 32, 32 ) ) ) );
                }
                else
                {
                    return QgsLayerItem::iconRaster();
                }
            }

            QgsVectorLayer *vlayer = dynamic_cast<QgsVectorLayer*>( layer );
            QIcon icon;

            // if there's just on legend entry that should be embedded in layer - do that!
            if ( testFlag( ShowLegend ) && legendEmbeddedInParent( nodeLayer ) )
            {
                icon = legendIconEmbeddedInParent( nodeLayer );
            }
            else if ( vlayer && layer->type() == QgsMapLayer::VectorLayer )
            {
                if ( vlayer->geometryType() == QGis::Point )
                    icon = QgsLayerItem::iconPoint();
                else if ( vlayer->geometryType() == QGis::Line )
                    icon = QgsLayerItem::iconLine();
                else if ( vlayer->geometryType() == QGis::Polygon )
                    icon = QgsLayerItem::iconPolygon();
                else if ( vlayer->geometryType() == QGis::NoGeometry )
                    icon = QgsLayerItem::iconTable();
                else
                    icon = QgsLayerItem::iconDefault();
            }

            if ( vlayer && vlayer->isEditable() )
            {
                QPixmap pixmap( icon.pixmap( 16, 16 ) );

                QPainter painter( &pixmap );
                painter.drawPixmap( 0, 0, 16, 16, QgsApplication::getThemePixmap( vlayer->isModified() ? "/mIconEditableEdits.png" : "/mIconEditable.png" ) );
                painter.end();

                icon = QIcon( pixmap );
            }

            return icon;
        }
    }
    else if ( role == Qt::CheckStateRole )
    {
        if ( !testFlag( AllowNodeChangeVisibility ) )
            return QVariant();

        if ( QgsLayerTree::isLayer( node ) )
        {
            QgsLayerTreeLayer* nodeLayer = QgsLayerTree::toLayer( node );
            if ( nodeLayer->layer() && nodeLayer->layer()->type() == QgsMapLayer::VectorLayer )
            {
                if ( qobject_cast<QgsVectorLayer*>( nodeLayer->layer() )->geometryType() == QGis::NoGeometry )
                    return QVariant(); // do not show checkbox for non-spatial tables
            }
            return nodeLayer->isVisible();
        }
        else if ( QgsLayerTree::isGroup( node ) )
        {
            QgsLayerTreeGroup* nodeGroup = QgsLayerTree::toGroup( node );
            return nodeGroup->isVisible();
        }
    }
    else if ( role == Qt::FontRole )
    {
        QFont f( QgsLayerTree::isLayer( node ) ? mFontLayer : ( QgsLayerTree::isGroup( node ) ? mFontGroup : QFont() ) );
        if ( node->customProperty( "embedded" ).toInt() )
            f.setItalic( true );
        if ( index == mCurrentIndex )
            f.setUnderline( true );
        return f;
    }
    else if ( role == Qt::ToolTipRole )
    {
        if ( QgsLayerTree::isLayer( node ) )
        {
            if ( QgsMapLayer* layer = QgsLayerTree::toLayer( node )->layer() )
                return layer->publicSource();
        }
    }

    return QVariant();
}
コード例 #6
0
QMenu* QgsAppLayerTreeViewMenuProvider::createContextMenu()
{
  QMenu* menu = new QMenu;

  QgsLayerTreeViewDefaultActions* actions = mView->defaultActions();

  QModelIndex idx = mView->currentIndex();
  if ( !idx.isValid() )
  {
    // global menu
    menu->addAction( actions->actionAddGroup( menu ) );

    // TODO: expand all, collapse all
    // TODO: update drawing order
  }
  else if ( QgsLayerTreeNode* node = mView->layerTreeModel()->index2node( idx ) )
  {
    // layer or group selected
    if ( QgsLayerTree::isGroup( node ) )
    {
      menu->addAction( actions->actionZoomToGroup( mCanvas, menu ) );
      menu->addAction( actions->actionRemoveGroupOrLayer( menu ) );

      menu->addAction( QgsApplication::getThemeIcon( "/mActionSetCRS.png" ),
                       tr( "&Set Group CRS" ), QgisApp::instance(), SLOT( legendGroupSetCRS() ) );

      menu->addAction( actions->actionRenameGroupOrLayer( menu ) );

      if ( mView->selectedNodes( true ).count() >= 2 )
        menu->addAction( actions->actionGroupSelected( menu ) );

      menu->addAction( actions->actionAddGroup( menu ) );
    }
    else if ( QgsLayerTree::isLayer( node ) )
    {
      QgsMapLayer* layer = QgsLayerTree::toLayer( node )->layer();

      menu->addAction( actions->actionZoomToLayer( mCanvas, menu ) );
      menu->addAction( actions->actionShowInOverview( menu ) );

      if ( layer && layer->type() == QgsMapLayer::RasterLayer )
      {
        menu->addAction( tr( "&Zoom to Best Scale (100%)" ), QgisApp::instance(), SLOT( legendLayerZoomNative() ) );

        QgsRasterLayer* rasterLayer =  qobject_cast<QgsRasterLayer *>( layer );
        if ( rasterLayer && rasterLayer->rasterType() != QgsRasterLayer::Palette )
          menu->addAction( tr( "&Stretch Using Current Extent" ), QgisApp::instance(), SLOT( legendLayerStretchUsingCurrentExtent() ) );
      }

      menu->addAction( actions->actionRemoveGroupOrLayer( menu ) );

      // duplicate layer
      QAction* duplicateLayersAction = menu->addAction( QgsApplication::getThemeIcon( "/mActionDuplicateLayer.svg" ), tr( "&Duplicate" ), QgisApp::instance(), SLOT( duplicateLayers() ) );

      // set layer scale visibility
      menu->addAction( tr( "&Set Layer Scale Visibility" ), QgisApp::instance(), SLOT( setLayerScaleVisibility() ) );

      // set layer crs
      menu->addAction( QgsApplication::getThemeIcon( "/mActionSetCRS.png" ), tr( "&Set Layer CRS" ), QgisApp::instance(), SLOT( setLayerCRS() ) );

      // assign layer crs to project
      menu->addAction( QgsApplication::getThemeIcon( "/mActionSetProjectCRS.png" ), tr( "Set &Project CRS from Layer" ), QgisApp::instance(), SLOT( setProjectCRSFromLayer() ) );

      menu->addSeparator();

      if ( layer && layer->type() == QgsMapLayer::VectorLayer )
      {
        QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer *>( layer );

        QAction *toggleEditingAction = QgisApp::instance()->actionToggleEditing();
        QAction *saveLayerEditsAction = QgisApp::instance()->actionSaveActiveLayerEdits();
        QAction *allEditsAction = QgisApp::instance()->actionAllEdits();

        // attribute table
        menu->addAction( QgsApplication::getThemeIcon( "/mActionOpenTable.png" ), tr( "&Open Attribute Table" ),
                         QgisApp::instance(), SLOT( attributeTable() ) );

        // allow editing
        int cap = vlayer->dataProvider()->capabilities();
        if ( cap & QgsVectorDataProvider::EditingCapabilities )
        {
          if ( toggleEditingAction )
          {
            menu->addAction( toggleEditingAction );
            toggleEditingAction->setChecked( vlayer->isEditable() );
          }
          if ( saveLayerEditsAction && vlayer->isModified() )
          {
            menu->addAction( saveLayerEditsAction );
          }
        }

        if ( allEditsAction->isEnabled() )
          menu->addAction( allEditsAction );

        // disable duplication of memory layers
        if ( vlayer->storageType() == "Memory storage" && mView->selectedLayerNodes().count() == 1 )
          duplicateLayersAction->setEnabled( false );

        // save as vector file
        menu->addAction( tr( "Save As..." ), QgisApp::instance(), SLOT( saveAsFile() ) );
        menu->addAction( tr( "Save As Layer Definition File..." ), QgisApp::instance(), SLOT( saveAsLayerDefinition() ) );

        if ( !vlayer->isEditable() && vlayer->dataProvider()->supportsSubsetString() && vlayer->vectorJoins().isEmpty() )
          menu->addAction( tr( "&Filter..." ), QgisApp::instance(), SLOT( layerSubsetString() ) );

        menu->addAction( actions->actionShowFeatureCount( menu ) );

        menu->addSeparator();
      }
      else if ( layer && layer->type() == QgsMapLayer::RasterLayer )
      {
        menu->addAction( tr( "Save As..." ), QgisApp::instance(), SLOT( saveAsRasterFile() ) );
        menu->addAction( tr( "Save As Layer Definition File..." ), QgisApp::instance(), SLOT( saveAsLayerDefinition() ) );
      }
      else if ( layer && layer->type() == QgsMapLayer::PluginLayer && mView->selectedLayerNodes().count() == 1 )
      {
        // disable duplication of plugin layers
        duplicateLayersAction->setEnabled( false );
      }

      // TODO: custom actions

      if ( layer && QgsProject::instance()->layerIsEmbedded( layer->id() ).isEmpty() )
        menu->addAction( tr( "&Properties" ), QgisApp::instance(), SLOT( layerProperties() ) );

      if ( node->parent() != mView->layerTreeModel()->rootGroup() )
        menu->addAction( actions->actionMakeTopLevel( menu ) );

      menu->addAction( actions->actionRenameGroupOrLayer( menu ) );

      if ( mView->selectedNodes( true ).count() >= 2 )
        menu->addAction( actions->actionGroupSelected( menu ) );

      if ( mView->selectedLayerNodes().count() == 1 )
      {
        QgisApp* app = QgisApp::instance();
        menu->addAction( tr( "Copy Style" ), app, SLOT( copyStyle() ) );
        if ( app->clipboard()->hasFormat( QGSCLIPBOARD_STYLE_MIME ) )
        {
          menu->addAction( tr( "Paste Style" ), app, SLOT( pasteStyle() ) );
        }
      }
    }

  }
  else
  {
    // symbology item?
  }

  return menu;
}
コード例 #7
0
QMenu *QgsAppLayerTreeViewMenuProvider::createContextMenu()
{
  QMenu *menu = new QMenu;

  QgsLayerTreeViewDefaultActions *actions = mView->defaultActions();

  QModelIndex idx = mView->currentIndex();
  if ( !idx.isValid() )
  {
    // global menu
    menu->addAction( actions->actionAddGroup( menu ) );
    menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionExpandTree.svg" ) ), tr( "&Expand All" ), mView, SLOT( expandAll() ) );
    menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionCollapseTree.svg" ) ), tr( "&Collapse All" ), mView, SLOT( collapseAll() ) );
    menu->addSeparator();
    if ( QgisApp::instance()->clipboard()->hasFormat( QGSCLIPBOARD_MAPLAYER_MIME ) )
    {
      QAction *actionPasteLayerOrGroup = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionEditPaste.svg" ) ), tr( "Paste Layer/Group" ), menu );
      connect( actionPasteLayerOrGroup, &QAction::triggered, QgisApp::instance(), &QgisApp::pasteLayer );
      menu->addAction( actionPasteLayerOrGroup );
    }

    // TODO: update drawing order
  }
  else if ( QgsLayerTreeNode *node = mView->layerTreeModel()->index2node( idx ) )
  {
    // layer or group selected
    if ( QgsLayerTree::isGroup( node ) )
    {
      menu->addAction( actions->actionZoomToGroup( mCanvas, menu ) );

      menu->addAction( tr( "Copy Group" ), QgisApp::instance(), SLOT( copyLayer() ) );
      if ( QgisApp::instance()->clipboard()->hasFormat( QGSCLIPBOARD_MAPLAYER_MIME ) )
      {
        QAction *actionPasteLayerOrGroup = new QAction( tr( "Paste Layer/Group" ), menu );
        connect( actionPasteLayerOrGroup, &QAction::triggered, QgisApp::instance(), &QgisApp::pasteLayer );
        menu->addAction( actionPasteLayerOrGroup );
      }

      menu->addAction( actions->actionRenameGroupOrLayer( menu ) );

      menu->addSeparator();
      menu->addAction( actions->actionAddGroup( menu ) );
      QAction *removeAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionRemoveLayer.svg" ) ), tr( "&Remove Group…" ), QgisApp::instance(), SLOT( removeLayer() ) );
      removeAction->setEnabled( removeActionEnabled() );
      menu->addSeparator();

      menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSetCRS.png" ) ),
                       tr( "&Set Group CRS…" ), QgisApp::instance(), SLOT( legendGroupSetCrs() ) );
      menu->addAction( tr( "&Set Group WMS Data…" ), QgisApp::instance(), SLOT( legendGroupSetWmsData() ) );

      menu->addSeparator();

      menu->addAction( actions->actionMutuallyExclusiveGroup( menu ) );

      menu->addAction( actions->actionCheckAndAllChildren( menu ) );

      menu->addAction( actions->actionUncheckAndAllChildren( menu ) );

      if ( !( mView->selectedNodes( true ).count() == 1 && idx.row() == 0 ) )
      {
        menu->addAction( actions->actionMoveToTop( menu ) );
      }

      menu->addSeparator();

      if ( mView->selectedNodes( true ).count() >= 2 )
        menu->addAction( actions->actionGroupSelected( menu ) );

      if ( QgisApp::instance()->clipboard()->hasFormat( QGSCLIPBOARD_STYLE_MIME ) )
      {
        menu->addAction( tr( "Paste Style" ), QgisApp::instance(), SLOT( applyStyleToGroup() ) );
      }

      menu->addSeparator();

      QMenu *menuExportGroup = new QMenu( tr( "Export" ), menu );
      QAction *actionSaveAsDefinitionGroup = new QAction( tr( "Save as Layer Definition File…" ), menuExportGroup );
      connect( actionSaveAsDefinitionGroup, &QAction::triggered, QgisApp::instance(), &QgisApp::saveAsLayerDefinition );
      menuExportGroup->addAction( actionSaveAsDefinitionGroup );

      menu->addMenu( menuExportGroup );
    }
    else if ( QgsLayerTree::isLayer( node ) )
    {
      QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
      QgsRasterLayer *rlayer = qobject_cast<QgsRasterLayer *>( layer );
      QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );

      if ( layer && layer->isSpatial() )
      {
        menu->addAction( actions->actionZoomToLayer( mCanvas, menu ) );
        if ( vlayer )
        {
          QAction *actionZoomSelected = actions->actionZoomToSelection( mCanvas, menu );
          actionZoomSelected->setEnabled( !vlayer->selectedFeatures().isEmpty() );
          menu->addAction( actionZoomSelected );
        }
        menu->addAction( actions->actionShowInOverview( menu ) );
      }

      if ( vlayer )
        menu->addAction( actions->actionShowFeatureCount( menu ) );

      QAction *actionCopyLayer = new QAction( tr( "Copy Layer" ), menu );
      connect( actionCopyLayer, &QAction::triggered, QgisApp::instance(), &QgisApp::copyLayer );
      menu->addAction( actionCopyLayer );

      menu->addAction( actions->actionRenameGroupOrLayer( menu ) );

      if ( rlayer )
      {
        menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionZoomActual.svg" ) ), tr( "&Zoom to Native Resolution (100%)" ), QgisApp::instance(), SLOT( legendLayerZoomNative() ) );

        if ( rlayer->rasterType() != QgsRasterLayer::Palette )
          menu->addAction( tr( "&Stretch Using Current Extent" ), QgisApp::instance(), SLOT( legendLayerStretchUsingCurrentExtent() ) );
      }

      addCustomLayerActions( menu, layer );
      if ( layer && layer->type() == QgsMapLayer::VectorLayer && static_cast<QgsVectorLayer *>( layer )->providerType() == QLatin1String( "virtual" ) )
      {
        menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddVirtualLayer.svg" ) ), tr( "Edit Virtual Layer…" ), QgisApp::instance(), SLOT( addVirtualLayer() ) );
      }

      menu->addSeparator();

      // duplicate layer
      QAction *duplicateLayersAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDuplicateLayer.svg" ) ), tr( "&Duplicate Layer" ), QgisApp::instance(), SLOT( duplicateLayers() ) );
      QAction *removeAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionRemoveLayer.svg" ) ), tr( "&Remove Layer…" ), QgisApp::instance(), SLOT( removeLayer() ) );
      removeAction->setEnabled( removeActionEnabled() );

      menu->addSeparator();

      if ( node->parent() != mView->layerTreeModel()->rootGroup() )
        menu->addAction( actions->actionMoveOutOfGroup( menu ) );

      if ( !( mView->selectedNodes( true ).count() == 1 && idx.row() == 0 ) )
      {
        menu->addAction( actions->actionMoveToTop( menu ) );
      }

      QAction *checkAll = actions->actionCheckAndAllParents( menu );
      if ( checkAll )
        menu->addAction( checkAll );

      if ( mView->selectedNodes( true ).count() >= 2 )
        menu->addAction( actions->actionGroupSelected( menu ) );

      menu->addSeparator();

      if ( vlayer )
      {
        QAction *toggleEditingAction = QgisApp::instance()->actionToggleEditing();
        QAction *saveLayerEditsAction = QgisApp::instance()->actionSaveActiveLayerEdits();
        QAction *allEditsAction = QgisApp::instance()->actionAllEdits();

        // attribute table
        menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionOpenTable.svg" ) ), tr( "&Open Attribute Table" ),
                         QgisApp::instance(), SLOT( attributeTable() ) );

        // allow editing
        int cap = vlayer->dataProvider()->capabilities();
        if ( cap & QgsVectorDataProvider::EditingCapabilities )
        {
          if ( toggleEditingAction )
          {
            menu->addAction( toggleEditingAction );
            toggleEditingAction->setChecked( vlayer->isEditable() );
            toggleEditingAction->setEnabled( true );
          }
          if ( saveLayerEditsAction && vlayer->isModified() )
          {
            menu->addAction( saveLayerEditsAction );
          }
        }

        if ( allEditsAction->isEnabled() )
          menu->addAction( allEditsAction );

        // disable duplication of memory layers
        if ( vlayer->storageType() == QLatin1String( "Memory storage" ) && mView->selectedLayerNodes().count() == 1 )
          duplicateLayersAction->setEnabled( false );

        if ( vlayer->dataProvider()->supportsSubsetString() )
        {
          QAction *action = menu->addAction( tr( "&Filter…" ), QgisApp::instance(), SLOT( layerSubsetString() ) );
          action->setEnabled( !vlayer->isEditable() );
        }
      }

      menu->addSeparator();

      if ( layer && layer->isSpatial() )
      {
        // set layer scale visibility
        menu->addAction( tr( "&Set Layer Scale Visibility…" ), QgisApp::instance(), SLOT( setLayerScaleVisibility() ) );

        if ( !layer->isInScaleRange( mCanvas->scale() ) )
          menu->addAction( tr( "Zoom to &Visible Scale" ), QgisApp::instance(), SLOT( zoomToLayerScale() ) );

        QMenu *menuSetCRS = new QMenu( tr( "Set CRS" ), menu );
        // set layer crs
        QAction *actionSetLayerCrs = new QAction( tr( "Set Layer CRS…" ), menuSetCRS );
        connect( actionSetLayerCrs, &QAction::triggered, QgisApp::instance(), &QgisApp::setLayerCrs );
        menuSetCRS->addAction( actionSetLayerCrs );
        // assign layer crs to project
        QAction *actionSetProjectCrs = new QAction( tr( "Set &Project CRS from Layer" ), menuSetCRS );
        connect( actionSetProjectCrs, &QAction::triggered, QgisApp::instance(), &QgisApp::setProjectCrsFromLayer );
        menuSetCRS->addAction( actionSetProjectCrs );

        menu->addMenu( menuSetCRS );
      }

      menu->addSeparator();

      if ( vlayer )
      {
        // save as vector file
        QMenu *menuExportVector = new QMenu( tr( "Export" ), menu );
        QAction *actionSaveAs = new QAction( tr( "Save Features As…" ), menuExportVector );
        connect( actionSaveAs, &QAction::triggered, QgisApp::instance(), [ = ] { QgisApp::instance()->saveAsFile(); } );
        menuExportVector->addAction( actionSaveAs );
        QAction *actionSaveSelectedFeaturesAs = new QAction( tr( "Save Selected Features As…" ), menuExportVector );
        connect( actionSaveSelectedFeaturesAs, &QAction::triggered, QgisApp::instance(), [ = ] { QgisApp::instance()->saveAsFile( nullptr, true ); } );
        actionSaveSelectedFeaturesAs->setEnabled( vlayer->selectedFeatureCount() > 0 );
        menuExportVector->addAction( actionSaveSelectedFeaturesAs );
        QAction *actionSaveAsDefinitionLayer = new QAction( tr( "Save as Layer Definition File…" ), menuExportVector );
        connect( actionSaveAsDefinitionLayer, &QAction::triggered, QgisApp::instance(), &QgisApp::saveAsLayerDefinition );
        menuExportVector->addAction( actionSaveAsDefinitionLayer );
        if ( vlayer->isSpatial() )
        {
          QAction *actionSaveStyle = new QAction( tr( "Save as QGIS Layer Style File…" ), menuExportVector );
          connect( actionSaveStyle, &QAction::triggered, QgisApp::instance(), [ = ] { QgisApp::instance()->saveStyleFile(); } );
          menuExportVector->addAction( actionSaveStyle );
        }
        menu->addMenu( menuExportVector );
      }
      else if ( rlayer )
      {
        QMenu *menuExportRaster = new QMenu( tr( "Export" ), menu );
        QAction *actionSaveAs = new QAction( tr( "Save As…" ), menuExportRaster );
        QAction *actionSaveAsDefinitionLayer = new QAction( tr( "Save as Layer Definition File…" ), menuExportRaster );
        QAction *actionSaveStyle = new QAction( tr( "Save as QGIS Layer Style File…" ), menuExportRaster );
        connect( actionSaveAs, &QAction::triggered, QgisApp::instance(), [ = ] { QgisApp::instance()->saveAsFile(); } );
        menuExportRaster->addAction( actionSaveAs );
        connect( actionSaveAsDefinitionLayer, &QAction::triggered, QgisApp::instance(), &QgisApp::saveAsLayerDefinition );
        menuExportRaster->addAction( actionSaveAsDefinitionLayer );
        connect( actionSaveStyle, &QAction::triggered, QgisApp::instance(), [ = ] { QgisApp::instance()->saveStyleFile(); } );
        menuExportRaster->addAction( actionSaveStyle );
        menu->addMenu( menuExportRaster );
      }
      else if ( layer && layer->type() == QgsMapLayer::PluginLayer && mView->selectedLayerNodes().count() == 1 )
      {
        // disable duplication of plugin layers
        duplicateLayersAction->setEnabled( false );
      }

      menu->addSeparator();

      // style-related actions
      if ( layer && mView->selectedLayerNodes().count() == 1 )
      {
        menu->addSeparator();
        QMenu *menuStyleManager = new QMenu( tr( "Styles" ), menu );

        QgisApp *app = QgisApp::instance();
        menuStyleManager->addAction( tr( "Copy Style" ), app, SLOT( copyStyle() ) );

        if ( app->clipboard()->hasFormat( QGSCLIPBOARD_STYLE_MIME ) )
        {
          menuStyleManager->addAction( tr( "Paste Style" ), app, SLOT( pasteStyle() ) );
        }

        menuStyleManager->addSeparator();
        QgsMapLayerStyleGuiUtils::instance()->addStyleManagerActions( menuStyleManager, layer );

        if ( vlayer )
        {
          const QgsSingleSymbolRenderer *singleRenderer = dynamic_cast< const QgsSingleSymbolRenderer * >( vlayer->renderer() );
          if ( !singleRenderer && vlayer->renderer() && vlayer->renderer()->embeddedRenderer() )
          {
            singleRenderer = dynamic_cast< const QgsSingleSymbolRenderer * >( vlayer->renderer()->embeddedRenderer() );
          }
          if ( singleRenderer && singleRenderer->symbol() )
          {
            //single symbol renderer, so add set color/edit symbol actions
            menuStyleManager->addSeparator();
            QgsColorWheel *colorWheel = new QgsColorWheel( menuStyleManager );
            colorWheel->setColor( singleRenderer->symbol()->color() );
            QgsColorWidgetAction *colorAction = new QgsColorWidgetAction( colorWheel, menuStyleManager, menuStyleManager );
            colorAction->setDismissOnColorSelection( false );
            connect( colorAction, &QgsColorWidgetAction::colorChanged, this, &QgsAppLayerTreeViewMenuProvider::setVectorSymbolColor );
            //store the layer id in action, so we can later retrieve the corresponding layer
            colorAction->setProperty( "layerId", vlayer->id() );
            menuStyleManager->addAction( colorAction );

            //add recent colors action
            QList<QgsRecentColorScheme *> recentSchemes;
            QgsApplication::colorSchemeRegistry()->schemes( recentSchemes );
            if ( !recentSchemes.isEmpty() )
            {
              QgsColorSwatchGridAction *recentColorAction = new QgsColorSwatchGridAction( recentSchemes.at( 0 ), menuStyleManager, QStringLiteral( "symbology" ), menuStyleManager );
              recentColorAction->setProperty( "layerId", vlayer->id() );
              recentColorAction->setDismissOnColorSelection( false );
              menuStyleManager->addAction( recentColorAction );
              connect( recentColorAction, &QgsColorSwatchGridAction::colorChanged, this, &QgsAppLayerTreeViewMenuProvider::setVectorSymbolColor );
            }

            menuStyleManager->addSeparator();
            QAction *editSymbolAction = new QAction( tr( "Edit Symbol…" ), menuStyleManager );
            //store the layer id in action, so we can later retrieve the corresponding layer
            editSymbolAction->setProperty( "layerId", vlayer->id() );
            connect( editSymbolAction, &QAction::triggered, this, &QgsAppLayerTreeViewMenuProvider::editVectorSymbol );
            menuStyleManager->addAction( editSymbolAction );
          }
        }

        menu->addMenu( menuStyleManager );
      }
      else
      {
        if ( QgisApp::instance()->clipboard()->hasFormat( QGSCLIPBOARD_STYLE_MIME ) )
        {
          menu->addAction( tr( "Paste Style" ), QgisApp::instance(), SLOT( applyStyleToGroup() ) );
        }
      }

      if ( layer && QgsProject::instance()->layerIsEmbedded( layer->id() ).isEmpty() )
        menu->addAction( tr( "&Properties…" ), QgisApp::instance(), SLOT( layerProperties() ) );
    }
  }
  else if ( QgsLayerTreeModelLegendNode *node = mView->layerTreeModel()->index2legendNode( idx ) )
  {
    if ( QgsSymbolLegendNode *symbolNode = dynamic_cast< QgsSymbolLegendNode * >( node ) )
    {
      // symbology item
      if ( symbolNode->flags() & Qt::ItemIsUserCheckable )
      {
        menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionShowAllLayers.svg" ) ), tr( "&Show All Items" ),
                         symbolNode, SLOT( checkAllItems() ) );
        menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionHideAllLayers.svg" ) ), tr( "&Hide All Items" ),
                         symbolNode, SLOT( uncheckAllItems() ) );
        menu->addSeparator();
      }

      if ( symbolNode->symbol() )
      {
        QgsColorWheel *colorWheel = new QgsColorWheel( menu );
        colorWheel->setColor( symbolNode->symbol()->color() );
        QgsColorWidgetAction *colorAction = new QgsColorWidgetAction( colorWheel, menu, menu );
        colorAction->setDismissOnColorSelection( false );
        connect( colorAction, &QgsColorWidgetAction::colorChanged, this, &QgsAppLayerTreeViewMenuProvider::setSymbolLegendNodeColor );
        //store the layer id and rule key in action, so we can later retrieve the corresponding
        //legend node, if it still exists
        colorAction->setProperty( "layerId", symbolNode->layerNode()->layerId() );
        colorAction->setProperty( "ruleKey", symbolNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString() );
        menu->addAction( colorAction );

        //add recent colors action
        QList<QgsRecentColorScheme *> recentSchemes;
        QgsApplication::colorSchemeRegistry()->schemes( recentSchemes );
        if ( !recentSchemes.isEmpty() )
        {
          QgsColorSwatchGridAction *recentColorAction = new QgsColorSwatchGridAction( recentSchemes.at( 0 ), menu, QStringLiteral( "symbology" ), menu );
          recentColorAction->setProperty( "layerId", symbolNode->layerNode()->layerId() );
          recentColorAction->setProperty( "ruleKey", symbolNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString() );
          recentColorAction->setDismissOnColorSelection( false );
          menu->addAction( recentColorAction );
          connect( recentColorAction, &QgsColorSwatchGridAction::colorChanged, this, &QgsAppLayerTreeViewMenuProvider::setSymbolLegendNodeColor );
        }

        menu->addSeparator();
      }

      QAction *editSymbolAction = new QAction( tr( "Edit Symbol…" ), menu );
      //store the layer id and rule key in action, so we can later retrieve the corresponding
      //legend node, if it still exists
      editSymbolAction->setProperty( "layerId", symbolNode->layerNode()->layerId() );
      editSymbolAction->setProperty( "ruleKey", symbolNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString() );
      connect( editSymbolAction, &QAction::triggered, this, &QgsAppLayerTreeViewMenuProvider::editSymbolLegendNodeSymbol );
      menu->addAction( editSymbolAction );
    }
  }

  return menu;
}