LCOV - code coverage report
Current view: top level - core/layertree - qgslayertreemodellegendnode.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 886 0.0 %
Date: 2021-04-10 08:29:14 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :   qgslayertreemodellegendnode.cpp
       3                 :            :   --------------------------------------
       4                 :            :   Date                 : August 2014
       5                 :            :   Copyright            : (C) 2014 by Martin Dobias
       6                 :            :   Email                : wonder dot sk at gmail dot com
       7                 :            : 
       8                 :            :   QgsWMSLegendNode     : Sandro Santilli < strk at keybit dot net >
       9                 :            : 
      10                 :            :  ***************************************************************************
      11                 :            :  *                                                                         *
      12                 :            :  *   This program is free software; you can redistribute it and/or modify  *
      13                 :            :  *   it under the terms of the GNU General Public License as published by  *
      14                 :            :  *   the Free Software Foundation; either version 2 of the License, or     *
      15                 :            :  *   (at your option) any later version.                                   *
      16                 :            :  *                                                                         *
      17                 :            :  ***************************************************************************/
      18                 :            : 
      19                 :            : #include "qgslayertreemodellegendnode.h"
      20                 :            : 
      21                 :            : #include "qgsdatadefinedsizelegend.h"
      22                 :            : #include "qgslayertree.h"
      23                 :            : #include "qgslayertreemodel.h"
      24                 :            : #include "qgslegendsettings.h"
      25                 :            : #include "qgsrasterlayer.h"
      26                 :            : #include "qgsrenderer.h"
      27                 :            : #include "qgssymbollayerutils.h"
      28                 :            : #include "qgsimageoperation.h"
      29                 :            : #include "qgsvectorlayer.h"
      30                 :            : #include "qgspointcloudlayer.h"
      31                 :            : #include "qgspointcloudrenderer.h"
      32                 :            : #include "qgsrasterrenderer.h"
      33                 :            : #include "qgsexpressioncontextutils.h"
      34                 :            : #include "qgsfeatureid.h"
      35                 :            : #include "qgslayoutitem.h"
      36                 :            : #include "qgsvectorlayerfeaturecounter.h"
      37                 :            : #include "qgsexpression.h"
      38                 :            : #include "qgstextrenderer.h"
      39                 :            : #include "qgssettings.h"
      40                 :            : #include "qgsfileutils.h"
      41                 :            : 
      42                 :            : #include <QBuffer>
      43                 :            : 
      44                 :          0 : QgsLayerTreeModelLegendNode::QgsLayerTreeModelLegendNode( QgsLayerTreeLayer *nodeL, QObject *parent )
      45                 :          0 :   : QObject( parent )
      46                 :          0 :   , mLayerNode( nodeL )
      47                 :          0 :   , mEmbeddedInParent( false )
      48                 :          0 : {
      49                 :          0 : }
      50                 :            : 
      51                 :          0 : QgsLayerTreeModel *QgsLayerTreeModelLegendNode::model() const
      52                 :            : {
      53                 :          0 :   return qobject_cast<QgsLayerTreeModel *>( parent() );
      54                 :            : }
      55                 :            : 
      56                 :          0 : Qt::ItemFlags QgsLayerTreeModelLegendNode::flags() const
      57                 :            : {
      58                 :          0 :   return Qt::ItemIsEnabled;
      59                 :            : }
      60                 :            : 
      61                 :          0 : bool QgsLayerTreeModelLegendNode::setData( const QVariant &value, int role )
      62                 :            : {
      63                 :          0 :   Q_UNUSED( value )
      64                 :            :   Q_UNUSED( role )
      65                 :          0 :   return false;
      66                 :            : }
      67                 :            : 
      68                 :          0 : QSizeF QgsLayerTreeModelLegendNode::userPatchSize() const
      69                 :            : {
      70                 :          0 :   if ( mEmbeddedInParent )
      71                 :          0 :     return mLayerNode->patchSize();
      72                 :            : 
      73                 :          0 :   return mUserSize;
      74                 :          0 : }
      75                 :            : 
      76                 :          0 : void QgsLayerTreeModelLegendNode::setUserPatchSize( QSizeF size )
      77                 :            : {
      78                 :          0 :   if ( mUserSize == size )
      79                 :          0 :     return;
      80                 :            : 
      81                 :          0 :   mUserSize = size;
      82                 :          0 :   emit sizeChanged();
      83                 :          0 : }
      84                 :            : 
      85                 :          0 : QgsLayerTreeModelLegendNode::ItemMetrics QgsLayerTreeModelLegendNode::draw( const QgsLegendSettings &settings, ItemContext *ctx )
      86                 :            : {
      87                 :          0 :   QFont symbolLabelFont = settings.style( QgsLegendStyle::SymbolLabel ).font();
      88                 :            : 
      89                 :          0 :   double textHeight = settings.fontHeightCharacterMM( symbolLabelFont, QChar( '0' ) );
      90                 :            :   // itemHeight here is not really item height, it is only for symbol
      91                 :            :   // vertical alignment purpose, i.e. OK take single line height
      92                 :            :   // if there are more lines, those run under the symbol
      93                 :          0 :   double itemHeight = std::max( static_cast< double >( ctx && ctx->patchSize.height() > 0 ? ctx->patchSize.height() : settings.symbolSize().height() ), textHeight );
      94                 :            : 
      95                 :          0 :   ItemMetrics im;
      96                 :          0 :   im.symbolSize = drawSymbol( settings, ctx, itemHeight );
      97                 :          0 :   im.labelSize = drawSymbolText( settings, ctx, im.symbolSize );
      98                 :            :   return im;
      99                 :          0 : }
     100                 :            : 
     101                 :          0 : QJsonObject QgsLayerTreeModelLegendNode::exportToJson( const QgsLegendSettings &settings, const QgsRenderContext &context )
     102                 :            : {
     103                 :          0 :   QJsonObject json = exportSymbolToJson( settings, context );
     104                 :          0 :   const QString text = data( Qt::DisplayRole ).toString();
     105                 :          0 :   json[ QStringLiteral( "title" ) ] = text;
     106                 :          0 :   return json;
     107                 :          0 : }
     108                 :            : 
     109                 :          0 : QSizeF QgsLayerTreeModelLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
     110                 :            : {
     111                 :          0 :   QIcon symbolIcon = data( Qt::DecorationRole ).value<QIcon>();
     112                 :          0 :   if ( symbolIcon.isNull() )
     113                 :          0 :     return QSizeF();
     114                 :            : 
     115                 :          0 :   QSizeF size = settings.symbolSize();
     116                 :          0 :   if ( ctx )
     117                 :            :   {
     118                 :          0 :     if ( ctx->patchSize.width() > 0 )
     119                 :          0 :       size.setWidth( ctx->patchSize.width( ) );
     120                 :          0 :     if ( ctx->patchSize.height() > 0 )
     121                 :          0 :       size.setHeight( ctx->patchSize.height( ) );
     122                 :          0 :   }
     123                 :            : 
     124                 :          0 :   if ( ctx && ctx->painter )
     125                 :            :   {
     126                 :          0 :     switch ( settings.symbolAlignment() )
     127                 :          0 :     {
     128                 :            :       case Qt::AlignLeft:
     129                 :            :       default:
     130                 :          0 :         symbolIcon.paint( ctx->painter,
     131                 :          0 :                           static_cast< int >( ctx->columnLeft ),
     132                 :          0 :                           static_cast< int >( ctx->top + ( itemHeight - size.height() ) / 2 ),
     133                 :          0 :                           static_cast< int >( size.width() ),
     134                 :          0 :                           static_cast< int >( size.height() ) );
     135                 :          0 :         break;
     136                 :            : 
     137                 :            :       case Qt::AlignRight:
     138                 :          0 :         symbolIcon.paint( ctx->painter,
     139                 :          0 :                           static_cast< int >( ctx->columnRight - size.width() ),
     140                 :          0 :                           static_cast< int >( ctx->top + ( itemHeight - size.height() ) / 2 ),
     141                 :          0 :                           static_cast< int >( size.width() ),
     142                 :          0 :                           static_cast< int >( size.height() ) );
     143                 :          0 :         break;
     144                 :            :     }
     145                 :          0 :   }
     146                 :          0 :   return size;
     147                 :          0 : }
     148                 :            : 
     149                 :          0 : QJsonObject QgsLayerTreeModelLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext & ) const
     150                 :            : {
     151                 :          0 :   const QIcon icon = data( Qt::DecorationRole ).value<QIcon>();
     152                 :          0 :   if ( icon.isNull() )
     153                 :          0 :     return QJsonObject();
     154                 :            : 
     155                 :          0 :   const QImage image( icon.pixmap( settings.symbolSize().width(), settings.symbolSize().height() ).toImage() );
     156                 :          0 :   QByteArray byteArray;
     157                 :          0 :   QBuffer buffer( &byteArray );
     158                 :          0 :   image.save( &buffer, "PNG" );
     159                 :          0 :   const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
     160                 :            : 
     161                 :          0 :   QJsonObject json;
     162                 :          0 :   json[ QStringLiteral( "icon" ) ] = base64;
     163                 :          0 :   return json;
     164                 :          0 : }
     165                 :            : 
     166                 :          0 : QSizeF QgsLayerTreeModelLegendNode::drawSymbolText( const QgsLegendSettings &settings, ItemContext *ctx, QSizeF symbolSize ) const
     167                 :            : {
     168                 :          0 :   QSizeF labelSize( 0, 0 );
     169                 :            : 
     170                 :          0 :   QFont symbolLabelFont = settings.style( QgsLegendStyle::SymbolLabel ).font();
     171                 :          0 :   double textHeight = settings.fontHeightCharacterMM( symbolLabelFont, QChar( '0' ) );
     172                 :          0 :   double textDescent = settings.fontDescentMillimeters( symbolLabelFont );
     173                 :            : 
     174                 :          0 :   QgsExpressionContext tempContext;
     175                 :            : 
     176                 :          0 :   const QStringList lines = settings.evaluateItemText( data( Qt::DisplayRole ).toString(), ctx && ctx->context ? ctx->context->expressionContext() : tempContext );
     177                 :            : 
     178                 :          0 :   labelSize.rheight() = lines.count() * textHeight + ( lines.count() - 1 ) * ( settings.lineSpacing() + textDescent );
     179                 :            : 
     180                 :          0 :   double labelXMin = 0.0;
     181                 :          0 :   double labelXMax = 0.0;
     182                 :          0 :   double labelY = 0.0;
     183                 :          0 :   if ( ctx && ctx->painter )
     184                 :            :   {
     185                 :          0 :     ctx->painter->setPen( settings.fontColor() );
     186                 :          0 :     switch ( settings.symbolAlignment() )
     187                 :          0 :     {
     188                 :            :       case Qt::AlignLeft:
     189                 :            :       default:
     190                 :          0 :         labelXMin = ctx->columnLeft + std::max( static_cast< double >( symbolSize.width() ), ctx->maxSiblingSymbolWidth )
     191                 :          0 :                     + settings.style( QgsLegendStyle::Symbol ).margin( QgsLegendStyle::Right )
     192                 :          0 :                     + settings.style( QgsLegendStyle::SymbolLabel ).margin( QgsLegendStyle::Left );
     193                 :          0 :         labelXMax = ctx->columnRight;
     194                 :          0 :         break;
     195                 :            : 
     196                 :            :       case Qt::AlignRight:
     197                 :          0 :         labelXMin = ctx->columnLeft;
     198                 :            :         // NOTE -- while the below calculations use the flipped margins from the style, that's only done because
     199                 :            :         // those are the only margins we expose and use for now! (and we expose them as generic margins, not side-specific
     200                 :            :         // ones) TODO when/if we expose other margin settings, these should be reversed...
     201                 :          0 :         labelXMax = ctx->columnRight - std::max( static_cast< double >( symbolSize.width() ), ctx->maxSiblingSymbolWidth )
     202                 :          0 :                     - settings.style( QgsLegendStyle::Symbol ).margin( QgsLegendStyle::Right )
     203                 :          0 :                     - settings.style( QgsLegendStyle::SymbolLabel ).margin( QgsLegendStyle::Left );
     204                 :          0 :         break;
     205                 :            :     }
     206                 :            : 
     207                 :          0 :     labelY = ctx->top;
     208                 :            : 
     209                 :            :     // Vertical alignment of label with symbol
     210                 :          0 :     if ( labelSize.height() < symbolSize.height() )
     211                 :          0 :       labelY += symbolSize.height() / 2 - labelSize.height() / 2;  // label centered with symbol
     212                 :            : 
     213                 :          0 :     labelY += textHeight;
     214                 :          0 :   }
     215                 :            : 
     216                 :          0 :   for ( QStringList::ConstIterator itemPart = lines.constBegin(); itemPart != lines.constEnd(); ++itemPart )
     217                 :            :   {
     218                 :          0 :     const double lineWidth = settings.textWidthMillimeters( symbolLabelFont, *itemPart );
     219                 :          0 :     labelSize.rwidth() = std::max( lineWidth, double( labelSize.width() ) );
     220                 :            : 
     221                 :          0 :     if ( ctx && ctx->painter )
     222                 :            :     {
     223                 :          0 :       switch ( settings.style( QgsLegendStyle::SymbolLabel ).alignment() )
     224                 :          0 :       {
     225                 :            :         case Qt::AlignLeft:
     226                 :            :         default:
     227                 :          0 :           settings.drawText( ctx->painter, labelXMin, labelY, *itemPart, symbolLabelFont );
     228                 :          0 :           break;
     229                 :            : 
     230                 :            :         case Qt::AlignRight:
     231                 :          0 :           settings.drawText( ctx->painter, labelXMax - lineWidth, labelY, *itemPart, symbolLabelFont );
     232                 :          0 :           break;
     233                 :            : 
     234                 :            :         case Qt::AlignHCenter:
     235                 :          0 :           settings.drawText( ctx->painter, labelXMin + ( labelXMax - labelXMin - lineWidth ) / 2.0, labelY, *itemPart, symbolLabelFont );
     236                 :          0 :           break;
     237                 :            :       }
     238                 :            : 
     239                 :          0 :       if ( itemPart != ( lines.end() - 1 ) )
     240                 :          0 :         labelY += textDescent + settings.lineSpacing() + textHeight;
     241                 :          0 :     }
     242                 :          0 :   }
     243                 :            : 
     244                 :            :   return labelSize;
     245                 :          0 : }
     246                 :            : 
     247                 :          0 : void QgsLayerTreeModelLegendNode::checkAllItems()
     248                 :            : {
     249                 :          0 :   checkAll( true );
     250                 :          0 : }
     251                 :            : 
     252                 :          0 : void QgsLayerTreeModelLegendNode::uncheckAllItems()
     253                 :            : {
     254                 :          0 :   checkAll( false );
     255                 :          0 : }
     256                 :            : 
     257                 :          0 : void QgsLayerTreeModelLegendNode::toggleAllItems()
     258                 :            : {
     259                 :          0 :   if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() ) )
     260                 :            :   {
     261                 :          0 :     if ( !vlayer->renderer() )
     262                 :          0 :       return;
     263                 :            : 
     264                 :          0 :     const QgsLegendSymbolList symbolList = vlayer->renderer()->legendSymbolItems();
     265                 :          0 :     for ( const auto &item : symbolList )
     266                 :            :     {
     267                 :          0 :       vlayer->renderer()->checkLegendSymbolItem( item.ruleKey(), ! vlayer->renderer()->legendSymbolItemChecked( item.ruleKey() ) );
     268                 :            :     }
     269                 :            : 
     270                 :          0 :     emit dataChanged();
     271                 :          0 :     vlayer->emitStyleChanged();
     272                 :          0 :     vlayer->triggerRepaint();
     273                 :          0 :   }
     274                 :          0 :   else if ( QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( mLayerNode->layer() ) )
     275                 :            :   {
     276                 :          0 :     if ( !pclayer->renderer() )
     277                 :          0 :       return;
     278                 :            : 
     279                 :          0 :     const QStringList ruleKeys = pclayer->renderer()->legendRuleKeys();
     280                 :          0 :     for ( const QString &rule : ruleKeys )
     281                 :            :     {
     282                 :          0 :       pclayer->renderer()->checkLegendItem( rule, !pclayer->renderer()->legendItemChecked( rule ) );
     283                 :            :     }
     284                 :            : 
     285                 :          0 :     emit dataChanged();
     286                 :          0 :     pclayer->emitStyleChanged();
     287                 :          0 :     pclayer->triggerRepaint();
     288                 :          0 :   }
     289                 :          0 : }
     290                 :            : 
     291                 :            : // -------------------------------------------------------------------------
     292                 :            : 
     293                 :            : double QgsSymbolLegendNode::MINIMUM_SIZE = -1.0;
     294                 :            : double QgsSymbolLegendNode::MAXIMUM_SIZE = -1.0;
     295                 :            : 
     296                 :          0 : QgsSymbolLegendNode::QgsSymbolLegendNode( QgsLayerTreeLayer *nodeLayer, const QgsLegendSymbolItem &item, QObject *parent )
     297                 :          0 :   : QgsLayerTreeModelLegendNode( nodeLayer, parent )
     298                 :          0 :   , mItem( item )
     299                 :          0 :   , mSymbolUsesMapUnits( false )
     300                 :          0 : {
     301                 :          0 :   const int iconSize = QgsLayerTreeModel::scaleIconSize( 16 );
     302                 :          0 :   mIconSize = QSize( iconSize, iconSize );
     303                 :            : 
     304                 :          0 :   if ( MINIMUM_SIZE < 0 )
     305                 :            :   {
     306                 :            :     // it's FAR too expensive to construct a QgsSettings object for every symbol node, especially for complex
     307                 :            :     // projects. So only read the valid size ranges once, and store them for subsequent use
     308                 :          0 :     QgsSettings settings;
     309                 :          0 :     MINIMUM_SIZE = settings.value( "/qgis/legendsymbolMinimumSize", 0.5 ).toDouble();
     310                 :          0 :     MAXIMUM_SIZE = settings.value( "/qgis/legendsymbolMaximumSize", 20.0 ).toDouble();
     311                 :          0 :   }
     312                 :            : 
     313                 :          0 :   updateLabel();
     314                 :          0 :   if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() ) )
     315                 :          0 :     connect( vl, &QgsVectorLayer::symbolFeatureCountMapChanged, this, &QgsSymbolLegendNode::updateLabel );
     316                 :            : 
     317                 :          0 :   connect( nodeLayer, &QObject::destroyed, this, [ = ]() { mLayerNode = nullptr; } );
     318                 :            : 
     319                 :          0 :   if ( const QgsSymbol *symbol = mItem.symbol() )
     320                 :            :   {
     321                 :          0 :     mSymbolUsesMapUnits = symbol->usesMapUnits();
     322                 :          0 :   }
     323                 :          0 : }
     324                 :            : 
     325                 :          0 : Qt::ItemFlags QgsSymbolLegendNode::flags() const
     326                 :            : {
     327                 :          0 :   if ( mItem.isCheckable() )
     328                 :          0 :     return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
     329                 :            :   else
     330                 :          0 :     return Qt::ItemIsEnabled;
     331                 :          0 : }
     332                 :            : 
     333                 :            : 
     334                 :          0 : QSize QgsSymbolLegendNode::minimumIconSize() const
     335                 :          0 : {
     336                 :          0 :   std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
     337                 :          0 :   return minimumIconSize( context.get() );
     338                 :          0 : }
     339                 :            : 
     340                 :          0 : QSize QgsSymbolLegendNode::minimumIconSize( QgsRenderContext *context ) const
     341                 :            : {
     342                 :          0 :   const int iconSize = QgsLayerTreeModel::scaleIconSize( 16 );
     343                 :          0 :   const int largeIconSize = QgsLayerTreeModel::scaleIconSize( 512 );
     344                 :          0 :   QSize minSz( iconSize, iconSize );
     345                 :          0 :   if ( mItem.symbol() && mItem.symbol()->type() == QgsSymbol::Marker )
     346                 :            :   {
     347                 :            :     // unusued width, height variables
     348                 :          0 :     double width = 0.0;
     349                 :          0 :     double height = 0.0;
     350                 :          0 :     std::unique_ptr<QgsSymbol> symbol( QgsSymbolLayerUtils::restrictedSizeSymbol( mItem.symbol(), MINIMUM_SIZE, MAXIMUM_SIZE, context, width, height ) );
     351                 :          0 :     minSz = QgsImageOperation::nonTransparentImageRect(
     352                 :          0 :               QgsSymbolLayerUtils::symbolPreviewPixmap( symbol ? symbol.get() : mItem.symbol(), QSize( largeIconSize, largeIconSize ), 0,
     353                 :          0 :                   context ).toImage(),
     354                 :          0 :               minSz,
     355                 :          0 :               true ).size();
     356                 :          0 :   }
     357                 :          0 :   else if ( mItem.symbol() && mItem.symbol()->type() == QgsSymbol::Line )
     358                 :            :   {
     359                 :          0 :     double width = 0.0;
     360                 :          0 :     double height = 0.0;
     361                 :          0 :     std::unique_ptr<QgsSymbol> symbol( QgsSymbolLayerUtils::restrictedSizeSymbol( mItem.symbol(), MINIMUM_SIZE, MAXIMUM_SIZE, context, width, height ) );
     362                 :          0 :     minSz = QgsImageOperation::nonTransparentImageRect(
     363                 :          0 :               QgsSymbolLayerUtils::symbolPreviewPixmap( symbol ? symbol.get() : mItem.symbol(), QSize( minSz.width(), largeIconSize ), 0,
     364                 :          0 :                   context ).toImage(),
     365                 :          0 :               minSz,
     366                 :          0 :               true ).size();
     367                 :          0 :   }
     368                 :            : 
     369                 :          0 :   if ( !mTextOnSymbolLabel.isEmpty() && context )
     370                 :            :   {
     371                 :          0 :     double w = QgsTextRenderer::textWidth( *context, mTextOnSymbolTextFormat, QStringList() << mTextOnSymbolLabel );
     372                 :          0 :     double h = QgsTextRenderer::textHeight( *context, mTextOnSymbolTextFormat, QStringList() << mTextOnSymbolLabel, QgsTextRenderer::Point );
     373                 :          0 :     int wInt = ceil( w ), hInt = ceil( h );
     374                 :          0 :     if ( wInt > minSz.width() ) minSz.setWidth( wInt );
     375                 :          0 :     if ( hInt > minSz.height() ) minSz.setHeight( hInt );
     376                 :          0 :   }
     377                 :            : 
     378                 :          0 :   return minSz;
     379                 :          0 : }
     380                 :            : 
     381                 :          0 : const QgsSymbol *QgsSymbolLegendNode::symbol() const
     382                 :            : {
     383                 :          0 :   return mItem.symbol();
     384                 :            : }
     385                 :            : 
     386                 :          0 : QString QgsSymbolLegendNode::symbolLabel() const
     387                 :            : {
     388                 :          0 :   QString label;
     389                 :          0 :   if ( mEmbeddedInParent )
     390                 :            :   {
     391                 :          0 :     QVariant legendlabel = mLayerNode->customProperty( QStringLiteral( "legend/title-label" ) );
     392                 :          0 :     QString layerName = legendlabel.isNull() ? mLayerNode->name() : legendlabel.toString();
     393                 :          0 :     label = mUserLabel.isEmpty() ? layerName : mUserLabel;
     394                 :          0 :   }
     395                 :            :   else
     396                 :          0 :     label = mUserLabel.isEmpty() ? mItem.label() : mUserLabel;
     397                 :          0 :   return label;
     398                 :          0 : }
     399                 :            : 
     400                 :          0 : QgsLegendPatchShape QgsSymbolLegendNode::patchShape() const
     401                 :            : {
     402                 :          0 :   if ( mEmbeddedInParent )
     403                 :            :   {
     404                 :          0 :     return mLayerNode->patchShape();
     405                 :            :   }
     406                 :            :   else
     407                 :            :   {
     408                 :          0 :     return mPatchShape;
     409                 :            :   }
     410                 :          0 : }
     411                 :            : 
     412                 :          0 : void QgsSymbolLegendNode::setPatchShape( const QgsLegendPatchShape &shape )
     413                 :            : {
     414                 :          0 :   mPatchShape = shape;
     415                 :          0 : }
     416                 :            : 
     417                 :          0 : QgsSymbol *QgsSymbolLegendNode::customSymbol() const
     418                 :            : {
     419                 :          0 :   return mCustomSymbol.get();
     420                 :            : }
     421                 :            : 
     422                 :          0 : void QgsSymbolLegendNode::setCustomSymbol( QgsSymbol *symbol )
     423                 :            : {
     424                 :          0 :   mCustomSymbol.reset( symbol );
     425                 :          0 : }
     426                 :            : 
     427                 :          0 : void QgsSymbolLegendNode::setSymbol( QgsSymbol *symbol )
     428                 :            : {
     429                 :          0 :   if ( !symbol )
     430                 :          0 :     return;
     431                 :            : 
     432                 :          0 :   std::unique_ptr< QgsSymbol > s( symbol ); // this method takes ownership of symbol
     433                 :          0 :   QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
     434                 :          0 :   if ( !vlayer || !vlayer->renderer() )
     435                 :          0 :     return;
     436                 :            : 
     437                 :          0 :   mItem.setSymbol( s.get() ); // doesn't transfer ownership
     438                 :          0 :   vlayer->renderer()->setLegendSymbolItem( mItem.ruleKey(), s.release() ); // DOES transfer ownership!
     439                 :            : 
     440                 :          0 :   mPixmap = QPixmap();
     441                 :            : 
     442                 :          0 :   emit dataChanged();
     443                 :          0 :   vlayer->triggerRepaint();
     444                 :          0 : }
     445                 :            : 
     446                 :          0 : QgsRenderContext *QgsLayerTreeModelLegendNode::createTemporaryRenderContext() const
     447                 :            : {
     448                 :          0 :   double scale = 0.0;
     449                 :          0 :   double mupp = 0.0;
     450                 :          0 :   int dpi = 0;
     451                 :          0 :   if ( auto *lModel = model() )
     452                 :          0 :     lModel->legendMapViewData( &mupp, &dpi, &scale );
     453                 :            : 
     454                 :          0 :   if ( qgsDoubleNear( mupp, 0.0 ) || dpi == 0 || qgsDoubleNear( scale, 0.0 ) )
     455                 :          0 :     return nullptr;
     456                 :            : 
     457                 :            :   // setup temporary render context
     458                 :          0 :   std::unique_ptr<QgsRenderContext> context = std::make_unique<QgsRenderContext>( );
     459                 :          0 :   context->setScaleFactor( dpi / 25.4 );
     460                 :          0 :   context->setRendererScale( scale );
     461                 :          0 :   context->setMapToPixel( QgsMapToPixel( mupp ) );
     462                 :          0 :   context->setFlag( QgsRenderContext::Antialiasing, true );
     463                 :          0 :   context->setFlag( QgsRenderContext::RenderSymbolPreview, true );
     464                 :          0 :   return context.release();
     465                 :          0 : }
     466                 :            : 
     467                 :          0 : void QgsLayerTreeModelLegendNode::checkAll( bool state )
     468                 :            : {
     469                 :          0 :   if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() ) )
     470                 :            :   {
     471                 :          0 :     if ( !vlayer->renderer() )
     472                 :          0 :       return;
     473                 :            : 
     474                 :          0 :     const QgsLegendSymbolList symbolList = vlayer->renderer()->legendSymbolItems();
     475                 :          0 :     for ( const auto &item : symbolList )
     476                 :            :     {
     477                 :          0 :       vlayer->renderer()->checkLegendSymbolItem( item.ruleKey(), state );
     478                 :            :     }
     479                 :            : 
     480                 :          0 :     emit dataChanged();
     481                 :          0 :     vlayer->emitStyleChanged();
     482                 :          0 :     vlayer->triggerRepaint();
     483                 :          0 :   }
     484                 :          0 :   else if ( QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( mLayerNode->layer() ) )
     485                 :            :   {
     486                 :          0 :     if ( !pclayer->renderer() )
     487                 :          0 :       return;
     488                 :            : 
     489                 :          0 :     const QStringList ruleKeys = pclayer->renderer()->legendRuleKeys();
     490                 :          0 :     for ( const QString &rule : ruleKeys )
     491                 :            :     {
     492                 :          0 :       pclayer->renderer()->checkLegendItem( rule, state );
     493                 :            :     }
     494                 :            : 
     495                 :          0 :     emit dataChanged();
     496                 :          0 :     pclayer->emitStyleChanged();
     497                 :          0 :     pclayer->triggerRepaint();
     498                 :          0 :   }
     499                 :          0 : }
     500                 :            : 
     501                 :          0 : QVariant QgsSymbolLegendNode::data( int role ) const
     502                 :            : {
     503                 :          0 :   if ( role == Qt::DisplayRole )
     504                 :            :   {
     505                 :          0 :     return mLabel;
     506                 :            :   }
     507                 :          0 :   else if ( role == Qt::EditRole )
     508                 :            :   {
     509                 :          0 :     return mUserLabel.isEmpty() ? mItem.label() : mUserLabel;
     510                 :            :   }
     511                 :          0 :   else if ( role == Qt::DecorationRole )
     512                 :            :   {
     513                 :          0 :     if ( mPixmap.isNull() || mPixmap.size() != mIconSize )
     514                 :            :     {
     515                 :          0 :       QPixmap pix;
     516                 :          0 :       if ( mItem.symbol() )
     517                 :            :       {
     518                 :          0 :         std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
     519                 :            : 
     520                 :            :         // unusued width, height variables
     521                 :          0 :         double width = 0.0;
     522                 :          0 :         double height = 0.0;
     523                 :          0 :         std::unique_ptr<QgsSymbol> symbol( QgsSymbolLayerUtils::restrictedSizeSymbol( mItem.symbol(), MINIMUM_SIZE, MAXIMUM_SIZE, context.get(), width, height ) );
     524                 :          0 :         pix = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol ? symbol.get() : mItem.symbol(), mIconSize, 0, context.get() );
     525                 :            : 
     526                 :          0 :         if ( !mTextOnSymbolLabel.isEmpty() && context )
     527                 :            :         {
     528                 :          0 :           QPainter painter( &pix );
     529                 :          0 :           painter.setRenderHint( QPainter::Antialiasing );
     530                 :          0 :           context->setPainter( &painter );
     531                 :          0 :           QFontMetricsF fm( mTextOnSymbolTextFormat.scaledFont( *context ) );
     532                 :          0 :           qreal yBaselineVCenter = ( mIconSize.height() + fm.ascent() - fm.descent() ) / 2;
     533                 :          0 :           QgsTextRenderer::drawText( QPointF( mIconSize.width() / 2, yBaselineVCenter ), 0, QgsTextRenderer::AlignCenter,
     534                 :          0 :                                      QStringList() << mTextOnSymbolLabel, *context, mTextOnSymbolTextFormat );
     535                 :          0 :         }
     536                 :          0 :       }
     537                 :            :       else
     538                 :            :       {
     539                 :          0 :         pix = QPixmap( mIconSize );
     540                 :          0 :         pix.fill( Qt::transparent );
     541                 :            :       }
     542                 :            : 
     543                 :          0 :       mPixmap = pix;
     544                 :          0 :     }
     545                 :          0 :     return mPixmap;
     546                 :            :   }
     547                 :          0 :   else if ( role == Qt::CheckStateRole )
     548                 :            :   {
     549                 :          0 :     if ( !mItem.isCheckable() )
     550                 :          0 :       return QVariant();
     551                 :            : 
     552                 :          0 :     if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() ) )
     553                 :            :     {
     554                 :          0 :       if ( !vlayer->renderer() )
     555                 :          0 :         return QVariant();
     556                 :            : 
     557                 :          0 :       return vlayer->renderer()->legendSymbolItemChecked( mItem.ruleKey() ) ? Qt::Checked : Qt::Unchecked;
     558                 :            :     }
     559                 :          0 :   }
     560                 :          0 :   else if ( role == RuleKeyRole )
     561                 :            :   {
     562                 :          0 :     return mItem.ruleKey();
     563                 :            :   }
     564                 :          0 :   else if ( role == ParentRuleKeyRole )
     565                 :            :   {
     566                 :          0 :     return mItem.parentRuleKey();
     567                 :            :   }
     568                 :          0 :   else if ( role == QgsLayerTreeModelLegendNode::NodeTypeRole )
     569                 :            :   {
     570                 :          0 :     return QgsLayerTreeModelLegendNode::SymbolLegend;
     571                 :            :   }
     572                 :            : 
     573                 :          0 :   return QVariant();
     574                 :          0 : }
     575                 :            : 
     576                 :          0 : bool QgsSymbolLegendNode::setData( const QVariant &value, int role )
     577                 :            : {
     578                 :          0 :   if ( role != Qt::CheckStateRole )
     579                 :          0 :     return false;
     580                 :            : 
     581                 :          0 :   if ( !mItem.isCheckable() )
     582                 :          0 :     return false;
     583                 :            : 
     584                 :          0 :   QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
     585                 :          0 :   if ( !vlayer || !vlayer->renderer() )
     586                 :          0 :     return false;
     587                 :            : 
     588                 :          0 :   vlayer->renderer()->checkLegendSymbolItem( mItem.ruleKey(), value == Qt::Checked );
     589                 :            : 
     590                 :          0 :   emit dataChanged();
     591                 :          0 :   vlayer->emitStyleChanged();
     592                 :            : 
     593                 :          0 :   vlayer->triggerRepaint();
     594                 :            : 
     595                 :          0 :   return true;
     596                 :          0 : }
     597                 :            : 
     598                 :            : 
     599                 :            : 
     600                 :          0 : QSizeF QgsSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
     601                 :            : {
     602                 :          0 :   QgsSymbol *s = mCustomSymbol ? mCustomSymbol.get() : mItem.symbol();
     603                 :          0 :   if ( !s )
     604                 :            :   {
     605                 :          0 :     return QSizeF();
     606                 :            :   }
     607                 :            : 
     608                 :            :   // setup temporary render context
     609                 :          0 :   QgsRenderContext *context = nullptr;
     610                 :          0 :   std::unique_ptr< QgsRenderContext > tempRenderContext;
     611                 :          0 :   QgsLegendPatchShape patchShape = ctx ? ctx->patchShape : QgsLegendPatchShape();
     612                 :          0 :   if ( ctx && ctx->context )
     613                 :          0 :     context = ctx->context;
     614                 :            :   else
     615                 :            :   {
     616                 :          0 :     tempRenderContext = std::make_unique< QgsRenderContext >();
     617                 :            :     // QGIS 4.0 - make ItemContext compulsory, so we don't have to construct temporary render contexts here
     618                 :            :     Q_NOWARN_DEPRECATED_PUSH
     619                 :          0 :     tempRenderContext->setScaleFactor( settings.dpi() / 25.4 );
     620                 :          0 :     tempRenderContext->setRendererScale( settings.mapScale() );
     621                 :          0 :     tempRenderContext->setFlag( QgsRenderContext::Antialiasing, true );
     622                 :          0 :     tempRenderContext->setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * tempRenderContext->scaleFactor() ) ) );
     623                 :            :     Q_NOWARN_DEPRECATED_POP
     624                 :          0 :     tempRenderContext->setForceVectorOutput( true );
     625                 :          0 :     tempRenderContext->setPainter( ctx ? ctx->painter : nullptr );
     626                 :            : 
     627                 :            :     // setup a minimal expression context
     628                 :          0 :     QgsExpressionContext expContext;
     629                 :          0 :     expContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) );
     630                 :          0 :     tempRenderContext->setExpressionContext( expContext );
     631                 :          0 :     context = tempRenderContext.get();
     632                 :          0 :   }
     633                 :            : 
     634                 :            :   //Consider symbol size for point markers
     635                 :          0 :   const double desiredHeight = ctx && ctx->patchSize.height() > 0 ? ctx->patchSize.height() : settings.symbolSize().height();
     636                 :          0 :   const double desiredWidth = ctx && ctx->patchSize.width() > 0 ? ctx->patchSize.width() : settings.symbolSize().width();
     637                 :          0 :   double height = desiredHeight;
     638                 :          0 :   double width = desiredWidth;
     639                 :            : 
     640                 :            :   //Center small marker symbols
     641                 :          0 :   double widthOffset = 0;
     642                 :          0 :   double heightOffset = 0;
     643                 :            : 
     644                 :          0 :   double maxSymbolSize = settings.maximumSymbolSize();
     645                 :          0 :   double minSymbolSize = settings.minimumSymbolSize();
     646                 :            : 
     647                 :          0 :   if ( QgsMarkerSymbol *markerSymbol = dynamic_cast<QgsMarkerSymbol *>( s ) )
     648                 :            :   {
     649                 :          0 :     double size = markerSymbol->size( *context ) / context->scaleFactor();
     650                 :          0 :     height = size;
     651                 :          0 :     width = size;
     652                 :          0 :   }
     653                 :            : 
     654                 :          0 :   std::unique_ptr<QgsSymbol> minMaxSizeSymbol( QgsSymbolLayerUtils::restrictedSizeSymbol( s, minSymbolSize, maxSymbolSize, context, width, height ) );
     655                 :          0 :   if ( minMaxSizeSymbol )
     656                 :            :   {
     657                 :          0 :     s = minMaxSizeSymbol.get();
     658                 :          0 :   }
     659                 :            : 
     660                 :          0 :   if ( s->type() == QgsSymbol::Marker )
     661                 :            :   {
     662                 :          0 :     if ( width < desiredWidth )
     663                 :            :     {
     664                 :          0 :       widthOffset = ( desiredWidth - width ) / 2.0;
     665                 :          0 :     }
     666                 :          0 :     if ( height < desiredHeight )
     667                 :            :     {
     668                 :          0 :       heightOffset = ( desiredHeight - height ) / 2.0;
     669                 :          0 :     }
     670                 :          0 :   }
     671                 :          0 :   if ( ctx && ctx->painter )
     672                 :            :   {
     673                 :          0 :     double currentYCoord = ctx->top + ( itemHeight - desiredHeight ) / 2;
     674                 :          0 :     QPainter *p = ctx->painter;
     675                 :            : 
     676                 :            :     //setup painter scaling to dots so that raster symbology is drawn to scale
     677                 :          0 :     double dotsPerMM = context->scaleFactor();
     678                 :            : 
     679                 :          0 :     int opacity = 255;
     680                 :          0 :     if ( QgsMapLayer *layer = layerNode()->layer() )
     681                 :          0 :       opacity = static_cast<int >( std::round( 255 * layer->opacity() ) );
     682                 :            : 
     683                 :          0 :     QgsScopedQPainterState painterState( p );
     684                 :          0 :     context->setPainterFlagsUsingContext( p );
     685                 :            : 
     686                 :          0 :     switch ( settings.symbolAlignment() )
     687                 :          0 :     {
     688                 :            :       case Qt::AlignLeft:
     689                 :            :       default:
     690                 :          0 :         p->translate( ctx->columnLeft + widthOffset, currentYCoord + heightOffset );
     691                 :          0 :         break;
     692                 :            :       case Qt::AlignRight:
     693                 :          0 :         p->translate( ctx->columnRight - widthOffset - width, currentYCoord + heightOffset );
     694                 :          0 :         break;
     695                 :            :     }
     696                 :            : 
     697                 :          0 :     p->scale( 1.0 / dotsPerMM, 1.0 / dotsPerMM );
     698                 :            :     Q_NOWARN_DEPRECATED_PUSH
     699                 :            :     // QGIS 4.0 -- ctx->context will be mandatory
     700                 :          0 :     const bool useAdvancedEffects = ctx->context ? ctx->context->flags() & QgsRenderContext::UseAdvancedEffects : settings.useAdvancedEffects();
     701                 :            :     Q_NOWARN_DEPRECATED_POP
     702                 :          0 :     if ( opacity != 255 && useAdvancedEffects )
     703                 :            :     {
     704                 :          0 :       const int maxBleed = static_cast< int >( std::ceil( QgsSymbolLayerUtils::estimateMaxSymbolBleed( s, *context ) ) );
     705                 :            : 
     706                 :            :       //semi transparent layer, so need to draw symbol to an image (to flatten it first)
     707                 :            :       //create image which is same size as legend rect, in case symbol bleeds outside its allotted space
     708                 :          0 :       const QSize symbolSize( static_cast< int >( std::round( width * dotsPerMM ) ), static_cast<int >( std::round( height * dotsPerMM ) ) );
     709                 :          0 :       const QSize tempImageSize( symbolSize.width() + maxBleed * 2, symbolSize.height() + maxBleed * 2 );
     710                 :          0 :       QImage tempImage = QImage( tempImageSize, QImage::Format_ARGB32 );
     711                 :          0 :       tempImage.fill( Qt::transparent );
     712                 :          0 :       QPainter imagePainter( &tempImage );
     713                 :          0 :       context->setPainterFlagsUsingContext( &imagePainter );
     714                 :            : 
     715                 :          0 :       context->setPainter( &imagePainter );
     716                 :          0 :       imagePainter.translate( maxBleed, maxBleed );
     717                 :          0 :       s->drawPreviewIcon( &imagePainter, symbolSize, context, false, nullptr, &patchShape );
     718                 :          0 :       imagePainter.translate( -maxBleed, -maxBleed );
     719                 :          0 :       context->setPainter( ctx->painter );
     720                 :            :       //reduce opacity of image
     721                 :          0 :       imagePainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
     722                 :          0 :       imagePainter.fillRect( tempImage.rect(), QColor( 0, 0, 0, opacity ) );
     723                 :          0 :       imagePainter.end();
     724                 :            :       //draw rendered symbol image
     725                 :          0 :       p->drawImage( -maxBleed, -maxBleed, tempImage );
     726                 :          0 :     }
     727                 :            :     else
     728                 :            :     {
     729                 :          0 :       s->drawPreviewIcon( p, QSize( static_cast< int >( std::round( width * dotsPerMM ) ), static_cast< int >( std::round( height * dotsPerMM ) ) ), context, false, nullptr, &patchShape );
     730                 :            :     }
     731                 :            : 
     732                 :          0 :     if ( !mTextOnSymbolLabel.isEmpty() )
     733                 :            :     {
     734                 :          0 :       QFontMetricsF fm( mTextOnSymbolTextFormat.scaledFont( *context ) );
     735                 :          0 :       qreal yBaselineVCenter = ( height * dotsPerMM + fm.ascent() - fm.descent() ) / 2;
     736                 :          0 :       QgsTextRenderer::drawText( QPointF( width * dotsPerMM / 2, yBaselineVCenter ), 0, QgsTextRenderer::AlignCenter,
     737                 :          0 :                                  QStringList() << mTextOnSymbolLabel, *context, mTextOnSymbolTextFormat );
     738                 :          0 :     }
     739                 :          0 :   }
     740                 :            : 
     741                 :          0 :   return QSizeF( std::max( width + 2 * widthOffset, static_cast< double >( desiredWidth ) ),
     742                 :          0 :                  std::max( height + 2 * heightOffset, static_cast< double >( desiredHeight ) ) );
     743                 :          0 : }
     744                 :            : 
     745                 :          0 : QJsonObject QgsSymbolLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context ) const
     746                 :            : {
     747                 :          0 :   const QgsSymbol *s = mCustomSymbol ? mCustomSymbol.get() : mItem.symbol();
     748                 :          0 :   if ( !s )
     749                 :            :   {
     750                 :          0 :     return QJsonObject();
     751                 :            :   }
     752                 :            : 
     753                 :            : 
     754                 :          0 :   QgsRenderContext ctx;
     755                 :            :   // QGIS 4.0 - use render context directly here, and note in the dox that the context must be correctly setup
     756                 :            :   Q_NOWARN_DEPRECATED_PUSH
     757                 :          0 :   ctx.setScaleFactor( settings.dpi() / 25.4 );
     758                 :          0 :   ctx.setRendererScale( settings.mapScale() );
     759                 :          0 :   ctx.setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * ctx.scaleFactor() ) ) );
     760                 :          0 :   ctx.setForceVectorOutput( true );
     761                 :          0 :   ctx.setFlag( QgsRenderContext::Antialiasing, context.flags() & QgsRenderContext::Antialiasing );
     762                 :          0 :   ctx.setFlag( QgsRenderContext::LosslessImageRendering, context.flags() & QgsRenderContext::LosslessImageRendering );
     763                 :            : 
     764                 :            :   Q_NOWARN_DEPRECATED_POP
     765                 :            : 
     766                 :            :   // ensure that a minimal expression context is available
     767                 :          0 :   QgsExpressionContext expContext = context.expressionContext();
     768                 :          0 :   expContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) );
     769                 :          0 :   ctx.setExpressionContext( expContext );
     770                 :            : 
     771                 :          0 :   const QPixmap pix = QgsSymbolLayerUtils::symbolPreviewPixmap( mItem.symbol(), minimumIconSize(), 0, &ctx );
     772                 :          0 :   QImage img( pix.toImage().convertToFormat( QImage::Format_ARGB32_Premultiplied ) );
     773                 :            : 
     774                 :          0 :   int opacity = 255;
     775                 :          0 :   if ( QgsMapLayer *layer = layerNode()->layer() )
     776                 :          0 :     opacity = ( 255 * layer->opacity() );
     777                 :            : 
     778                 :          0 :   if ( opacity != 255 )
     779                 :            :   {
     780                 :          0 :     QPainter painter;
     781                 :          0 :     painter.begin( &img );
     782                 :          0 :     painter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
     783                 :          0 :     painter.fillRect( pix.rect(), QColor( 0, 0, 0, opacity ) );
     784                 :          0 :     painter.end();
     785                 :          0 :   }
     786                 :            : 
     787                 :          0 :   QByteArray byteArray;
     788                 :          0 :   QBuffer buffer( &byteArray );
     789                 :          0 :   img.save( &buffer, "PNG" );
     790                 :          0 :   const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
     791                 :            : 
     792                 :          0 :   QJsonObject json;
     793                 :          0 :   json[ QStringLiteral( "icon" ) ] = base64;
     794                 :          0 :   if ( mItem.scaleMaxDenom() > 0 )
     795                 :            :   {
     796                 :          0 :     json[ QStringLiteral( "scaleMaxDenom" ) ] = mItem.scaleMaxDenom();
     797                 :          0 :   }
     798                 :          0 :   if ( mItem.scaleMinDenom() > 0 )
     799                 :            :   {
     800                 :          0 :     json[ QStringLiteral( "scaleMinDenom" ) ] = mItem.scaleMinDenom();
     801                 :          0 :   }
     802                 :          0 :   mItem.scaleMaxDenom();
     803                 :          0 :   return json;
     804                 :          0 : }
     805                 :            : 
     806                 :          0 : void QgsSymbolLegendNode::setEmbeddedInParent( bool embedded )
     807                 :            : {
     808                 :          0 :   QgsLayerTreeModelLegendNode::setEmbeddedInParent( embedded );
     809                 :          0 :   updateLabel();
     810                 :          0 : }
     811                 :            : 
     812                 :            : 
     813                 :          0 : void QgsSymbolLegendNode::invalidateMapBasedData()
     814                 :            : {
     815                 :          0 :   if ( mSymbolUsesMapUnits )
     816                 :            :   {
     817                 :          0 :     mPixmap = QPixmap();
     818                 :          0 :     emit dataChanged();
     819                 :          0 :   }
     820                 :          0 : }
     821                 :            : 
     822                 :            : 
     823                 :          0 : void QgsSymbolLegendNode::updateLabel()
     824                 :            : {
     825                 :          0 :   if ( !mLayerNode )
     826                 :          0 :     return;
     827                 :            : 
     828                 :          0 :   bool showFeatureCount = mLayerNode->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toBool();
     829                 :          0 :   QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
     830                 :          0 :   mLabel = symbolLabel();
     831                 :            : 
     832                 :          0 :   if ( showFeatureCount && vl )
     833                 :            :   {
     834                 :          0 :     qlonglong count = mEmbeddedInParent ? vl->featureCount() : vl->featureCount( mItem.ruleKey() ) ;
     835                 :          0 :     mLabel += QStringLiteral( " [%1]" ).arg( count != -1 ? QLocale().toString( count ) : tr( "N/A" ) );
     836                 :          0 :   }
     837                 :            : 
     838                 :          0 :   emit dataChanged();
     839                 :          0 : }
     840                 :            : 
     841                 :          0 : QString QgsSymbolLegendNode::evaluateLabel( const QgsExpressionContext &context, const QString &label )
     842                 :            : {
     843                 :          0 :   if ( !mLayerNode )
     844                 :          0 :     return QString();
     845                 :            : 
     846                 :          0 :   QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
     847                 :            : 
     848                 :          0 :   if ( vl )
     849                 :            :   {
     850                 :          0 :     QgsExpressionContext contextCopy = QgsExpressionContext( context );
     851                 :          0 :     QgsExpressionContextScope *symbolScope = createSymbolScope();
     852                 :          0 :     contextCopy.appendScope( symbolScope );
     853                 :          0 :     contextCopy.appendScope( vl->createExpressionContextScope() );
     854                 :            : 
     855                 :          0 :     if ( label.isEmpty() )
     856                 :            :     {
     857                 :          0 :       if ( ! mLayerNode->labelExpression().isEmpty() )
     858                 :          0 :         mLabel = QgsExpression::replaceExpressionText( "[%" + mLayerNode->labelExpression() + "%]", &contextCopy );
     859                 :          0 :       else if ( mLabel.contains( "[%" ) )
     860                 :            :       {
     861                 :          0 :         const QString symLabel = symbolLabel();
     862                 :          0 :         mLabel = QgsExpression::replaceExpressionText( symLabel, &contextCopy );
     863                 :          0 :       }
     864                 :          0 :       return mLabel;
     865                 :            :     }
     866                 :            :     else
     867                 :            :     {
     868                 :          0 :       QString eLabel;
     869                 :          0 :       if ( ! mLayerNode->labelExpression().isEmpty() )
     870                 :          0 :         eLabel = QgsExpression::replaceExpressionText( label + "[%" + mLayerNode->labelExpression() + "%]", &contextCopy );
     871                 :          0 :       else if ( label.contains( "[%" ) )
     872                 :          0 :         eLabel = QgsExpression::replaceExpressionText( label, &contextCopy );
     873                 :          0 :       return eLabel;
     874                 :          0 :     }
     875                 :          0 :   }
     876                 :          0 :   return mLabel;
     877                 :          0 : }
     878                 :            : 
     879                 :          0 : QgsExpressionContextScope *QgsSymbolLegendNode::createSymbolScope() const
     880                 :            : {
     881                 :          0 :   QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
     882                 :            : 
     883                 :          0 :   QgsExpressionContextScope *scope = new QgsExpressionContextScope( tr( "Symbol scope" ) );
     884                 :          0 :   scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_label" ), symbolLabel().remove( "[%" ).remove( "%]" ), true ) );
     885                 :          0 :   scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_id" ), mItem.ruleKey(), true ) );
     886                 :          0 :   if ( vl )
     887                 :            :   {
     888                 :          0 :     scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_count" ), QVariant::fromValue( vl->featureCount( mItem.ruleKey() ) ), true ) );
     889                 :          0 :   }
     890                 :          0 :   return scope;
     891                 :          0 : }
     892                 :            : 
     893                 :            : // -------------------------------------------------------------------------
     894                 :            : 
     895                 :            : 
     896                 :          0 : QgsSimpleLegendNode::QgsSimpleLegendNode( QgsLayerTreeLayer *nodeLayer, const QString &label, const QIcon &icon, QObject *parent, const QString &key )
     897                 :          0 :   : QgsLayerTreeModelLegendNode( nodeLayer, parent )
     898                 :          0 :   , mLabel( label )
     899                 :          0 :   , mIcon( icon )
     900                 :          0 :   , mKey( key )
     901                 :          0 : {
     902                 :          0 : }
     903                 :            : 
     904                 :          0 : QVariant QgsSimpleLegendNode::data( int role ) const
     905                 :            : {
     906                 :          0 :   if ( role == Qt::DisplayRole || role == Qt::EditRole )
     907                 :          0 :     return mUserLabel.isEmpty() ? mLabel : mUserLabel;
     908                 :          0 :   else if ( role == Qt::DecorationRole )
     909                 :          0 :     return mIcon;
     910                 :          0 :   else if ( role == RuleKeyRole && !mKey.isEmpty() )
     911                 :          0 :     return mKey;
     912                 :          0 :   else if ( role == QgsLayerTreeModelLegendNode::NodeTypeRole )
     913                 :          0 :     return QgsLayerTreeModelLegendNode::SimpleLegend;
     914                 :            :   else
     915                 :          0 :     return QVariant();
     916                 :          0 : }
     917                 :            : 
     918                 :            : 
     919                 :            : // -------------------------------------------------------------------------
     920                 :            : 
     921                 :          0 : QgsImageLegendNode::QgsImageLegendNode( QgsLayerTreeLayer *nodeLayer, const QImage &img, QObject *parent )
     922                 :          0 :   : QgsLayerTreeModelLegendNode( nodeLayer, parent )
     923                 :          0 :   , mImage( img )
     924                 :          0 : {
     925                 :          0 : }
     926                 :            : 
     927                 :          0 : QVariant QgsImageLegendNode::data( int role ) const
     928                 :            : {
     929                 :          0 :   if ( role == Qt::DecorationRole )
     930                 :            :   {
     931                 :          0 :     return QPixmap::fromImage( mImage );
     932                 :            :   }
     933                 :          0 :   else if ( role == Qt::SizeHintRole )
     934                 :            :   {
     935                 :          0 :     return mImage.size();
     936                 :            :   }
     937                 :          0 :   else if ( role == QgsLayerTreeModelLegendNode::NodeTypeRole )
     938                 :            :   {
     939                 :          0 :     return QgsLayerTreeModelLegendNode::ImageLegend;
     940                 :            :   }
     941                 :          0 :   return QVariant();
     942                 :          0 : }
     943                 :            : 
     944                 :          0 : QSizeF QgsImageLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
     945                 :            : {
     946                 :            :   Q_UNUSED( itemHeight )
     947                 :            : 
     948                 :          0 :   if ( ctx && ctx->painter )
     949                 :            :   {
     950                 :          0 :     switch ( settings.symbolAlignment() )
     951                 :          0 :     {
     952                 :            :       case Qt::AlignLeft:
     953                 :            :       default:
     954                 :          0 :         ctx->painter->drawImage( QRectF( ctx->columnLeft, ctx->top, settings.wmsLegendSize().width(), settings.wmsLegendSize().height() ),
     955                 :          0 :                                  mImage, QRectF( 0, 0, mImage.width(), mImage.height() ) );
     956                 :          0 :         break;
     957                 :            : 
     958                 :            :       case Qt::AlignRight:
     959                 :          0 :         ctx->painter->drawImage( QRectF( ctx->columnRight - settings.wmsLegendSize().width(), ctx->top, settings.wmsLegendSize().width(), settings.wmsLegendSize().height() ),
     960                 :          0 :                                  mImage, QRectF( 0, 0, mImage.width(), mImage.height() ) );
     961                 :          0 :         break;
     962                 :            :     }
     963                 :          0 :   }
     964                 :          0 :   return settings.wmsLegendSize();
     965                 :            : }
     966                 :            : 
     967                 :          0 : QJsonObject QgsImageLegendNode::exportSymbolToJson( const QgsLegendSettings &, const QgsRenderContext & ) const
     968                 :            : {
     969                 :          0 :   QByteArray byteArray;
     970                 :          0 :   QBuffer buffer( &byteArray );
     971                 :          0 :   mImage.save( &buffer, "PNG" );
     972                 :          0 :   const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
     973                 :            : 
     974                 :          0 :   QJsonObject json;
     975                 :          0 :   json[ QStringLiteral( "icon" ) ] = base64;
     976                 :          0 :   return json;
     977                 :          0 : }
     978                 :            : 
     979                 :            : // -------------------------------------------------------------------------
     980                 :            : 
     981                 :          0 : QgsRasterSymbolLegendNode::QgsRasterSymbolLegendNode( QgsLayerTreeLayer *nodeLayer, const QColor &color, const QString &label, QObject *parent, bool isCheckable, const QString &ruleKey )
     982                 :          0 :   : QgsLayerTreeModelLegendNode( nodeLayer, parent )
     983                 :          0 :   , mColor( color )
     984                 :          0 :   , mLabel( label )
     985                 :          0 :   , mCheckable( isCheckable )
     986                 :          0 :   , mRuleKey( ruleKey )
     987                 :          0 : {
     988                 :          0 : }
     989                 :            : 
     990                 :          0 : Qt::ItemFlags QgsRasterSymbolLegendNode::flags() const
     991                 :            : {
     992                 :          0 :   if ( mCheckable )
     993                 :          0 :     return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
     994                 :            :   else
     995                 :          0 :     return Qt::ItemIsEnabled;
     996                 :          0 : }
     997                 :            : 
     998                 :          0 : QVariant QgsRasterSymbolLegendNode::data( int role ) const
     999                 :            : {
    1000                 :          0 :   switch ( role )
    1001                 :            :   {
    1002                 :            :     case Qt::DecorationRole:
    1003                 :            :     {
    1004                 :          0 :       const int iconSize = QgsLayerTreeModel::scaleIconSize( 16 ); // TODO: configurable?
    1005                 :          0 :       QPixmap pix( iconSize, iconSize );
    1006                 :          0 :       pix.fill( mColor );
    1007                 :          0 :       return QIcon( pix );
    1008                 :          0 :     }
    1009                 :            : 
    1010                 :            :     case Qt::DisplayRole:
    1011                 :            :     case Qt::EditRole:
    1012                 :          0 :       return mUserLabel.isEmpty() ? mLabel : mUserLabel;
    1013                 :            : 
    1014                 :            :     case QgsLayerTreeModelLegendNode::NodeTypeRole:
    1015                 :          0 :       return QgsLayerTreeModelLegendNode::RasterSymbolLegend;
    1016                 :            : 
    1017                 :            :     case QgsLayerTreeModelLegendNode::RuleKeyRole:
    1018                 :          0 :       return mRuleKey;
    1019                 :            : 
    1020                 :            :     case Qt::CheckStateRole:
    1021                 :            :     {
    1022                 :          0 :       if ( !mCheckable )
    1023                 :          0 :         return QVariant();
    1024                 :            : 
    1025                 :          0 :       if ( QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( mLayerNode->layer() ) )
    1026                 :            :       {
    1027                 :          0 :         if ( !pclayer->renderer() )
    1028                 :          0 :           return QVariant();
    1029                 :            : 
    1030                 :          0 :         return pclayer->renderer()->legendItemChecked( mRuleKey ) ? Qt::Checked : Qt::Unchecked;
    1031                 :            :       }
    1032                 :            : 
    1033                 :          0 :       return QVariant();
    1034                 :            :     }
    1035                 :            : 
    1036                 :            :     default:
    1037                 :          0 :       return QVariant();
    1038                 :            :   }
    1039                 :          0 : }
    1040                 :            : 
    1041                 :          0 : bool QgsRasterSymbolLegendNode::setData( const QVariant &value, int role )
    1042                 :            : {
    1043                 :          0 :   if ( role != Qt::CheckStateRole )
    1044                 :          0 :     return false;
    1045                 :            : 
    1046                 :          0 :   if ( !mCheckable )
    1047                 :          0 :     return false;
    1048                 :            : 
    1049                 :          0 :   if ( QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( mLayerNode->layer() ) )
    1050                 :            :   {
    1051                 :          0 :     if ( !pclayer->renderer() )
    1052                 :          0 :       return false;
    1053                 :            : 
    1054                 :          0 :     pclayer->renderer()->checkLegendItem( mRuleKey, value == Qt::Checked );
    1055                 :            : 
    1056                 :          0 :     emit dataChanged();
    1057                 :          0 :     pclayer->emitStyleChanged();
    1058                 :            : 
    1059                 :          0 :     pclayer->triggerRepaint();
    1060                 :          0 :     return true;
    1061                 :            :   }
    1062                 :            :   else
    1063                 :            :   {
    1064                 :          0 :     return false;
    1065                 :            :   }
    1066                 :          0 : }
    1067                 :            : 
    1068                 :            : 
    1069                 :          0 : QSizeF QgsRasterSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
    1070                 :            : {
    1071                 :          0 :   QSizeF size = settings.symbolSize();
    1072                 :          0 :   double offsetX = 0;
    1073                 :          0 :   if ( ctx )
    1074                 :            :   {
    1075                 :          0 :     if ( ctx->patchSize.width() > 0 )
    1076                 :            :     {
    1077                 :          0 :       if ( ctx->patchSize.width() < size.width() )
    1078                 :          0 :         offsetX = ( size.width() - ctx->patchSize.width() ) / 2.0;
    1079                 :          0 :       size.setWidth( ctx->patchSize.width() );
    1080                 :          0 :     }
    1081                 :          0 :     if ( ctx->patchSize.height() > 0 )
    1082                 :            :     {
    1083                 :          0 :       size.setHeight( ctx->patchSize.height() );
    1084                 :          0 :     }
    1085                 :          0 :   }
    1086                 :            : 
    1087                 :          0 :   if ( ctx && ctx->painter )
    1088                 :            :   {
    1089                 :          0 :     QColor itemColor = mColor;
    1090                 :          0 :     if ( QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( layerNode()->layer() ) )
    1091                 :            :     {
    1092                 :          0 :       if ( QgsRasterRenderer *rasterRenderer = rasterLayer->renderer() )
    1093                 :          0 :         itemColor.setAlpha( rasterRenderer->opacity() * 255.0 );
    1094                 :          0 :     }
    1095                 :          0 :     ctx->painter->setBrush( itemColor );
    1096                 :            : 
    1097                 :          0 :     if ( settings.drawRasterStroke() )
    1098                 :            :     {
    1099                 :          0 :       QPen pen;
    1100                 :          0 :       pen.setColor( settings.rasterStrokeColor() );
    1101                 :          0 :       pen.setWidthF( settings.rasterStrokeWidth() );
    1102                 :          0 :       pen.setJoinStyle( Qt::MiterJoin );
    1103                 :          0 :       ctx->painter->setPen( pen );
    1104                 :          0 :     }
    1105                 :            :     else
    1106                 :            :     {
    1107                 :          0 :       ctx->painter->setPen( Qt::NoPen );
    1108                 :            :     }
    1109                 :            : 
    1110                 :          0 :     switch ( settings.symbolAlignment() )
    1111                 :          0 :     {
    1112                 :            :       case Qt::AlignLeft:
    1113                 :            :       default:
    1114                 :          0 :         ctx->painter->drawRect( QRectF( ctx->columnLeft + offsetX, ctx->top + ( itemHeight - size.height() ) / 2,
    1115                 :          0 :                                         size.width(), size.height() ) );
    1116                 :          0 :         break;
    1117                 :            : 
    1118                 :            :       case Qt::AlignRight:
    1119                 :          0 :         ctx->painter->drawRect( QRectF( ctx->columnRight - size.width() - offsetX, ctx->top + ( itemHeight - size.height() ) / 2,
    1120                 :          0 :                                         size.width(), size.height() ) );
    1121                 :          0 :         break;
    1122                 :            :     }
    1123                 :          0 :   }
    1124                 :          0 :   return size;
    1125                 :          0 : }
    1126                 :            : 
    1127                 :          0 : QJsonObject QgsRasterSymbolLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext & ) const
    1128                 :            : {
    1129                 :          0 :   QImage img = QImage( settings.symbolSize().toSize(), QImage::Format_ARGB32 );
    1130                 :          0 :   img.fill( Qt::transparent );
    1131                 :            : 
    1132                 :          0 :   QPainter painter( &img );
    1133                 :          0 :   painter.setRenderHint( QPainter::Antialiasing );
    1134                 :            : 
    1135                 :          0 :   QColor itemColor = mColor;
    1136                 :          0 :   if ( QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( layerNode()->layer() ) )
    1137                 :            :   {
    1138                 :          0 :     if ( QgsRasterRenderer *rasterRenderer = rasterLayer->renderer() )
    1139                 :          0 :       itemColor.setAlpha( rasterRenderer->opacity() * 255.0 );
    1140                 :          0 :   }
    1141                 :          0 :   painter.setBrush( itemColor );
    1142                 :            : 
    1143                 :          0 :   if ( settings.drawRasterStroke() )
    1144                 :            :   {
    1145                 :          0 :     QPen pen;
    1146                 :          0 :     pen.setColor( settings.rasterStrokeColor() );
    1147                 :          0 :     pen.setWidthF( settings.rasterStrokeWidth() );
    1148                 :          0 :     pen.setJoinStyle( Qt::MiterJoin );
    1149                 :          0 :     painter.setPen( pen );
    1150                 :          0 :   }
    1151                 :            :   else
    1152                 :            :   {
    1153                 :          0 :     painter.setPen( Qt::NoPen );
    1154                 :            :   }
    1155                 :            : 
    1156                 :          0 :   painter.drawRect( QRectF( 0, 0, settings.symbolSize().width(), settings.symbolSize().height() ) );
    1157                 :            : 
    1158                 :          0 :   QByteArray byteArray;
    1159                 :          0 :   QBuffer buffer( &byteArray );
    1160                 :          0 :   img.save( &buffer, "PNG" );
    1161                 :          0 :   const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
    1162                 :            : 
    1163                 :          0 :   QJsonObject json;
    1164                 :          0 :   json[ QStringLiteral( "icon" ) ] = base64;
    1165                 :          0 :   return json;
    1166                 :          0 : }
    1167                 :            : 
    1168                 :            : // -------------------------------------------------------------------------
    1169                 :            : 
    1170                 :          0 : QgsWmsLegendNode::QgsWmsLegendNode( QgsLayerTreeLayer *nodeLayer, QObject *parent )
    1171                 :          0 :   : QgsLayerTreeModelLegendNode( nodeLayer, parent )
    1172                 :          0 :   , mValid( false )
    1173                 :          0 : {
    1174                 :          0 : }
    1175                 :            : 
    1176                 :          0 : QgsWmsLegendNode::~QgsWmsLegendNode() = default;
    1177                 :            : 
    1178                 :          0 : QImage QgsWmsLegendNode::getLegendGraphic() const
    1179                 :            : {
    1180                 :          0 :   if ( ! mValid && ! mFetcher )
    1181                 :            :   {
    1182                 :            :     // or maybe in presence of a downloader we should just delete it
    1183                 :            :     // and start a new one ?
    1184                 :            : 
    1185                 :          0 :     QgsRasterLayer *layer = qobject_cast<QgsRasterLayer *>( mLayerNode->layer() );
    1186                 :          0 :     const QgsLayerTreeModel *mod = model();
    1187                 :          0 :     if ( ! mod )
    1188                 :          0 :       return mImage;
    1189                 :          0 :     const QgsMapSettings *ms = mod->legendFilterMapSettings();
    1190                 :            : 
    1191                 :          0 :     QgsRasterDataProvider *prov = layer->dataProvider();
    1192                 :          0 :     if ( ! prov )
    1193                 :          0 :       return mImage;
    1194                 :            : 
    1195                 :            :     Q_ASSERT( ! mFetcher );
    1196                 :          0 :     mFetcher.reset( prov->getLegendGraphicFetcher( ms ) );
    1197                 :          0 :     if ( mFetcher )
    1198                 :            :     {
    1199                 :          0 :       connect( mFetcher.get(), &QgsImageFetcher::finish, this, &QgsWmsLegendNode::getLegendGraphicFinished );
    1200                 :          0 :       connect( mFetcher.get(), &QgsImageFetcher::error, this, &QgsWmsLegendNode::getLegendGraphicErrored );
    1201                 :          0 :       connect( mFetcher.get(), &QgsImageFetcher::progress, this, &QgsWmsLegendNode::getLegendGraphicProgress );
    1202                 :          0 :       mFetcher->start();
    1203                 :          0 :     }
    1204                 :          0 :   }
    1205                 :            : 
    1206                 :          0 :   return mImage;
    1207                 :          0 : }
    1208                 :            : 
    1209                 :          0 : QVariant QgsWmsLegendNode::data( int role ) const
    1210                 :            : {
    1211                 :          0 :   if ( role == Qt::DecorationRole )
    1212                 :            :   {
    1213                 :          0 :     return QPixmap::fromImage( getLegendGraphic() );
    1214                 :            :   }
    1215                 :          0 :   else if ( role == Qt::SizeHintRole )
    1216                 :            :   {
    1217                 :          0 :     return getLegendGraphic().size();
    1218                 :            :   }
    1219                 :          0 :   else if ( role == QgsLayerTreeModelLegendNode::NodeTypeRole )
    1220                 :            :   {
    1221                 :          0 :     return QgsLayerTreeModelLegendNode::WmsLegend;
    1222                 :            :   }
    1223                 :          0 :   return QVariant();
    1224                 :          0 : }
    1225                 :            : 
    1226                 :          0 : QSizeF QgsWmsLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
    1227                 :            : {
    1228                 :            :   Q_UNUSED( itemHeight )
    1229                 :            : 
    1230                 :          0 :   if ( ctx && ctx->painter )
    1231                 :            :   {
    1232                 :          0 :     switch ( settings.symbolAlignment() )
    1233                 :          0 :     {
    1234                 :            :       case Qt::AlignLeft:
    1235                 :            :       default:
    1236                 :          0 :         ctx->painter->drawImage( QRectF( ctx->columnLeft,
    1237                 :          0 :                                          ctx->top,
    1238                 :          0 :                                          settings.wmsLegendSize().width(),
    1239                 :          0 :                                          settings.wmsLegendSize().height() ),
    1240                 :          0 :                                  mImage,
    1241                 :          0 :                                  QRectF( QPointF( 0, 0 ), mImage.size() ) );
    1242                 :          0 :         break;
    1243                 :            : 
    1244                 :            :       case Qt::AlignRight:
    1245                 :          0 :         ctx->painter->drawImage( QRectF( ctx->columnRight - settings.wmsLegendSize().width(),
    1246                 :          0 :                                          ctx->top,
    1247                 :          0 :                                          settings.wmsLegendSize().width(),
    1248                 :          0 :                                          settings.wmsLegendSize().height() ),
    1249                 :          0 :                                  mImage,
    1250                 :          0 :                                  QRectF( QPointF( 0, 0 ), mImage.size() ) );
    1251                 :          0 :         break;
    1252                 :            :     }
    1253                 :          0 :   }
    1254                 :          0 :   return settings.wmsLegendSize();
    1255                 :            : }
    1256                 :            : 
    1257                 :          0 : QJsonObject QgsWmsLegendNode::exportSymbolToJson( const QgsLegendSettings &, const QgsRenderContext & ) const
    1258                 :            : {
    1259                 :          0 :   QByteArray byteArray;
    1260                 :          0 :   QBuffer buffer( &byteArray );
    1261                 :          0 :   mImage.save( &buffer, "PNG" );
    1262                 :          0 :   const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
    1263                 :            : 
    1264                 :          0 :   QJsonObject json;
    1265                 :          0 :   json[ QStringLiteral( "icon" ) ] = base64;
    1266                 :          0 :   return json;
    1267                 :          0 : }
    1268                 :            : 
    1269                 :          0 : QImage QgsWmsLegendNode::renderMessage( const QString &msg ) const
    1270                 :            : {
    1271                 :          0 :   const int fontHeight = 10;
    1272                 :          0 :   const int margin = fontHeight / 2;
    1273                 :          0 :   const int nlines = 1;
    1274                 :            : 
    1275                 :          0 :   const int w = 512, h = fontHeight * nlines + margin * ( nlines + 1 );
    1276                 :          0 :   QImage image( w, h, QImage::Format_ARGB32_Premultiplied );
    1277                 :          0 :   QPainter painter;
    1278                 :          0 :   painter.begin( &image );
    1279                 :          0 :   painter.setPen( QColor( 255, 0, 0 ) );
    1280                 :          0 :   painter.setFont( QFont( QStringLiteral( "Chicago" ), fontHeight ) );
    1281                 :          0 :   painter.fillRect( 0, 0, w, h, QColor( 255, 255, 255 ) );
    1282                 :          0 :   painter.drawText( 0, margin + fontHeight, msg );
    1283                 :            :   //painter.drawText(0,2*(margin+fontHeight),tr("retrying in 5 seconds…"));
    1284                 :          0 :   painter.end();
    1285                 :            : 
    1286                 :          0 :   return image;
    1287                 :          0 : }
    1288                 :            : 
    1289                 :          0 : void QgsWmsLegendNode::getLegendGraphicProgress( qint64 cur, qint64 tot )
    1290                 :            : {
    1291                 :          0 :   const QString msg = tot > 0 ? tr( "Downloading: %1% (%2)" ).arg( static_cast< int >( std::round( 100 * cur / tot ) ) ).arg( QgsFileUtils::representFileSize( tot ) )
    1292                 :          0 :                       : tr( "Downloading: %1" ).arg( QgsFileUtils::representFileSize( cur ) );
    1293                 :          0 :   mImage = renderMessage( msg );
    1294                 :          0 :   emit dataChanged();
    1295                 :          0 : }
    1296                 :            : 
    1297                 :          0 : void QgsWmsLegendNode::getLegendGraphicErrored( const QString & )
    1298                 :            : {
    1299                 :          0 :   if ( ! mFetcher )
    1300                 :          0 :     return; // must be coming after finish
    1301                 :            : 
    1302                 :          0 :   mImage = QImage();
    1303                 :          0 :   emit dataChanged();
    1304                 :            : 
    1305                 :          0 :   mFetcher.reset();
    1306                 :            : 
    1307                 :          0 :   mValid = true; // we consider it valid anyway
    1308                 :          0 : }
    1309                 :            : 
    1310                 :          0 : void QgsWmsLegendNode::getLegendGraphicFinished( const QImage &image )
    1311                 :            : {
    1312                 :          0 :   if ( ! mFetcher )
    1313                 :          0 :     return; // must be coming after error
    1314                 :            : 
    1315                 :          0 :   if ( ! image.isNull() )
    1316                 :            :   {
    1317                 :          0 :     if ( image != mImage )
    1318                 :            :     {
    1319                 :          0 :       mImage = image;
    1320                 :          0 :       setUserPatchSize( mImage.size() );
    1321                 :          0 :       emit dataChanged();
    1322                 :          0 :     }
    1323                 :          0 :     mValid = true; // only if not null I guess
    1324                 :          0 :   }
    1325                 :          0 :   mFetcher.reset();
    1326                 :          0 : }
    1327                 :            : 
    1328                 :          0 : void QgsWmsLegendNode::invalidateMapBasedData()
    1329                 :            : {
    1330                 :            :   // TODO: do this only if this extent != prev extent ?
    1331                 :          0 :   mValid = false;
    1332                 :          0 :   emit dataChanged();
    1333                 :          0 : }
    1334                 :            : 
    1335                 :            : // -------------------------------------------------------------------------
    1336                 :            : 
    1337                 :          0 : QgsDataDefinedSizeLegendNode::QgsDataDefinedSizeLegendNode( QgsLayerTreeLayer *nodeLayer, const QgsDataDefinedSizeLegend &settings, QObject *parent )
    1338                 :          0 :   : QgsLayerTreeModelLegendNode( nodeLayer, parent )
    1339                 :          0 :   , mSettings( new QgsDataDefinedSizeLegend( settings ) )
    1340                 :          0 : {
    1341                 :          0 : }
    1342                 :            : 
    1343                 :          0 : QgsDataDefinedSizeLegendNode::~QgsDataDefinedSizeLegendNode()
    1344                 :          0 : {
    1345                 :          0 :   delete mSettings;
    1346                 :          0 : }
    1347                 :            : 
    1348                 :          0 : QVariant QgsDataDefinedSizeLegendNode::data( int role ) const
    1349                 :            : {
    1350                 :          0 :   if ( role == Qt::DecorationRole )
    1351                 :            :   {
    1352                 :          0 :     cacheImage();
    1353                 :          0 :     return QPixmap::fromImage( mImage );
    1354                 :            :   }
    1355                 :          0 :   else if ( role == Qt::SizeHintRole )
    1356                 :            :   {
    1357                 :          0 :     cacheImage();
    1358                 :          0 :     return mImage.size();
    1359                 :            :   }
    1360                 :          0 :   else if ( role == QgsLayerTreeModelLegendNode::NodeTypeRole )
    1361                 :            :   {
    1362                 :          0 :     return QgsLayerTreeModelLegendNode::DataDefinedSizeLegend;
    1363                 :            :   }
    1364                 :          0 :   return QVariant();
    1365                 :          0 : }
    1366                 :            : 
    1367                 :          0 : QgsLayerTreeModelLegendNode::ItemMetrics QgsDataDefinedSizeLegendNode::draw( const QgsLegendSettings &settings, QgsLayerTreeModelLegendNode::ItemContext *ctx )
    1368                 :            : {
    1369                 :            :   // setup temporary render context if none specified
    1370                 :          0 :   QgsRenderContext *context = nullptr;
    1371                 :          0 :   std::unique_ptr< QgsRenderContext > tempRenderContext;
    1372                 :          0 :   if ( ctx && ctx->context )
    1373                 :          0 :     context = ctx->context;
    1374                 :            :   else
    1375                 :            :   {
    1376                 :          0 :     tempRenderContext = std::make_unique< QgsRenderContext >();
    1377                 :            :     // QGIS 4.0 - make ItemContext compulsory, so we don't have to construct temporary render contexts here
    1378                 :            :     Q_NOWARN_DEPRECATED_PUSH
    1379                 :          0 :     tempRenderContext->setScaleFactor( settings.dpi() / 25.4 );
    1380                 :          0 :     tempRenderContext->setRendererScale( settings.mapScale() );
    1381                 :          0 :     tempRenderContext->setFlag( QgsRenderContext::Antialiasing, true );
    1382                 :          0 :     tempRenderContext->setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * tempRenderContext->scaleFactor() ) ) );
    1383                 :          0 :     tempRenderContext->setForceVectorOutput( true );
    1384                 :          0 :     tempRenderContext->setPainter( ctx ? ctx->painter : nullptr );
    1385                 :          0 :     tempRenderContext->setFlag( QgsRenderContext::Antialiasing, true );
    1386                 :            :     Q_NOWARN_DEPRECATED_POP
    1387                 :            : 
    1388                 :            :     // setup a minimal expression context
    1389                 :          0 :     QgsExpressionContext expContext;
    1390                 :          0 :     expContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) );
    1391                 :          0 :     tempRenderContext->setExpressionContext( expContext );
    1392                 :          0 :     context = tempRenderContext.get();
    1393                 :          0 :   }
    1394                 :            : 
    1395                 :          0 :   if ( context->painter() )
    1396                 :            :   {
    1397                 :          0 :     context->painter()->save();
    1398                 :          0 :     context->painter()->translate( ctx->columnLeft, ctx->top );
    1399                 :            : 
    1400                 :            :     // scale to pixels
    1401                 :          0 :     context->painter()->scale( 1 / context->scaleFactor(), 1 / context->scaleFactor() );
    1402                 :          0 :   }
    1403                 :            : 
    1404                 :          0 :   QgsDataDefinedSizeLegend ddsLegend( *mSettings );
    1405                 :          0 :   ddsLegend.setFont( settings.style( QgsLegendStyle::SymbolLabel ).font() );
    1406                 :          0 :   ddsLegend.setTextColor( settings.fontColor() );
    1407                 :            : 
    1408                 :          0 :   QSizeF contentSize;
    1409                 :            :   double labelXOffset;
    1410                 :          0 :   ddsLegend.drawCollapsedLegend( *context, &contentSize, &labelXOffset );
    1411                 :            : 
    1412                 :          0 :   if ( context->painter() )
    1413                 :          0 :     context->painter()->restore();
    1414                 :            : 
    1415                 :          0 :   ItemMetrics im;
    1416                 :          0 :   im.symbolSize = QSizeF( ( contentSize.width() - labelXOffset ) / context->scaleFactor(), contentSize.height() / context->scaleFactor() );
    1417                 :          0 :   im.labelSize = QSizeF( labelXOffset / context->scaleFactor(), contentSize.height() / context->scaleFactor() );
    1418                 :            :   return im;
    1419                 :          0 : }
    1420                 :            : 
    1421                 :            : 
    1422                 :          0 : void QgsDataDefinedSizeLegendNode::cacheImage() const
    1423                 :            : {
    1424                 :          0 :   if ( mImage.isNull() )
    1425                 :            :   {
    1426                 :          0 :     std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
    1427                 :          0 :     if ( !context )
    1428                 :            :     {
    1429                 :          0 :       context.reset( new QgsRenderContext );
    1430                 :            :       Q_ASSERT( context ); // to make cppcheck happy
    1431                 :          0 :       context->setScaleFactor( 96 / 25.4 );
    1432                 :          0 :     }
    1433                 :          0 :     mImage = mSettings->collapsedLegendImage( *context );
    1434                 :          0 :   }
    1435                 :          0 : }
    1436                 :            : 

Generated by: LCOV version 1.14