1 /***************************************************************************
2                          qgslayoutitemlegend.h
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 
18 #ifndef QGSLAYOUTITEMLEGEND_H
19 #define QGSLAYOUTITEMLEGEND_H
20 
21 #include "qgis_core.h"
22 #include "qgis_sip.h"
23 #include "qgslayoutitem.h"
24 #include "qgslayertreemodel.h"
25 #include "qgslegendsettings.h"
26 #include "qgslayertreegroup.h"
27 #include "qgsexpressioncontext.h"
28 
29 class QgsLayerTreeModel;
30 class QgsSymbol;
31 class QgsLayoutItemMap;
32 class QgsLegendRenderer;
33 class QgsLayoutItemLegend;
34 
35 /**
36  * \ingroup core
37  * \brief Item model implementation based on layer tree model for layout legend.
38  *
39  * Overrides some functionality of QgsLayerTreeModel to better fit the needs of layout legends.
40  *
41  * \since QGIS 2.6
42  */
43 class CORE_EXPORT QgsLegendModel : public QgsLayerTreeModel
44 {
45     Q_OBJECT
46 
47   public:
48     //! Construct the model based on the given layer tree
49     QgsLegendModel( QgsLayerTree *rootNode, QObject *parent SIP_TRANSFERTHIS = nullptr, QgsLayoutItemLegend *layout = nullptr );
50 
51     //! Alternative constructor.
52     QgsLegendModel( QgsLayerTree *rootNode,  QgsLayoutItemLegend *layout );
53 
54     QVariant data( const QModelIndex &index, int role ) const override;
55 
56     Qt::ItemFlags flags( const QModelIndex &index ) const override;
57 
58     /**
59      * Returns filtered list of active legend nodes attached to a particular layer node
60      * (by default it returns also legend node embedded in parent layer node (if any) unless skipNodeEmbeddedInParent is TRUE)
61      * \note Parameter skipNodeEmbeddedInParent added in QGIS 2.18
62      * \note Not available in Python bindings
63      * \see layerOriginalLegendNodes()
64      * \since QGIS 3.10
65      */
66     QList<QgsLayerTreeModelLegendNode *> layerLegendNodes( QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent = false ) const SIP_SKIP;
67 
68     /**
69      * Clears any previously cached data for the specified \a node.
70      * \since QGIS 3.14
71      */
72     void clearCachedData( QgsLayerTreeNode *node ) const;
73 
74   signals:
75 
76     /**
77      * Emitted to refresh the legend.
78      * \since QGIS 3.10
79      */
80     void refreshLegend();
81 
82   private slots:
83 
84     /**
85      * Handle incoming signal to refresh the legend.
86      * \since QGIS 3.10
87      */
88     void forceRefresh();
89 
90   private:
91 
92     /**
93      * Pointer to the QgsLayoutItemLegend class that made the model.
94      * \since QGIS 3.10
95      */
96     QgsLayoutItemLegend *mLayoutLegend = nullptr;
97 
98     /**
99      * Evaluate the expression or symbol expressions of a given layer node.
100      * \since QGIS 3.14
101      */
102     QString evaluateLayerExpressions( QgsLayerTreeLayer *nodeLayer ) const;
103 
104 };
105 
106 
107 
108 /**
109  * \ingroup core
110  * \brief A layout item subclass for map legends.
111  * \since QGIS 3.0
112  */
113 class CORE_EXPORT QgsLayoutItemLegend : public QgsLayoutItem
114 {
115     Q_OBJECT
116 
117   public:
118 
119     /**
120      * Constructor for QgsLayoutItemLegend, with the specified parent \a layout.
121      */
122     QgsLayoutItemLegend( QgsLayout *layout );
123 
124     /**
125      * Returns a new legend item for the specified \a layout.
126      *
127      * The caller takes responsibility for deleting the returned object.
128      */
129     static QgsLayoutItemLegend *create( QgsLayout *layout ) SIP_FACTORY;
130 
131     int type() const override;
132     QIcon icon() const override;
133     QgsLayoutItem::Flags itemFlags() const override;
134     //Overridden to show legend title
135     QString displayName() const override;
136 
137     /**
138      * Sets the legend's item bounds to fit the whole legend content.
139      */
140     void adjustBoxSize();
141 
142     /**
143      * Sets whether the legend should automatically resize to fit its contents.
144      * \param enabled set to FALSE to disable automatic resizing. The legend frame will not
145      * be expanded to fit legend items, and items may be cropped from display.
146      * \see resizeToContents()
147      */
148     void setResizeToContents( bool enabled );
149 
150     /**
151      * Returns whether the legend should automatically resize to fit its contents.
152      * \see setResizeToContents()
153      */
154     bool resizeToContents() const;
155 
156     /**
157      * Returns the legend model.
158      */
model()159     QgsLegendModel *model() { return mLegendModel.get(); }
160 
161     /**
162      * Sets whether the legend content should auto update to reflect changes in the project's
163      * layer tree.
164      * \see autoUpdateModel()
165      */
166     void setAutoUpdateModel( bool autoUpdate );
167 
168     /**
169      * Returns whether the legend content should auto update to reflect changes in the project's
170      * layer tree.
171      * \see setAutoUpdateModel()
172      */
173     bool autoUpdateModel() const;
174 
175     /**
176      * Set whether legend items should be filtered to show just the ones visible in the associated map.
177      * \see legendFilterByMapEnabled()
178      */
179     void setLegendFilterByMapEnabled( bool enabled );
180 
181     /**
182      * Find out whether legend items are filtered to show just the ones visible in the associated map
183      * \see setLegendFilterByMapEnabled()
184      */
legendFilterByMapEnabled()185     bool legendFilterByMapEnabled() const { return mLegendFilterByMap; }
186 
187     /**
188      * When set to TRUE, during an atlas rendering, it will filter out legend elements
189      * where features are outside the current atlas feature.
190      * \see legendFilterOutAtlas()
191      */
192     void setLegendFilterOutAtlas( bool doFilter );
193 
194     /**
195      * Returns whether to filter out legend elements outside of the current atlas feature.
196      * \see setLegendFilterOutAtlas()
197      */
198     bool legendFilterOutAtlas() const;
199 
200     /**
201      * Sets the legend \a title.
202      * \see title()
203      */
204     void setTitle( const QString &title );
205 
206     /**
207      * Returns the legend title.
208      * \see setTitle()
209      */
210     QString title() const;
211 
212     /**
213      * Returns the alignment of the legend title.
214      * \see setTitleAlignment()
215      */
216     Qt::AlignmentFlag titleAlignment() const;
217 
218     /**
219      * Sets the \a alignment of the legend title.
220      * \see titleAlignment()
221      */
222     void setTitleAlignment( Qt::AlignmentFlag alignment );
223 
224     /**
225      * Returns reference to modifiable legend style.
226      */
227     QgsLegendStyle &rstyle( QgsLegendStyle::Style s );
228 
229     /**
230      * Returns legend style.
231      */
232     QgsLegendStyle style( QgsLegendStyle::Style s ) const;
233 
234     /**
235      * Sets the style of \a component to \a style for the legend.
236      */
237     void setStyle( QgsLegendStyle::Style component, const QgsLegendStyle &style );
238 
239     /**
240      * Returns the font settings for a legend \a component.
241      * \see setStyleFont()
242      */
243     QFont styleFont( QgsLegendStyle::Style component ) const;
244 
245     /**
246      * Sets the style \a font for a legend \a component.
247      * \see styleFont()
248      */
249     void setStyleFont( QgsLegendStyle::Style component, const QFont &font );
250 
251     /**
252      * Set the \a margin for a legend \a component.
253      */
254     void setStyleMargin( QgsLegendStyle::Style component, double margin );
255 
256     /**
257      * Set the \a margin for a particular \a side of a legend \a component.
258      */
259     void setStyleMargin( QgsLegendStyle::Style component, QgsLegendStyle::Side side, double margin );
260 
261     /**
262      * Returns the spacing in-between lines in layout units.
263      * \see setLineSpacing()
264      */
265     double lineSpacing() const;
266 
267     /**
268      * Sets the \a spacing in-between multiple lines.
269      * \see lineSpacing()
270      */
271     void setLineSpacing( double spacing );
272 
273     /**
274      * Returns the legend box space.
275      * \see setBoxSpace()
276      */
277     double boxSpace() const;
278 
279     /**
280      * Sets the legend box \a space.
281      * \see boxSpace()
282      */
283     void setBoxSpace( double space );
284 
285     /**
286      * Returns the legend column spacing.
287      * \see setColumnSpace()
288      */
289     double columnSpace() const;
290 
291     /**
292      * Sets the legend column \a spacing.
293      * \see columnSpace()
294      */
295     void setColumnSpace( double spacing );
296 
297     /**
298      * Returns the legend font color.
299      * \see setFontColor()
300      */
301     QColor fontColor() const;
302 
303     /**
304      * Sets the legend font \a color.
305      * \see fontColor()
306      */
307     void setFontColor( const QColor &color );
308 
309     /**
310      * Returns the legend symbol width.
311      * \see setSymbolWidth()
312      */
313     double symbolWidth() const;
314 
315     /**
316      * Sets the legend symbol \a width.
317      * \see symbolWidth()
318      */
319     void setSymbolWidth( double width );
320 
321     /**
322      * Returns the maximum symbol size (in mm). 0.0 means there is no maximum set.
323      *
324      * \see setMaximumSymbolSize()
325      * \since QGIS 3.16
326      */
327     double maximumSymbolSize() const;
328 
329     /**
330      * Set the maximum symbol \a size for symbol (in millimeters).
331      *
332      * A symbol size of 0.0 indicates no maximum is set.
333      *
334      * \see maximumSymbolSize()
335      * \since QGIS 3.16
336      */
337     void setMaximumSymbolSize( double size );
338 
339     /**
340      * Returns the minimum symbol size (in mm). A value 0.0 means there is no minimum set.
341      *
342      * \see setMinimumSymbolSize
343      * \since QGIS 3.16
344      */
345     double minimumSymbolSize() const;
346 
347     /**
348      * Set the minimum symbol \a size for symbol (in millimeters).
349      *
350      * A symbol size of 0.0 indicates no minimum is set.
351      *
352      * \see minimumSymbolSize()
353      * \since QGIS 3.16
354      */
355     void setMinimumSymbolSize( double size );
356 
357     /**
358      * Sets the \a alignment for placement of legend symbols.
359      *
360      * Only Qt::AlignLeft or Qt::AlignRight are supported values.
361      *
362      * \see symbolAlignment()
363      * \since QGIS 3.10
364      */
365     void setSymbolAlignment( Qt::AlignmentFlag alignment );
366 
367     /**
368      * Returns the alignment for placement of legend symbols.
369      *
370      * Only Qt::AlignLeft or Qt::AlignRight are supported values.
371      *
372      * \see setSymbolAlignment()
373      * \since QGIS 3.10
374      */
375     Qt::AlignmentFlag symbolAlignment() const;
376 
377     /**
378      * Returns the legend symbol height.
379      * \see setSymbolHeight()
380      */
381     double symbolHeight() const;
382 
383     /**
384      * Sets the legend symbol \a height.
385      * \see symbolHeight()
386      */
387     void setSymbolHeight( double height );
388 
389     /**
390      * Returns the WMS legend width.
391      * \see setWmsLegendWidth()
392      */
393     double wmsLegendWidth() const;
394 
395     /**
396      * Sets the WMS legend \a width.
397      * \see wmsLegendWidth()
398      */
399     void setWmsLegendWidth( double width );
400 
401     /**
402      * Returns the WMS legend height.
403      * \see setWmsLegendHeight()
404      */
405     double wmsLegendHeight() const;
406 
407     /**
408      * Sets the WMS legend \a height.
409      * \see wmsLegendHeight()
410      */
411     void setWmsLegendHeight( double height );
412 
413     /**
414      * Sets the legend text wrapping \a string.
415      * \see wrapString()
416      */
417     void setWrapString( const QString &string );
418 
419     /**
420      * Returns the legend text wrapping string.
421      * \see setWrapString()
422      */
423     QString wrapString() const;
424 
425     /**
426      * Returns the legend column count.
427      * \see setColumnCount()
428      */
429     int columnCount() const;
430 
431     /**
432      * Sets the legend column \a count.
433      * \see columnCount()
434      */
435     void setColumnCount( int count );
436 
437     /**
438      * Returns whether the legend items from a single layer can be split
439      * over multiple columns.
440      * \see setSplitLayer()
441      */
442     bool splitLayer() const;
443 
444     /**
445      * Sets whether the legend items from a single layer can be split
446      * over multiple columns.
447      * \see splitLayer()
448      */
449     void setSplitLayer( bool enabled );
450 
451     /**
452      * Returns whether column widths should be equalized.
453      * \see setEqualColumnWidth()
454      */
455     bool equalColumnWidth() const;
456 
457     /**
458      * Sets whether column widths should be equalized.
459      * \see equalColumnWidth()
460      */
461     void setEqualColumnWidth( bool equalize );
462 
463     /**
464      * Returns whether a stroke will be drawn around raster symbol items.
465      * \see setDrawRasterStroke()
466      * \see rasterStrokeColor()
467      * \see rasterStrokeWidth()
468      */
469     bool drawRasterStroke() const;
470 
471     /**
472      * Sets whether a stroke will be drawn around raster symbol items.
473      * \param enabled set to TRUE to draw borders
474      * \see drawRasterStroke()
475      * \see setRasterStrokeColor()
476      * \see setRasterStrokeWidth()
477      */
478     void setDrawRasterStroke( bool enabled );
479 
480     /**
481      * Returns the stroke color for the stroke drawn around raster symbol items. The stroke is
482      * only drawn if drawRasterStroke() is TRUE.
483      * \see setRasterStrokeColor()
484      * \see drawRasterStroke()
485      * \see rasterStrokeWidth()
486      */
487     QColor rasterStrokeColor() const;
488 
489     /**
490      * Sets the stroke \a color for the stroke drawn around raster symbol items. The stroke is
491      * only drawn if drawRasterStroke() is TRUE.
492      * \see rasterStrokeColor()
493      * \see setDrawRasterStroke()
494      * \see setRasterStrokeWidth()
495      */
496     void setRasterStrokeColor( const QColor &color );
497 
498     /**
499      * Returns the stroke width (in layout units) for the stroke drawn around raster symbol items. The stroke is
500      * only drawn if drawRasterStroke() is TRUE.
501      * \see setRasterStrokeWidth()
502      * \see drawRasterStroke()
503      * \see rasterStrokeColor()
504      */
505     double rasterStrokeWidth() const;
506 
507     /**
508      * Sets the stroke width for the stroke drawn around raster symbol items. The stroke is
509      * only drawn if drawRasterStroke() is TRUE.
510      * \see rasterStrokeWidth()
511      * \see setDrawRasterStroke()
512      * \see setRasterStrokeColor()
513      */
514     void setRasterStrokeWidth( double width );
515 
516     /**
517      * Sets the \a map to associate with the legend.
518      * \see linkedMap()
519      */
520     void setLinkedMap( QgsLayoutItemMap *map );
521 
522     /**
523      * Returns the associated map.
524      * \see setLinkedMap()
525      */
linkedMap()526     QgsLayoutItemMap *linkedMap() const { return mMap; }
527 
528     /**
529      * Returns the name of the theme currently linked to the legend.
530      *
531      * This usually equates to the theme rendered in the linkedMap().
532      *
533      * \since QGIS 3.14
534      */
535     QString themeName() const;
536 
537     /**
538      * Updates the model and all legend entries.
539      */
540     void updateLegend();
541 
542     /**
543      * Updates the legend content when filtered by map.
544      */
545     void updateFilterByMap( bool redraw = true );
546 
547     /**
548      * Returns the legend's renderer settings object.
549      */
legendSettings()550     const QgsLegendSettings &legendSettings() const { return mSettings; }
551 
552     void paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget ) override;
553 
554     void finalizeRestoreFromXml() override;
555 
556     QgsExpressionContext createExpressionContext() const override;
557     ExportLayerBehavior exportLayerBehavior() const override;
558     bool accept( QgsStyleEntityVisitorInterface *visitor ) const override;
559 
560   public slots:
561 
562     void refresh() override;
563     void refreshDataDefinedProperty( QgsLayoutObject::DataDefinedProperty property = QgsLayoutObject::AllProperties ) override;
564 
565   protected:
566     void draw( QgsLayoutItemRenderContext &context ) override;
567     bool writePropertiesToElement( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const override;
568     bool readPropertiesFromElement( const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context ) override;
569 
570   private slots:
571 
572     //! Removes the associated map if the map is deleted.
573     void invalidateCurrentMap();
574 
575     void updateFilterByMapAndRedraw();
576 
577 
578     //! update legend in case style of associated map has changed
579     void mapLayerStyleOverridesChanged();
580     //! update legend in case theme of associated map has changed
581     void mapThemeChanged( const QString &theme );
582 
583     //! react to atlas
584     void onAtlasEnded();
585     void onAtlasFeature();
586 
587     void nodeCustomPropertyChanged( QgsLayerTreeNode *node, const QString &key );
588 
589     //! Clears any data cached for the legend model
590     void clearLegendCachedData();
591 
592   private:
593     QgsLayoutItemLegend() = delete;
594 
595     //! use new custom layer tree and update model. if new root is NULLPTR, will use project's tree
596     void setCustomLayerTree( QgsLayerTree *rootGroup );
597 
598     void setupMapConnections( QgsLayoutItemMap *map, bool connect = true );
599 
600     void setModelStyleOverrides( const QMap<QString, QString> &overrides );
601 
602     std::unique_ptr< QgsLegendModel > mLegendModel;
603     std::unique_ptr< QgsLayerTreeGroup > mCustomLayerTree;
604 
605     QgsLegendSettings mSettings;
606 
607     QString mTitle;
608     int mColumnCount = 1;
609 
610     QString mMapUuid;
611     QgsLayoutItemMap *mMap = nullptr;
612 
613     bool mLegendFilterByMap = false;
614     bool mLegendFilterByExpression = false;
615 
616     //! whether to filter out legend elements outside of the atlas feature
617     bool mFilterOutAtlas = false;
618 
619     //! tag for update request
620     bool mFilterAskedForUpdate = false;
621     //! actual filter update
622     void doUpdateFilterByMap();
623 
624     bool mInAtlas = false;
625 
626     //! Will be FALSE until the associated map scale and DPI have been calculated
627     bool mInitialMapScaleCalculated = false;
628 
629     //! Will be TRUE if the legend size should be totally reset at next paint
630     bool mForceResize = false;
631 
632     //! Will be TRUE if the legend should be resized automatically to fit contents
633     bool mSizeToContents = true;
634 
635     //! Name of theme for legend -- usually the theme associated with the linked map.
636     QString mThemeName;
637 
638     friend class QgsCompositionConverter;
639 
640 };
641 
642 #endif // QGSLAYOUTITEMLEGEND_H
643 
644