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

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :                          qgslayoutitemlegend.cpp
       3                 :            :                          -----------------------
       4                 :            :     begin                : October 2017
       5                 :            :     copyright            : (C) 2017 by Nyall Dawson
       6                 :            :     email                : nyall dot dawson at gmail dot com
       7                 :            :  ***************************************************************************/
       8                 :            : 
       9                 :            : /***************************************************************************
      10                 :            :  *                                                                         *
      11                 :            :  *   This program is free software; you can redistribute it and/or modify  *
      12                 :            :  *   it under the terms of the GNU General Public License as published by  *
      13                 :            :  *   the Free Software Foundation; either version 2 of the License, or     *
      14                 :            :  *   (at your option) any later version.                                   *
      15                 :            :  *                                                                         *
      16                 :            :  ***************************************************************************/
      17                 :            : #include <limits>
      18                 :            : 
      19                 :            : #include "qgslayoutitemlegend.h"
      20                 :            : #include "qgslayoutitemregistry.h"
      21                 :            : #include "qgslayoutitemmap.h"
      22                 :            : #include "qgslayout.h"
      23                 :            : #include "qgslayoutmodel.h"
      24                 :            : #include "qgslayertree.h"
      25                 :            : #include "qgslayertreemodel.h"
      26                 :            : #include "qgslegendrenderer.h"
      27                 :            : #include "qgslegendstyle.h"
      28                 :            : #include "qgslogger.h"
      29                 :            : #include "qgsmapsettings.h"
      30                 :            : #include "qgsproject.h"
      31                 :            : #include "qgssymbollayerutils.h"
      32                 :            : #include "qgslayertreeutils.h"
      33                 :            : #include "qgslayoututils.h"
      34                 :            : #include "qgsmapthemecollection.h"
      35                 :            : #include "qgsstyleentityvisitor.h"
      36                 :            : #include <QDomDocument>
      37                 :            : #include <QDomElement>
      38                 :            : #include <QPainter>
      39                 :            : #include "qgsexpressioncontext.h"
      40                 :            : 
      41                 :          0 : QgsLayoutItemLegend::QgsLayoutItemLegend( QgsLayout *layout )
      42                 :          0 :   : QgsLayoutItem( layout )
      43                 :          0 :   , mLegendModel( new QgsLegendModel( layout->project()->layerTreeRoot(), this ) )
      44                 :          0 : {
      45                 :            : #if 0 //no longer required?
      46                 :            :   connect( &layout->atlasComposition(), &QgsAtlasComposition::renderEnded, this, &QgsLayoutItemLegend::onAtlasEnded );
      47                 :            : #endif
      48                 :            : 
      49                 :          0 :   mTitle = mSettings.title();
      50                 :            : 
      51                 :            :   // Connect to the main layertreeroot.
      52                 :            :   // It serves in "auto update mode" as a medium between the main app legend and this one
      53                 :          0 :   connect( mLayout->project()->layerTreeRoot(), &QgsLayerTreeNode::customPropertyChanged, this, &QgsLayoutItemLegend::nodeCustomPropertyChanged );
      54                 :            : 
      55                 :            :   // If project colors change, we need to redraw legend, as legend symbols may rely on project colors
      56                 :          0 :   connect( mLayout->project(), &QgsProject::projectColorsChanged, this, [ = ]
      57                 :            :   {
      58                 :          0 :     invalidateCache();
      59                 :          0 :     update();
      60                 :          0 :   } );
      61                 :          0 :   connect( mLegendModel.get(), &QgsLegendModel::refreshLegend, this, &QgsLayoutItemLegend::refresh );
      62                 :          0 : }
      63                 :            : 
      64                 :          0 : QgsLayoutItemLegend *QgsLayoutItemLegend::create( QgsLayout *layout )
      65                 :            : {
      66                 :          0 :   return new QgsLayoutItemLegend( layout );
      67                 :          0 : }
      68                 :            : 
      69                 :          0 : int QgsLayoutItemLegend::type() const
      70                 :            : {
      71                 :          0 :   return QgsLayoutItemRegistry::LayoutLegend;
      72                 :            : }
      73                 :            : 
      74                 :          0 : QIcon QgsLayoutItemLegend::icon() const
      75                 :            : {
      76                 :          0 :   return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemLegend.svg" ) );
      77                 :          0 : }
      78                 :            : 
      79                 :          0 : QgsLayoutItem::Flags QgsLayoutItemLegend::itemFlags() const
      80                 :            : {
      81                 :          0 :   return QgsLayoutItem::FlagOverridesPaint;
      82                 :            : }
      83                 :            : 
      84                 :          0 : void QgsLayoutItemLegend::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
      85                 :            : {
      86                 :          0 :   if ( !painter )
      87                 :          0 :     return;
      88                 :            : 
      89                 :          0 :   if ( mFilterAskedForUpdate )
      90                 :            :   {
      91                 :          0 :     mFilterAskedForUpdate = false;
      92                 :          0 :     doUpdateFilterByMap();
      93                 :          0 :   }
      94                 :            : 
      95                 :          0 :   int dpi = painter->device()->logicalDpiX();
      96                 :          0 :   double dotsPerMM = dpi / 25.4;
      97                 :            : 
      98                 :          0 :   if ( mLayout )
      99                 :            :   {
     100                 :            :     Q_NOWARN_DEPRECATED_PUSH
     101                 :            :     // no longer required, but left set for api stability
     102                 :          0 :     mSettings.setUseAdvancedEffects( mLayout->renderContext().flags() & QgsLayoutRenderContext::FlagUseAdvancedEffects );
     103                 :          0 :     mSettings.setDpi( dpi );
     104                 :            :     Q_NOWARN_DEPRECATED_POP
     105                 :          0 :   }
     106                 :          0 :   if ( mMap && mLayout )
     107                 :            :   {
     108                 :            :     Q_NOWARN_DEPRECATED_PUSH
     109                 :            :     // no longer required, but left set for api stability
     110                 :          0 :     mSettings.setMmPerMapUnit( mLayout->convertFromLayoutUnits( mMap->mapUnitsToLayoutUnits(), QgsUnitTypes::LayoutMillimeters ).length() );
     111                 :            :     Q_NOWARN_DEPRECATED_POP
     112                 :            : 
     113                 :            :     // use a temporary QgsMapSettings to find out real map scale
     114                 :          0 :     QSizeF mapSizePixels = QSizeF( mMap->rect().width() * dotsPerMM, mMap->rect().height() * dotsPerMM );
     115                 :          0 :     QgsRectangle mapExtent = mMap->extent();
     116                 :            : 
     117                 :          0 :     QgsMapSettings ms = mMap->mapSettings( mapExtent, mapSizePixels, dpi, false );
     118                 :            : 
     119                 :            :     // no longer required, but left set for api stability
     120                 :            :     Q_NOWARN_DEPRECATED_PUSH
     121                 :          0 :     mSettings.setMapScale( ms.scale() );
     122                 :            :     Q_NOWARN_DEPRECATED_POP
     123                 :          0 :   }
     124                 :          0 :   mInitialMapScaleCalculated = true;
     125                 :            : 
     126                 :          0 :   QgsLegendRenderer legendRenderer( mLegendModel.get(), mSettings );
     127                 :          0 :   legendRenderer.setLegendSize( mForceResize && mSizeToContents ? QSize() : rect().size() );
     128                 :            : 
     129                 :            :   //adjust box if width or height is too small
     130                 :          0 :   if ( mSizeToContents )
     131                 :            :   {
     132                 :          0 :     QgsRenderContext context = mMap ? QgsLayoutUtils::createRenderContextForMap( mMap, painter )
     133                 :          0 :                                : QgsLayoutUtils::createRenderContextForLayout( mLayout, painter );
     134                 :            : 
     135                 :          0 :     QSizeF size = legendRenderer.minimumSize( &context );
     136                 :          0 :     if ( mForceResize )
     137                 :            :     {
     138                 :          0 :       mForceResize = false;
     139                 :            : 
     140                 :            :       //set new rect, respecting position mode and data defined size/position
     141                 :          0 :       QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( size, sizeWithUnits().units() );
     142                 :          0 :       attemptResize( newSize );
     143                 :          0 :     }
     144                 :          0 :     else if ( size.height() > rect().height() || size.width() > rect().width() )
     145                 :            :     {
     146                 :            :       //need to resize box
     147                 :          0 :       QSizeF targetSize = rect().size();
     148                 :          0 :       if ( size.height() > targetSize.height() )
     149                 :          0 :         targetSize.setHeight( size.height() );
     150                 :          0 :       if ( size.width() > targetSize.width() )
     151                 :          0 :         targetSize.setWidth( size.width() );
     152                 :            : 
     153                 :          0 :       QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( targetSize, sizeWithUnits().units() );
     154                 :            :       //set new rect, respecting position mode and data defined size/position
     155                 :          0 :       attemptResize( newSize );
     156                 :          0 :     }
     157                 :          0 :   }
     158                 :            : 
     159                 :          0 :   QgsLayoutItem::paint( painter, itemStyle, pWidget );
     160                 :          0 : }
     161                 :            : 
     162                 :          0 : void QgsLayoutItemLegend::finalizeRestoreFromXml()
     163                 :            : {
     164                 :          0 :   if ( !mMapUuid.isEmpty() )
     165                 :            :   {
     166                 :          0 :     setLinkedMap( qobject_cast< QgsLayoutItemMap * >( mLayout->itemByUuid( mMapUuid, true ) ) );
     167                 :          0 :   }
     168                 :          0 : }
     169                 :            : 
     170                 :          0 : void QgsLayoutItemLegend::refresh()
     171                 :            : {
     172                 :          0 :   QgsLayoutItem::refresh();
     173                 :          0 :   clearLegendCachedData();
     174                 :          0 :   onAtlasFeature();
     175                 :          0 : }
     176                 :            : 
     177                 :          0 : void QgsLayoutItemLegend::draw( QgsLayoutItemRenderContext &context )
     178                 :            : {
     179                 :          0 :   QPainter *painter = context.renderContext().painter();
     180                 :            : 
     181                 :          0 :   QgsRenderContext rc = mMap ? QgsLayoutUtils::createRenderContextForMap( mMap, painter, context.renderContext().scaleFactor() * 25.4 )
     182                 :          0 :                         : QgsLayoutUtils::createRenderContextForLayout( mLayout, painter, context.renderContext().scaleFactor() * 25.4 );
     183                 :            : 
     184                 :          0 :   rc.expressionContext().appendScopes( createExpressionContext().takeScopes() );
     185                 :            : 
     186                 :          0 :   QgsScopedQPainterState painterState( painter );
     187                 :            : 
     188                 :            :   // painter is scaled to dots, so scale back to layout units
     189                 :          0 :   painter->scale( rc.scaleFactor(), rc.scaleFactor() );
     190                 :            : 
     191                 :          0 :   painter->setPen( QPen( QColor( 0, 0, 0 ) ) );
     192                 :            : 
     193                 :          0 :   if ( !mSizeToContents )
     194                 :            :   {
     195                 :            :     // set a clip region to crop out parts of legend which don't fit
     196                 :          0 :     QRectF thisPaintRect = QRectF( 0, 0, rect().width(), rect().height() );
     197                 :          0 :     painter->setClipRect( thisPaintRect );
     198                 :          0 :   }
     199                 :            : 
     200                 :          0 :   if ( mLayout )
     201                 :            :   {
     202                 :            :     // no longer required, but left for API compatibility
     203                 :            :     Q_NOWARN_DEPRECATED_PUSH
     204                 :          0 :     mSettings.setDpi( mLayout->renderContext().dpi() );
     205                 :            :     Q_NOWARN_DEPRECATED_POP
     206                 :          0 :   }
     207                 :            : 
     208                 :            : 
     209                 :            : 
     210                 :            : 
     211                 :          0 :   QgsLegendRenderer legendRenderer( mLegendModel.get(), mSettings );
     212                 :          0 :   legendRenderer.setLegendSize( rect().size() );
     213                 :            : 
     214                 :          0 :   legendRenderer.drawLegend( rc );
     215                 :          0 : }
     216                 :            : 
     217                 :          0 : void QgsLayoutItemLegend::adjustBoxSize()
     218                 :            : {
     219                 :          0 :   if ( !mSizeToContents )
     220                 :          0 :     return;
     221                 :            : 
     222                 :          0 :   if ( !mInitialMapScaleCalculated )
     223                 :            :   {
     224                 :            :     // this is messy - but until we have painted the item we have no knowledge of the current DPI
     225                 :            :     // and so cannot correctly calculate the map scale. This results in incorrect size calculations
     226                 :            :     // for marker symbols with size in map units, causing the legends to initially expand to huge
     227                 :            :     // sizes if we attempt to calculate the box size first.
     228                 :          0 :     return;
     229                 :            :   }
     230                 :            : 
     231                 :          0 :   QgsRenderContext context = mMap ? QgsLayoutUtils::createRenderContextForMap( mMap, nullptr ) :
     232                 :          0 :                              QgsLayoutUtils::createRenderContextForLayout( mLayout, nullptr );
     233                 :            : 
     234                 :          0 :   QgsLegendRenderer legendRenderer( mLegendModel.get(), mSettings );
     235                 :          0 :   QSizeF size = legendRenderer.minimumSize( &context );
     236                 :          0 :   QgsDebugMsg( QStringLiteral( "width = %1 height = %2" ).arg( size.width() ).arg( size.height() ) );
     237                 :          0 :   if ( size.isValid() )
     238                 :            :   {
     239                 :          0 :     QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( size, sizeWithUnits().units() );
     240                 :            :     //set new rect, respecting position mode and data defined size/position
     241                 :          0 :     attemptResize( newSize );
     242                 :          0 :   }
     243                 :          0 : }
     244                 :            : 
     245                 :          0 : void QgsLayoutItemLegend::setResizeToContents( bool enabled )
     246                 :            : {
     247                 :          0 :   mSizeToContents = enabled;
     248                 :          0 : }
     249                 :            : 
     250                 :          0 : bool QgsLayoutItemLegend::resizeToContents() const
     251                 :            : {
     252                 :          0 :   return mSizeToContents;
     253                 :            : }
     254                 :            : 
     255                 :          0 : void QgsLayoutItemLegend::setCustomLayerTree( QgsLayerTree *rootGroup )
     256                 :            : {
     257                 :          0 :   mLegendModel->setRootGroup( rootGroup ? rootGroup : ( mLayout ? mLayout->project()->layerTreeRoot() : nullptr ) );
     258                 :            : 
     259                 :          0 :   mCustomLayerTree.reset( rootGroup );
     260                 :          0 : }
     261                 :            : 
     262                 :            : 
     263                 :          0 : void QgsLayoutItemLegend::setAutoUpdateModel( bool autoUpdate )
     264                 :            : {
     265                 :          0 :   if ( autoUpdate == autoUpdateModel() )
     266                 :          0 :     return;
     267                 :            : 
     268                 :          0 :   setCustomLayerTree( autoUpdate ? nullptr : mLayout->project()->layerTreeRoot()->clone() );
     269                 :          0 :   adjustBoxSize();
     270                 :          0 :   updateFilterByMap( false );
     271                 :          0 : }
     272                 :            : 
     273                 :          0 : void QgsLayoutItemLegend::nodeCustomPropertyChanged( QgsLayerTreeNode *, const QString &key )
     274                 :            : {
     275                 :          0 :   if ( key == QLatin1String( "cached_name" ) )
     276                 :          0 :     return;
     277                 :            : 
     278                 :          0 :   if ( autoUpdateModel() )
     279                 :            :   {
     280                 :            :     // in "auto update" mode, some parameters on the main app legend may have been changed (expression filtering)
     281                 :            :     // we must then call updateItem to reflect the changes
     282                 :          0 :     updateFilterByMap( false );
     283                 :          0 :   }
     284                 :          0 : }
     285                 :            : 
     286                 :          0 : bool QgsLayoutItemLegend::autoUpdateModel() const
     287                 :            : {
     288                 :          0 :   return !mCustomLayerTree;
     289                 :            : }
     290                 :            : 
     291                 :          0 : void QgsLayoutItemLegend::setLegendFilterByMapEnabled( bool enabled )
     292                 :            : {
     293                 :          0 :   mLegendFilterByMap = enabled;
     294                 :          0 :   updateFilterByMap( false );
     295                 :          0 : }
     296                 :            : 
     297                 :          0 : void QgsLayoutItemLegend::setTitle( const QString &t )
     298                 :            : {
     299                 :          0 :   mTitle = t;
     300                 :          0 :   mSettings.setTitle( t );
     301                 :            : 
     302                 :          0 :   if ( mLayout && id().isEmpty() )
     303                 :            :   {
     304                 :            :     //notify the model that the display name has changed
     305                 :          0 :     mLayout->itemsModel()->updateItemDisplayName( this );
     306                 :          0 :   }
     307                 :          0 : }
     308                 :          0 : QString QgsLayoutItemLegend::title() const
     309                 :            : {
     310                 :          0 :   return mTitle;
     311                 :            : }
     312                 :            : 
     313                 :          0 : Qt::AlignmentFlag QgsLayoutItemLegend::titleAlignment() const
     314                 :            : {
     315                 :          0 :   return mSettings.titleAlignment();
     316                 :            : }
     317                 :            : 
     318                 :          0 : void QgsLayoutItemLegend::setTitleAlignment( Qt::AlignmentFlag alignment )
     319                 :            : {
     320                 :          0 :   mSettings.setTitleAlignment( alignment );
     321                 :          0 : }
     322                 :            : 
     323                 :          0 : QgsLegendStyle &QgsLayoutItemLegend::rstyle( QgsLegendStyle::Style s )
     324                 :            : {
     325                 :          0 :   return mSettings.rstyle( s );
     326                 :            : }
     327                 :            : 
     328                 :          0 : QgsLegendStyle QgsLayoutItemLegend::style( QgsLegendStyle::Style s ) const
     329                 :            : {
     330                 :          0 :   return mSettings.style( s );
     331                 :            : }
     332                 :            : 
     333                 :          0 : void QgsLayoutItemLegend::setStyle( QgsLegendStyle::Style s, const QgsLegendStyle &style )
     334                 :            : {
     335                 :          0 :   mSettings.setStyle( s, style );
     336                 :          0 : }
     337                 :            : 
     338                 :          0 : QFont QgsLayoutItemLegend::styleFont( QgsLegendStyle::Style s ) const
     339                 :            : {
     340                 :          0 :   return mSettings.style( s ).font();
     341                 :          0 : }
     342                 :            : 
     343                 :          0 : void QgsLayoutItemLegend::setStyleFont( QgsLegendStyle::Style s, const QFont &f )
     344                 :            : {
     345                 :          0 :   rstyle( s ).setFont( f );
     346                 :          0 : }
     347                 :            : 
     348                 :          0 : void QgsLayoutItemLegend::setStyleMargin( QgsLegendStyle::Style s, double margin )
     349                 :            : {
     350                 :          0 :   rstyle( s ).setMargin( margin );
     351                 :          0 : }
     352                 :            : 
     353                 :          0 : void QgsLayoutItemLegend::setStyleMargin( QgsLegendStyle::Style s, QgsLegendStyle::Side side, double margin )
     354                 :            : {
     355                 :          0 :   rstyle( s ).setMargin( side, margin );
     356                 :          0 : }
     357                 :            : 
     358                 :          0 : double QgsLayoutItemLegend::lineSpacing() const
     359                 :            : {
     360                 :          0 :   return mSettings.lineSpacing();
     361                 :            : }
     362                 :            : 
     363                 :          0 : void QgsLayoutItemLegend::setLineSpacing( double spacing )
     364                 :            : {
     365                 :          0 :   mSettings.setLineSpacing( spacing );
     366                 :          0 : }
     367                 :            : 
     368                 :          0 : double QgsLayoutItemLegend::boxSpace() const
     369                 :            : {
     370                 :          0 :   return mSettings.boxSpace();
     371                 :            : }
     372                 :            : 
     373                 :          0 : void QgsLayoutItemLegend::setBoxSpace( double s )
     374                 :            : {
     375                 :          0 :   mSettings.setBoxSpace( s );
     376                 :          0 : }
     377                 :            : 
     378                 :          0 : double QgsLayoutItemLegend::columnSpace() const
     379                 :            : {
     380                 :          0 :   return mSettings.columnSpace();
     381                 :            : }
     382                 :            : 
     383                 :          0 : void QgsLayoutItemLegend::setColumnSpace( double s )
     384                 :            : {
     385                 :          0 :   mSettings.setColumnSpace( s );
     386                 :          0 : }
     387                 :            : 
     388                 :          0 : QColor QgsLayoutItemLegend::fontColor() const
     389                 :            : {
     390                 :          0 :   return mSettings.fontColor();
     391                 :            : }
     392                 :            : 
     393                 :          0 : void QgsLayoutItemLegend::setFontColor( const QColor &c )
     394                 :            : {
     395                 :          0 :   mSettings.setFontColor( c );
     396                 :          0 : }
     397                 :            : 
     398                 :          0 : double QgsLayoutItemLegend::symbolWidth() const
     399                 :            : {
     400                 :          0 :   return mSettings.symbolSize().width();
     401                 :            : }
     402                 :            : 
     403                 :          0 : void QgsLayoutItemLegend::setSymbolWidth( double w )
     404                 :            : {
     405                 :          0 :   mSettings.setSymbolSize( QSizeF( w, mSettings.symbolSize().height() ) );
     406                 :          0 : }
     407                 :            : 
     408                 :          0 : double QgsLayoutItemLegend::maximumSymbolSize() const
     409                 :            : {
     410                 :          0 :   return mSettings.maximumSymbolSize();
     411                 :            : }
     412                 :            : 
     413                 :          0 : void QgsLayoutItemLegend::setMaximumSymbolSize( double size )
     414                 :            : {
     415                 :          0 :   mSettings.setMaximumSymbolSize( size );
     416                 :          0 : }
     417                 :            : 
     418                 :          0 : double QgsLayoutItemLegend::minimumSymbolSize() const
     419                 :            : {
     420                 :          0 :   return mSettings.minimumSymbolSize();
     421                 :            : }
     422                 :            : 
     423                 :          0 : void QgsLayoutItemLegend::setMinimumSymbolSize( double size )
     424                 :            : {
     425                 :          0 :   mSettings.setMinimumSymbolSize( size );
     426                 :          0 : }
     427                 :            : 
     428                 :          0 : void QgsLayoutItemLegend::setSymbolAlignment( Qt::AlignmentFlag alignment )
     429                 :            : {
     430                 :          0 :   mSettings.setSymbolAlignment( alignment );
     431                 :          0 : }
     432                 :            : 
     433                 :          0 : Qt::AlignmentFlag QgsLayoutItemLegend::symbolAlignment() const
     434                 :            : {
     435                 :          0 :   return mSettings.symbolAlignment();
     436                 :            : }
     437                 :            : 
     438                 :          0 : double QgsLayoutItemLegend::symbolHeight() const
     439                 :            : {
     440                 :          0 :   return mSettings.symbolSize().height();
     441                 :            : }
     442                 :            : 
     443                 :          0 : void QgsLayoutItemLegend::setSymbolHeight( double h )
     444                 :            : {
     445                 :          0 :   mSettings.setSymbolSize( QSizeF( mSettings.symbolSize().width(), h ) );
     446                 :          0 : }
     447                 :            : 
     448                 :          0 : double QgsLayoutItemLegend::wmsLegendWidth() const
     449                 :            : {
     450                 :          0 :   return mSettings.wmsLegendSize().width();
     451                 :            : }
     452                 :            : 
     453                 :          0 : void QgsLayoutItemLegend::setWmsLegendWidth( double w )
     454                 :            : {
     455                 :          0 :   mSettings.setWmsLegendSize( QSizeF( w, mSettings.wmsLegendSize().height() ) );
     456                 :          0 : }
     457                 :            : 
     458                 :          0 : double QgsLayoutItemLegend::wmsLegendHeight() const
     459                 :            : {
     460                 :          0 :   return mSettings.wmsLegendSize().height();
     461                 :            : }
     462                 :          0 : void QgsLayoutItemLegend::setWmsLegendHeight( double h )
     463                 :            : {
     464                 :          0 :   mSettings.setWmsLegendSize( QSizeF( mSettings.wmsLegendSize().width(), h ) );
     465                 :          0 : }
     466                 :            : 
     467                 :          0 : void QgsLayoutItemLegend::setWrapString( const QString &t )
     468                 :            : {
     469                 :          0 :   mSettings.setWrapChar( t );
     470                 :          0 : }
     471                 :            : 
     472                 :          0 : QString QgsLayoutItemLegend::wrapString() const
     473                 :            : {
     474                 :          0 :   return mSettings.wrapChar();
     475                 :            : }
     476                 :            : 
     477                 :          0 : int QgsLayoutItemLegend::columnCount() const
     478                 :            : {
     479                 :          0 :   return mColumnCount;
     480                 :            : }
     481                 :            : 
     482                 :          0 : void QgsLayoutItemLegend::setColumnCount( int c )
     483                 :            : {
     484                 :          0 :   mColumnCount = c;
     485                 :          0 :   mSettings.setColumnCount( c );
     486                 :          0 : }
     487                 :            : 
     488                 :          0 : bool QgsLayoutItemLegend::splitLayer() const
     489                 :            : {
     490                 :          0 :   return mSettings.splitLayer();
     491                 :            : }
     492                 :            : 
     493                 :          0 : void QgsLayoutItemLegend::setSplitLayer( bool s )
     494                 :            : {
     495                 :          0 :   mSettings.setSplitLayer( s );
     496                 :          0 : }
     497                 :            : 
     498                 :          0 : bool QgsLayoutItemLegend::equalColumnWidth() const
     499                 :            : {
     500                 :          0 :   return mSettings.equalColumnWidth();
     501                 :            : }
     502                 :            : 
     503                 :          0 : void QgsLayoutItemLegend::setEqualColumnWidth( bool s )
     504                 :            : {
     505                 :          0 :   mSettings.setEqualColumnWidth( s );
     506                 :          0 : }
     507                 :            : 
     508                 :          0 : bool QgsLayoutItemLegend::drawRasterStroke() const
     509                 :            : {
     510                 :          0 :   return mSettings.drawRasterStroke();
     511                 :            : }
     512                 :            : 
     513                 :          0 : void QgsLayoutItemLegend::setDrawRasterStroke( bool enabled )
     514                 :            : {
     515                 :          0 :   mSettings.setDrawRasterStroke( enabled );
     516                 :          0 : }
     517                 :            : 
     518                 :          0 : QColor QgsLayoutItemLegend::rasterStrokeColor() const
     519                 :            : {
     520                 :          0 :   return mSettings.rasterStrokeColor();
     521                 :            : }
     522                 :            : 
     523                 :          0 : void QgsLayoutItemLegend::setRasterStrokeColor( const QColor &color )
     524                 :            : {
     525                 :          0 :   mSettings.setRasterStrokeColor( color );
     526                 :          0 : }
     527                 :            : 
     528                 :          0 : double QgsLayoutItemLegend::rasterStrokeWidth() const
     529                 :            : {
     530                 :          0 :   return mSettings.rasterStrokeWidth();
     531                 :            : }
     532                 :            : 
     533                 :          0 : void QgsLayoutItemLegend::setRasterStrokeWidth( double width )
     534                 :            : {
     535                 :          0 :   mSettings.setRasterStrokeWidth( width );
     536                 :          0 : }
     537                 :            : 
     538                 :            : 
     539                 :          0 : void QgsLayoutItemLegend::updateLegend()
     540                 :            : {
     541                 :          0 :   adjustBoxSize();
     542                 :          0 :   updateFilterByMap( false );
     543                 :          0 : }
     544                 :            : 
     545                 :          0 : bool QgsLayoutItemLegend::writePropertiesToElement( QDomElement &legendElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
     546                 :            : {
     547                 :            : 
     548                 :            :   //write general properties
     549                 :          0 :   legendElem.setAttribute( QStringLiteral( "title" ), mTitle );
     550                 :          0 :   legendElem.setAttribute( QStringLiteral( "titleAlignment" ), QString::number( static_cast< int >( mSettings.titleAlignment() ) ) );
     551                 :          0 :   legendElem.setAttribute( QStringLiteral( "columnCount" ), QString::number( mColumnCount ) );
     552                 :          0 :   legendElem.setAttribute( QStringLiteral( "splitLayer" ), QString::number( mSettings.splitLayer() ) );
     553                 :          0 :   legendElem.setAttribute( QStringLiteral( "equalColumnWidth" ), QString::number( mSettings.equalColumnWidth() ) );
     554                 :            : 
     555                 :          0 :   legendElem.setAttribute( QStringLiteral( "boxSpace" ), QString::number( mSettings.boxSpace() ) );
     556                 :          0 :   legendElem.setAttribute( QStringLiteral( "columnSpace" ), QString::number( mSettings.columnSpace() ) );
     557                 :            : 
     558                 :          0 :   legendElem.setAttribute( QStringLiteral( "symbolWidth" ), QString::number( mSettings.symbolSize().width() ) );
     559                 :          0 :   legendElem.setAttribute( QStringLiteral( "symbolHeight" ), QString::number( mSettings.symbolSize().height() ) );
     560                 :          0 :   legendElem.setAttribute( QStringLiteral( "maxSymbolSize" ), QString::number( mSettings.maximumSymbolSize() ) );
     561                 :          0 :   legendElem.setAttribute( QStringLiteral( "minSymbolSize" ), QString::number( mSettings.minimumSymbolSize() ) );
     562                 :            : 
     563                 :          0 :   legendElem.setAttribute( QStringLiteral( "symbolAlignment" ), mSettings.symbolAlignment() );
     564                 :            : 
     565                 :          0 :   legendElem.setAttribute( QStringLiteral( "symbolAlignment" ), mSettings.symbolAlignment() );
     566                 :          0 :   legendElem.setAttribute( QStringLiteral( "lineSpacing" ), QString::number( mSettings.lineSpacing() ) );
     567                 :            : 
     568                 :          0 :   legendElem.setAttribute( QStringLiteral( "rasterBorder" ), mSettings.drawRasterStroke() );
     569                 :          0 :   legendElem.setAttribute( QStringLiteral( "rasterBorderColor" ), QgsSymbolLayerUtils::encodeColor( mSettings.rasterStrokeColor() ) );
     570                 :          0 :   legendElem.setAttribute( QStringLiteral( "rasterBorderWidth" ), QString::number( mSettings.rasterStrokeWidth() ) );
     571                 :            : 
     572                 :          0 :   legendElem.setAttribute( QStringLiteral( "wmsLegendWidth" ), QString::number( mSettings.wmsLegendSize().width() ) );
     573                 :          0 :   legendElem.setAttribute( QStringLiteral( "wmsLegendHeight" ), QString::number( mSettings.wmsLegendSize().height() ) );
     574                 :          0 :   legendElem.setAttribute( QStringLiteral( "wrapChar" ), mSettings.wrapChar() );
     575                 :          0 :   legendElem.setAttribute( QStringLiteral( "fontColor" ), mSettings.fontColor().name() );
     576                 :            : 
     577                 :          0 :   legendElem.setAttribute( QStringLiteral( "resizeToContents" ), mSizeToContents );
     578                 :            : 
     579                 :          0 :   if ( mMap )
     580                 :            :   {
     581                 :          0 :     legendElem.setAttribute( QStringLiteral( "map_uuid" ), mMap->uuid() );
     582                 :          0 :   }
     583                 :            : 
     584                 :          0 :   QDomElement legendStyles = doc.createElement( QStringLiteral( "styles" ) );
     585                 :          0 :   legendElem.appendChild( legendStyles );
     586                 :            : 
     587                 :          0 :   style( QgsLegendStyle::Title ).writeXml( QStringLiteral( "title" ), legendStyles, doc );
     588                 :          0 :   style( QgsLegendStyle::Group ).writeXml( QStringLiteral( "group" ), legendStyles, doc );
     589                 :          0 :   style( QgsLegendStyle::Subgroup ).writeXml( QStringLiteral( "subgroup" ), legendStyles, doc );
     590                 :          0 :   style( QgsLegendStyle::Symbol ).writeXml( QStringLiteral( "symbol" ), legendStyles, doc );
     591                 :          0 :   style( QgsLegendStyle::SymbolLabel ).writeXml( QStringLiteral( "symbolLabel" ), legendStyles, doc );
     592                 :            : 
     593                 :          0 :   if ( mCustomLayerTree )
     594                 :            :   {
     595                 :            :     // if not using auto-update - store the custom layer tree
     596                 :          0 :     mCustomLayerTree->writeXml( legendElem, context );
     597                 :          0 :   }
     598                 :            : 
     599                 :          0 :   if ( mLegendFilterByMap )
     600                 :            :   {
     601                 :          0 :     legendElem.setAttribute( QStringLiteral( "legendFilterByMap" ), QStringLiteral( "1" ) );
     602                 :          0 :   }
     603                 :          0 :   legendElem.setAttribute( QStringLiteral( "legendFilterByAtlas" ), mFilterOutAtlas ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
     604                 :            : 
     605                 :            :   return true;
     606                 :          0 : }
     607                 :            : 
     608                 :          0 : bool QgsLayoutItemLegend::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context )
     609                 :            : {
     610                 :            :   //read general properties
     611                 :          0 :   mTitle = itemElem.attribute( QStringLiteral( "title" ) );
     612                 :          0 :   mSettings.setTitle( mTitle );
     613                 :          0 :   if ( !itemElem.attribute( QStringLiteral( "titleAlignment" ) ).isEmpty() )
     614                 :          0 :   {
     615                 :          0 :     mSettings.setTitleAlignment( static_cast< Qt::AlignmentFlag >( itemElem.attribute( QStringLiteral( "titleAlignment" ) ).toInt() ) );
     616                 :          0 :   }
     617                 :          0 :   int colCount = itemElem.attribute( QStringLiteral( "columnCount" ), QStringLiteral( "1" ) ).toInt();
     618                 :          0 :   if ( colCount < 1 ) colCount = 1;
     619                 :          0 :   mColumnCount = colCount;
     620                 :          0 :   mSettings.setColumnCount( mColumnCount );
     621                 :          0 :   mSettings.setSplitLayer( itemElem.attribute( QStringLiteral( "splitLayer" ), QStringLiteral( "0" ) ).toInt() == 1 );
     622                 :          0 :   mSettings.setEqualColumnWidth( itemElem.attribute( QStringLiteral( "equalColumnWidth" ), QStringLiteral( "0" ) ).toInt() == 1 );
     623                 :            : 
     624                 :          0 :   QDomNodeList stylesNodeList = itemElem.elementsByTagName( QStringLiteral( "styles" ) );
     625                 :          0 :   if ( !stylesNodeList.isEmpty() )
     626                 :            :   {
     627                 :          0 :     QDomNode stylesNode = stylesNodeList.at( 0 );
     628                 :          0 :     for ( int i = 0; i < stylesNode.childNodes().size(); i++ )
     629                 :            :     {
     630                 :          0 :       QDomElement styleElem = stylesNode.childNodes().at( i ).toElement();
     631                 :          0 :       QgsLegendStyle style;
     632                 :          0 :       style.readXml( styleElem, doc, context );
     633                 :          0 :       QString name = styleElem.attribute( QStringLiteral( "name" ) );
     634                 :            :       QgsLegendStyle::Style s;
     635                 :          0 :       if ( name == QLatin1String( "title" ) ) s = QgsLegendStyle::Title;
     636                 :          0 :       else if ( name == QLatin1String( "group" ) ) s = QgsLegendStyle::Group;
     637                 :          0 :       else if ( name == QLatin1String( "subgroup" ) ) s = QgsLegendStyle::Subgroup;
     638                 :          0 :       else if ( name == QLatin1String( "symbol" ) ) s = QgsLegendStyle::Symbol;
     639                 :          0 :       else if ( name == QLatin1String( "symbolLabel" ) ) s = QgsLegendStyle::SymbolLabel;
     640                 :          0 :       else continue;
     641                 :          0 :       setStyle( s, style );
     642                 :          0 :     }
     643                 :          0 :   }
     644                 :            : 
     645                 :            :   //font color
     646                 :          0 :   QColor fontClr;
     647                 :          0 :   fontClr.setNamedColor( itemElem.attribute( QStringLiteral( "fontColor" ), QStringLiteral( "#000000" ) ) );
     648                 :          0 :   mSettings.setFontColor( fontClr );
     649                 :            : 
     650                 :            :   //spaces
     651                 :          0 :   mSettings.setBoxSpace( itemElem.attribute( QStringLiteral( "boxSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
     652                 :          0 :   mSettings.setColumnSpace( itemElem.attribute( QStringLiteral( "columnSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
     653                 :            : 
     654                 :          0 :   mSettings.setSymbolSize( QSizeF( itemElem.attribute( QStringLiteral( "symbolWidth" ), QStringLiteral( "7.0" ) ).toDouble(), itemElem.attribute( QStringLiteral( "symbolHeight" ), QStringLiteral( "14.0" ) ).toDouble() ) );
     655                 :          0 :   mSettings.setSymbolAlignment( static_cast< Qt::AlignmentFlag >( itemElem.attribute( QStringLiteral( "symbolAlignment" ), QString::number( Qt::AlignLeft ) ).toInt() ) );
     656                 :            : 
     657                 :          0 :   mSettings.setMaximumSymbolSize( itemElem.attribute( QStringLiteral( "maxSymbolSize" ), QStringLiteral( "0.0" ) ).toDouble() );
     658                 :          0 :   mSettings.setMinimumSymbolSize( itemElem.attribute( QStringLiteral( "minSymbolSize" ), QStringLiteral( "0.0" ) ).toDouble() );
     659                 :            : 
     660                 :          0 :   mSettings.setWmsLegendSize( QSizeF( itemElem.attribute( QStringLiteral( "wmsLegendWidth" ), QStringLiteral( "50" ) ).toDouble(), itemElem.attribute( QStringLiteral( "wmsLegendHeight" ), QStringLiteral( "25" ) ).toDouble() ) );
     661                 :          0 :   mSettings.setLineSpacing( itemElem.attribute( QStringLiteral( "lineSpacing" ), QStringLiteral( "1.0" ) ).toDouble() );
     662                 :            : 
     663                 :          0 :   mSettings.setDrawRasterStroke( itemElem.attribute( QStringLiteral( "rasterBorder" ), QStringLiteral( "1" ) ) != QLatin1String( "0" ) );
     664                 :          0 :   mSettings.setRasterStrokeColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "rasterBorderColor" ), QStringLiteral( "0,0,0" ) ) ) );
     665                 :          0 :   mSettings.setRasterStrokeWidth( itemElem.attribute( QStringLiteral( "rasterBorderWidth" ), QStringLiteral( "0" ) ).toDouble() );
     666                 :            : 
     667                 :          0 :   mSettings.setWrapChar( itemElem.attribute( QStringLiteral( "wrapChar" ) ) );
     668                 :            : 
     669                 :          0 :   mSizeToContents = itemElem.attribute( QStringLiteral( "resizeToContents" ), QStringLiteral( "1" ) ) != QLatin1String( "0" );
     670                 :            : 
     671                 :            :   // map
     672                 :          0 :   mLegendFilterByMap = itemElem.attribute( QStringLiteral( "legendFilterByMap" ), QStringLiteral( "0" ) ).toInt();
     673                 :            : 
     674                 :          0 :   mMapUuid.clear();
     675                 :          0 :   if ( !itemElem.attribute( QStringLiteral( "map_uuid" ) ).isEmpty() )
     676                 :            :   {
     677                 :          0 :     mMapUuid = itemElem.attribute( QStringLiteral( "map_uuid" ) );
     678                 :          0 :   }
     679                 :            :   // disconnect current map
     680                 :          0 :   setupMapConnections( mMap, false );
     681                 :          0 :   mMap = nullptr;
     682                 :            : 
     683                 :          0 :   mFilterOutAtlas = itemElem.attribute( QStringLiteral( "legendFilterByAtlas" ), QStringLiteral( "0" ) ).toInt();
     684                 :            : 
     685                 :            :   // QGIS >= 2.6
     686                 :          0 :   QDomElement layerTreeElem = itemElem.firstChildElement( QStringLiteral( "layer-tree" ) );
     687                 :          0 :   if ( layerTreeElem.isNull() )
     688                 :          0 :     layerTreeElem = itemElem.firstChildElement( QStringLiteral( "layer-tree-group" ) );
     689                 :            : 
     690                 :          0 :   if ( !layerTreeElem.isNull() )
     691                 :            :   {
     692                 :          0 :     std::unique_ptr< QgsLayerTree > tree( QgsLayerTree::readXml( layerTreeElem, context ) );
     693                 :          0 :     if ( mLayout )
     694                 :          0 :       tree->resolveReferences( mLayout->project(), true );
     695                 :          0 :     setCustomLayerTree( tree.release() );
     696                 :          0 :   }
     697                 :            :   else
     698                 :          0 :     setCustomLayerTree( nullptr );
     699                 :            : 
     700                 :            :   return true;
     701                 :          0 : }
     702                 :            : 
     703                 :          0 : QString QgsLayoutItemLegend::displayName() const
     704                 :            : {
     705                 :          0 :   if ( !id().isEmpty() )
     706                 :            :   {
     707                 :          0 :     return id();
     708                 :            :   }
     709                 :            : 
     710                 :            :   //if no id, default to portion of title text
     711                 :          0 :   QString text = mSettings.title();
     712                 :          0 :   if ( text.isEmpty() )
     713                 :            :   {
     714                 :          0 :     return tr( "<Legend>" );
     715                 :            :   }
     716                 :          0 :   if ( text.length() > 25 )
     717                 :            :   {
     718                 :          0 :     return tr( "%1…" ).arg( text.left( 25 ) );
     719                 :            :   }
     720                 :            :   else
     721                 :            :   {
     722                 :          0 :     return text;
     723                 :            :   }
     724                 :          0 : }
     725                 :            : 
     726                 :            : 
     727                 :          0 : void QgsLayoutItemLegend::setupMapConnections( QgsLayoutItemMap *map, bool connectSlots )
     728                 :            : {
     729                 :          0 :   if ( !map )
     730                 :          0 :     return;
     731                 :            : 
     732                 :          0 :   if ( !connectSlots )
     733                 :            :   {
     734                 :          0 :     disconnect( map, &QObject::destroyed, this, &QgsLayoutItemLegend::invalidateCurrentMap );
     735                 :          0 :     disconnect( map, &QgsLayoutObject::changed, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
     736                 :          0 :     disconnect( map, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
     737                 :          0 :     disconnect( map, &QgsLayoutItemMap::mapRotationChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
     738                 :          0 :     disconnect( map, &QgsLayoutItemMap::layerStyleOverridesChanged, this, &QgsLayoutItemLegend::mapLayerStyleOverridesChanged );
     739                 :          0 :     disconnect( map, &QgsLayoutItemMap::themeChanged, this, &QgsLayoutItemLegend::mapThemeChanged );
     740                 :          0 :   }
     741                 :            :   else
     742                 :            :   {
     743                 :          0 :     connect( map, &QObject::destroyed, this, &QgsLayoutItemLegend::invalidateCurrentMap );
     744                 :          0 :     connect( map, &QgsLayoutObject::changed, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
     745                 :          0 :     connect( map, &QgsLayoutItemMap::extentChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
     746                 :          0 :     connect( map, &QgsLayoutItemMap::mapRotationChanged, this, &QgsLayoutItemLegend::updateFilterByMapAndRedraw );
     747                 :          0 :     connect( map, &QgsLayoutItemMap::layerStyleOverridesChanged, this, &QgsLayoutItemLegend::mapLayerStyleOverridesChanged );
     748                 :          0 :     connect( map, &QgsLayoutItemMap::themeChanged, this, &QgsLayoutItemLegend::mapThemeChanged );
     749                 :            :   }
     750                 :          0 : }
     751                 :            : 
     752                 :          0 : void QgsLayoutItemLegend::setLinkedMap( QgsLayoutItemMap *map )
     753                 :            : {
     754                 :          0 :   if ( mMap )
     755                 :            :   {
     756                 :          0 :     setupMapConnections( mMap, false );
     757                 :          0 :   }
     758                 :            : 
     759                 :          0 :   mMap = map;
     760                 :            : 
     761                 :          0 :   if ( mMap )
     762                 :            :   {
     763                 :          0 :     setupMapConnections( mMap, true );
     764                 :          0 :     mapThemeChanged( mMap->themeToRender( mMap->createExpressionContext() ) );
     765                 :          0 :   }
     766                 :            : 
     767                 :          0 :   updateFilterByMap();
     768                 :          0 : }
     769                 :            : 
     770                 :          0 : void QgsLayoutItemLegend::invalidateCurrentMap()
     771                 :            : {
     772                 :          0 :   setLinkedMap( nullptr );
     773                 :          0 : }
     774                 :            : 
     775                 :          0 : void QgsLayoutItemLegend::refreshDataDefinedProperty( const QgsLayoutObject::DataDefinedProperty property )
     776                 :            : {
     777                 :          0 :   QgsExpressionContext context = createExpressionContext();
     778                 :            : 
     779                 :          0 :   bool forceUpdate = false;
     780                 :            :   //updates data defined properties and redraws item to match
     781                 :          0 :   if ( property == QgsLayoutObject::LegendTitle || property == QgsLayoutObject::AllProperties )
     782                 :            :   {
     783                 :          0 :     bool ok = false;
     784                 :          0 :     QString t = mDataDefinedProperties.valueAsString( QgsLayoutObject::LegendTitle, context, mTitle, &ok );
     785                 :          0 :     if ( ok )
     786                 :            :     {
     787                 :          0 :       mSettings.setTitle( t );
     788                 :          0 :       forceUpdate = true;
     789                 :          0 :     }
     790                 :          0 :   }
     791                 :          0 :   if ( property == QgsLayoutObject::LegendColumnCount || property == QgsLayoutObject::AllProperties )
     792                 :            :   {
     793                 :          0 :     bool ok = false;
     794                 :          0 :     int cols = mDataDefinedProperties.valueAsInt( QgsLayoutObject::LegendColumnCount, context, mColumnCount, &ok );
     795                 :          0 :     if ( ok && cols >= 0 )
     796                 :            :     {
     797                 :          0 :       mSettings.setColumnCount( cols );
     798                 :          0 :       forceUpdate = true;
     799                 :          0 :     }
     800                 :          0 :   }
     801                 :          0 :   if ( forceUpdate )
     802                 :            :   {
     803                 :          0 :     adjustBoxSize();
     804                 :          0 :     update();
     805                 :          0 :   }
     806                 :            : 
     807                 :          0 :   QgsLayoutItem::refreshDataDefinedProperty( property );
     808                 :          0 : }
     809                 :            : 
     810                 :            : 
     811                 :          0 : void QgsLayoutItemLegend::updateFilterByMapAndRedraw()
     812                 :            : {
     813                 :          0 :   updateFilterByMap( true );
     814                 :          0 : }
     815                 :            : 
     816                 :          0 : void QgsLayoutItemLegend::setModelStyleOverrides( const QMap<QString, QString> &overrides )
     817                 :            : {
     818                 :          0 :   mLegendModel->setLayerStyleOverrides( overrides );
     819                 :          0 :   const QList< QgsLayerTreeLayer * > layers =  mLegendModel->rootGroup()->findLayers();
     820                 :          0 :   for ( QgsLayerTreeLayer *nodeLayer : layers )
     821                 :          0 :     mLegendModel->refreshLayerLegend( nodeLayer );
     822                 :            : 
     823                 :          0 : }
     824                 :            : 
     825                 :          0 : void QgsLayoutItemLegend::clearLegendCachedData()
     826                 :            : {
     827                 :          0 :   std::function< void( QgsLayerTreeNode * ) > clearNodeCache;
     828                 :          0 :   clearNodeCache = [&]( QgsLayerTreeNode * node )
     829                 :            :   {
     830                 :          0 :     mLegendModel->clearCachedData( node );
     831                 :          0 :     if ( QgsLayerTree::isGroup( node ) )
     832                 :            :     {
     833                 :          0 :       QgsLayerTreeGroup *group = QgsLayerTree::toGroup( node );
     834                 :          0 :       const QList< QgsLayerTreeNode * > children = group->children();
     835                 :          0 :       for ( QgsLayerTreeNode *child : children )
     836                 :            :       {
     837                 :          0 :         clearNodeCache( child );
     838                 :            :       }
     839                 :          0 :     }
     840                 :          0 :   };
     841                 :            : 
     842                 :          0 :   clearNodeCache( mLegendModel->rootGroup() );
     843                 :          0 : }
     844                 :            : 
     845                 :          0 : void QgsLayoutItemLegend::mapLayerStyleOverridesChanged()
     846                 :            : {
     847                 :          0 :   if ( !mMap )
     848                 :          0 :     return;
     849                 :            : 
     850                 :            :   // map's style has been changed, so make sure to update the legend here
     851                 :          0 :   if ( mLegendFilterByMap )
     852                 :            :   {
     853                 :            :     // legend is being filtered by map, so we need to re run the hit test too
     854                 :            :     // as the style overrides may also have affected the visible symbols
     855                 :          0 :     updateFilterByMap( false );
     856                 :          0 :   }
     857                 :            :   else
     858                 :            :   {
     859                 :          0 :     setModelStyleOverrides( mMap->layerStyleOverrides() );
     860                 :            :   }
     861                 :            : 
     862                 :          0 :   adjustBoxSize();
     863                 :            : 
     864                 :          0 :   updateFilterByMap( false );
     865                 :          0 : }
     866                 :            : 
     867                 :          0 : void QgsLayoutItemLegend::mapThemeChanged( const QString &theme )
     868                 :            : {
     869                 :          0 :   if ( mThemeName == theme )
     870                 :          0 :     return;
     871                 :            : 
     872                 :          0 :   mThemeName = theme;
     873                 :            : 
     874                 :            :   // map's theme has been changed, so make sure to update the legend here
     875                 :          0 :   if ( mLegendFilterByMap )
     876                 :            :   {
     877                 :            :     // legend is being filtered by map, so we need to re run the hit test too
     878                 :            :     // as the style overrides may also have affected the visible symbols
     879                 :          0 :     updateFilterByMap( false );
     880                 :          0 :   }
     881                 :            :   else
     882                 :            :   {
     883                 :          0 :     if ( mThemeName.isEmpty() )
     884                 :            :     {
     885                 :          0 :       setModelStyleOverrides( QMap<QString, QString>() );
     886                 :          0 :     }
     887                 :            :     else
     888                 :            :     {
     889                 :            :       // get style overrides for theme
     890                 :          0 :       const QMap<QString, QString> overrides = mLayout->project()->mapThemeCollection()->mapThemeStyleOverrides( mThemeName );
     891                 :          0 :       setModelStyleOverrides( overrides );
     892                 :          0 :     }
     893                 :            :   }
     894                 :            : 
     895                 :          0 :   adjustBoxSize();
     896                 :            : 
     897                 :          0 :   updateFilterByMap();
     898                 :          0 : }
     899                 :            : 
     900                 :          0 : void QgsLayoutItemLegend::updateFilterByMap( bool redraw )
     901                 :            : {
     902                 :            :   // ask for update
     903                 :            :   // the actual update will take place before the redraw.
     904                 :            :   // This is to avoid multiple calls to the filter
     905                 :          0 :   mFilterAskedForUpdate = true;
     906                 :            : 
     907                 :          0 :   if ( redraw )
     908                 :          0 :     update();
     909                 :          0 : }
     910                 :            : 
     911                 :          0 : void QgsLayoutItemLegend::doUpdateFilterByMap()
     912                 :            : {
     913                 :          0 :   if ( mMap )
     914                 :            :   {
     915                 :          0 :     if ( !mThemeName.isEmpty() )
     916                 :            :     {
     917                 :            :       // get style overrides for theme
     918                 :          0 :       const QMap<QString, QString> overrides = mLayout->project()->mapThemeCollection()->mapThemeStyleOverrides( mThemeName );
     919                 :          0 :       mLegendModel->setLayerStyleOverrides( overrides );
     920                 :          0 :     }
     921                 :            :     else
     922                 :            :     {
     923                 :          0 :       mLegendModel->setLayerStyleOverrides( mMap->layerStyleOverrides() );
     924                 :            :     }
     925                 :          0 :   }
     926                 :            :   else
     927                 :          0 :     mLegendModel->setLayerStyleOverrides( QMap<QString, QString>() );
     928                 :            : 
     929                 :            : 
     930                 :          0 :   bool filterByExpression = QgsLayerTreeUtils::hasLegendFilterExpression( *( mCustomLayerTree ? mCustomLayerTree.get() : mLayout->project()->layerTreeRoot() ) );
     931                 :            : 
     932                 :          0 :   if ( mMap && ( mLegendFilterByMap || filterByExpression || mInAtlas ) )
     933                 :            :   {
     934                 :          0 :     double dpi = mLayout->renderContext().dpi();
     935                 :            : 
     936                 :          0 :     QgsRectangle requestRectangle = mMap->requestedExtent();
     937                 :            : 
     938                 :          0 :     QSizeF size( requestRectangle.width(), requestRectangle.height() );
     939                 :          0 :     size *= mLayout->convertFromLayoutUnits( mMap->mapUnitsToLayoutUnits(), QgsUnitTypes::LayoutMillimeters ).length() * dpi / 25.4;
     940                 :            : 
     941                 :          0 :     QgsMapSettings ms = mMap->mapSettings( requestRectangle, size, dpi, true );
     942                 :            : 
     943                 :          0 :     QgsGeometry filterPolygon;
     944                 :          0 :     if ( mInAtlas )
     945                 :            :     {
     946                 :          0 :       filterPolygon = mLayout->reportContext().currentGeometry( mMap->crs() );
     947                 :          0 :     }
     948                 :          0 :     mLegendModel->setLegendFilter( &ms, /* useExtent */ mInAtlas || mLegendFilterByMap, filterPolygon, /* useExpressions */ true );
     949                 :          0 :   }
     950                 :            :   else
     951                 :          0 :     mLegendModel->setLegendFilterByMap( nullptr );
     952                 :            : 
     953                 :          0 :   clearLegendCachedData();
     954                 :          0 :   mForceResize = true;
     955                 :          0 : }
     956                 :            : 
     957                 :          0 : QString QgsLayoutItemLegend::themeName() const
     958                 :            : {
     959                 :          0 :   return mThemeName;
     960                 :            : }
     961                 :            : 
     962                 :          0 : void QgsLayoutItemLegend::setLegendFilterOutAtlas( bool doFilter )
     963                 :            : {
     964                 :          0 :   mFilterOutAtlas = doFilter;
     965                 :          0 : }
     966                 :            : 
     967                 :          0 : bool QgsLayoutItemLegend::legendFilterOutAtlas() const
     968                 :            : {
     969                 :          0 :   return mFilterOutAtlas;
     970                 :            : }
     971                 :            : 
     972                 :          0 : void QgsLayoutItemLegend::onAtlasFeature()
     973                 :            : {
     974                 :          0 :   if ( !mLayout->reportContext().feature().isValid() )
     975                 :          0 :     return;
     976                 :          0 :   mInAtlas = mFilterOutAtlas;
     977                 :          0 :   updateFilterByMap();
     978                 :          0 : }
     979                 :            : 
     980                 :          0 : void QgsLayoutItemLegend::onAtlasEnded()
     981                 :            : {
     982                 :          0 :   mInAtlas = false;
     983                 :          0 :   updateFilterByMap();
     984                 :          0 : }
     985                 :            : 
     986                 :          0 : QgsExpressionContext QgsLayoutItemLegend::createExpressionContext() const
     987                 :            : {
     988                 :          0 :   QgsExpressionContext context = QgsLayoutItem::createExpressionContext();
     989                 :            : 
     990                 :            :   // We only want the last scope from the map's expression context, as this contains
     991                 :            :   // the map specific variables. We don't want the rest of the map's context, because that
     992                 :            :   // will contain duplicate global, project, layout, etc scopes.
     993                 :          0 :   if ( mMap )
     994                 :          0 :     context.appendScope( mMap->createExpressionContext().popScope() );
     995                 :            : 
     996                 :          0 :   QgsExpressionContextScope *scope = new QgsExpressionContextScope( tr( "Legend Settings" ) );
     997                 :            : 
     998                 :          0 :   scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_title" ), title(), true ) );
     999                 :          0 :   scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_column_count" ), columnCount(), true ) );
    1000                 :          0 :   scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_split_layers" ), splitLayer(), true ) );
    1001                 :          0 :   scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_wrap_string" ), wrapString(), true ) );
    1002                 :          0 :   scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_filter_by_map" ), legendFilterByMapEnabled(), true ) );
    1003                 :          0 :   scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "legend_filter_out_atlas" ), legendFilterOutAtlas(), true ) );
    1004                 :            : 
    1005                 :          0 :   context.appendScope( scope );
    1006                 :          0 :   return context;
    1007                 :          0 : }
    1008                 :            : 
    1009                 :          0 : QgsLayoutItem::ExportLayerBehavior QgsLayoutItemLegend::exportLayerBehavior() const
    1010                 :            : {
    1011                 :          0 :   return MustPlaceInOwnLayer;
    1012                 :            : }
    1013                 :            : 
    1014                 :          0 : bool QgsLayoutItemLegend::accept( QgsStyleEntityVisitorInterface *visitor ) const
    1015                 :            : {
    1016                 :          0 :   std::function<bool( QgsLayerTreeGroup *group ) >visit;
    1017                 :            : 
    1018                 :          0 :   visit = [ =, &visit]( QgsLayerTreeGroup * group ) -> bool
    1019                 :            :   {
    1020                 :          0 :     const QList<QgsLayerTreeNode *> childNodes = group->children();
    1021                 :          0 :     for ( QgsLayerTreeNode *node : childNodes )
    1022                 :            :     {
    1023                 :          0 :       if ( QgsLayerTree::isGroup( node ) )
    1024                 :            :       {
    1025                 :          0 :         QgsLayerTreeGroup *nodeGroup = QgsLayerTree::toGroup( node );
    1026                 :          0 :         if ( !visit( nodeGroup ) )
    1027                 :          0 :           return false;
    1028                 :          0 :       }
    1029                 :          0 :       else if ( QgsLayerTree::isLayer( node ) )
    1030                 :            :       {
    1031                 :          0 :         QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
    1032                 :          0 :         if ( !nodeLayer->patchShape().isNull() )
    1033                 :            :         {
    1034                 :          0 :           QgsStyleLegendPatchShapeEntity entity( nodeLayer->patchShape() );
    1035                 :          0 :           if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, uuid(), displayName() ) ) )
    1036                 :          0 :             return false;
    1037                 :          0 :         }
    1038                 :          0 :         const QList<QgsLayerTreeModelLegendNode *> legendNodes = mLegendModel->layerLegendNodes( nodeLayer );
    1039                 :          0 :         for ( QgsLayerTreeModelLegendNode *legendNode : legendNodes )
    1040                 :            :         {
    1041                 :          0 :           if ( QgsSymbolLegendNode *symbolNode = dynamic_cast< QgsSymbolLegendNode * >( legendNode ) )
    1042                 :            :           {
    1043                 :          0 :             if ( !symbolNode->patchShape().isNull() )
    1044                 :            :             {
    1045                 :          0 :               QgsStyleLegendPatchShapeEntity entity( symbolNode->patchShape() );
    1046                 :          0 :               if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, uuid(), displayName() ) ) )
    1047                 :          0 :                 return false;
    1048                 :          0 :             }
    1049                 :          0 :           }
    1050                 :            :         }
    1051                 :          0 :       }
    1052                 :            :     }
    1053                 :          0 :     return true;
    1054                 :          0 :   };
    1055                 :          0 :   return visit( mLegendModel->rootGroup( ) );
    1056                 :          0 : }
    1057                 :            : 
    1058                 :            : 
    1059                 :            : // -------------------------------------------------------------------------
    1060                 :            : #include "qgslayertreemodellegendnode.h"
    1061                 :            : #include "qgsvectorlayer.h"
    1062                 :            : #include "qgsmaplayerlegend.h"
    1063                 :            : 
    1064                 :          0 : QgsLegendModel::QgsLegendModel( QgsLayerTree *rootNode, QObject *parent, QgsLayoutItemLegend *layout )
    1065                 :          0 :   : QgsLayerTreeModel( rootNode, parent )
    1066                 :          0 :   , mLayoutLegend( layout )
    1067                 :          0 : {
    1068                 :          0 :   setFlag( QgsLayerTreeModel::AllowLegendChangeState, false );
    1069                 :          0 :   setFlag( QgsLayerTreeModel::AllowNodeReorder, true );
    1070                 :          0 : }
    1071                 :            : 
    1072                 :          0 : QgsLegendModel::QgsLegendModel( QgsLayerTree *rootNode,  QgsLayoutItemLegend *layout )
    1073                 :          0 :   : QgsLayerTreeModel( rootNode )
    1074                 :          0 :   , mLayoutLegend( layout )
    1075                 :          0 : {
    1076                 :          0 :   setFlag( QgsLayerTreeModel::AllowLegendChangeState, false );
    1077                 :          0 :   setFlag( QgsLayerTreeModel::AllowNodeReorder, true );
    1078                 :          0 : }
    1079                 :            : 
    1080                 :          0 : QVariant QgsLegendModel::data( const QModelIndex &index, int role ) const
    1081                 :            : {
    1082                 :            :   // handle custom layer node labels
    1083                 :            : 
    1084                 :          0 :   QgsLayerTreeNode *node = index2node( index );
    1085                 :          0 :   QgsLayerTreeLayer *nodeLayer = QgsLayerTree::isLayer( node ) ? QgsLayerTree::toLayer( node ) : nullptr;
    1086                 :          0 :   if ( nodeLayer && ( role == Qt::DisplayRole || role == Qt::EditRole ) )
    1087                 :            :   {
    1088                 :          0 :     QString name = node->customProperty( QStringLiteral( "cached_name" ) ).toString();
    1089                 :          0 :     if ( !name.isEmpty() )
    1090                 :          0 :       return name;
    1091                 :            : 
    1092                 :          0 :     QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() );
    1093                 :            : 
    1094                 :            :     //finding the first label that is stored
    1095                 :          0 :     name = nodeLayer->customProperty( QStringLiteral( "legend/title-label" ) ).toString();
    1096                 :          0 :     if ( name.isEmpty() )
    1097                 :          0 :       name = nodeLayer->name();
    1098                 :          0 :     if ( name.isEmpty() )
    1099                 :          0 :       name = node->customProperty( QStringLiteral( "legend/title-label" ) ).toString();
    1100                 :          0 :     if ( name.isEmpty() )
    1101                 :          0 :       name = node->name();
    1102                 :          0 :     if ( nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toInt() )
    1103                 :            :     {
    1104                 :          0 :       if ( vlayer && vlayer->featureCount() >= 0 )
    1105                 :            :       {
    1106                 :          0 :         name += QStringLiteral( " [%1]" ).arg( vlayer->featureCount() );
    1107                 :          0 :         node->setCustomProperty( QStringLiteral( "cached_name" ), name );
    1108                 :          0 :         return name;
    1109                 :            :       }
    1110                 :          0 :     }
    1111                 :            : 
    1112                 :          0 :     const bool evaluate = ( vlayer && !nodeLayer->labelExpression().isEmpty() ) || name.contains( "[%" );
    1113                 :          0 :     if ( evaluate )
    1114                 :            :     {
    1115                 :          0 :       QgsExpressionContext expressionContext;
    1116                 :          0 :       if ( vlayer )
    1117                 :            :       {
    1118                 :          0 :         connect( vlayer, &QgsVectorLayer::symbolFeatureCountMapChanged, this, &QgsLegendModel::forceRefresh, Qt::UniqueConnection );
    1119                 :            :         // counting is done here to ensure that a valid vector layer needs to be evaluated, count is used to validate previous count or update the count if invalidated
    1120                 :          0 :         vlayer->countSymbolFeatures();
    1121                 :          0 :       }
    1122                 :            : 
    1123                 :          0 :       if ( mLayoutLegend )
    1124                 :          0 :         expressionContext = mLayoutLegend->createExpressionContext();
    1125                 :            : 
    1126                 :          0 :       const QList<QgsLayerTreeModelLegendNode *> legendnodes = layerLegendNodes( nodeLayer, false );
    1127                 :          0 :       if ( legendnodes.count() > 1 ) // evaluate all existing legend nodes but leave the name for the legend evaluator
    1128                 :            :       {
    1129                 :          0 :         for ( QgsLayerTreeModelLegendNode *treenode : legendnodes )
    1130                 :            :         {
    1131                 :          0 :           if ( QgsSymbolLegendNode *symnode = qobject_cast<QgsSymbolLegendNode *>( treenode ) )
    1132                 :          0 :             symnode->evaluateLabel( expressionContext );
    1133                 :            :         }
    1134                 :          0 :       }
    1135                 :          0 :       else if ( QgsSymbolLegendNode *symnode = qobject_cast<QgsSymbolLegendNode *>( legendnodes.first() ) )
    1136                 :          0 :         name = symnode->evaluateLabel( expressionContext );
    1137                 :          0 :     }
    1138                 :          0 :     node->setCustomProperty( QStringLiteral( "cached_name" ), name );
    1139                 :          0 :     return name;
    1140                 :          0 :   }
    1141                 :          0 :   return QgsLayerTreeModel::data( index, role );
    1142                 :          0 : }
    1143                 :            : 
    1144                 :          0 : Qt::ItemFlags QgsLegendModel::flags( const QModelIndex &index ) const
    1145                 :            : {
    1146                 :            :   // make the legend nodes selectable even if they are not by default
    1147                 :          0 :   if ( index2legendNode( index ) )
    1148                 :          0 :     return QgsLayerTreeModel::flags( index ) | Qt::ItemIsSelectable;
    1149                 :            : 
    1150                 :          0 :   return QgsLayerTreeModel::flags( index );
    1151                 :          0 : }
    1152                 :            : 
    1153                 :          0 : QList<QgsLayerTreeModelLegendNode *> QgsLegendModel::layerLegendNodes( QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent ) const
    1154                 :            : {
    1155                 :          0 :   if ( !mLegend.contains( nodeLayer ) )
    1156                 :          0 :     return QList<QgsLayerTreeModelLegendNode *>();
    1157                 :            : 
    1158                 :          0 :   const LayerLegendData &data = mLegend[nodeLayer];
    1159                 :          0 :   QList<QgsLayerTreeModelLegendNode *> lst( data.activeNodes );
    1160                 :          0 :   if ( !skipNodeEmbeddedInParent && data.embeddedNodeInParent )
    1161                 :          0 :     lst.prepend( data.embeddedNodeInParent );
    1162                 :          0 :   return lst;
    1163                 :          0 : }
    1164                 :            : 
    1165                 :          0 : void QgsLegendModel::clearCachedData( QgsLayerTreeNode *node ) const
    1166                 :            : {
    1167                 :          0 :   node->removeCustomProperty( QStringLiteral( "cached_name" ) );
    1168                 :          0 : }
    1169                 :            : 
    1170                 :          0 : void QgsLegendModel::forceRefresh()
    1171                 :            : {
    1172                 :          0 :   emit refreshLegend();
    1173                 :          0 : }
    1174                 :            : 
    1175                 :            : 

Generated by: LCOV version 1.14