1 /*
2  * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB.  All rights reserved.
3  *
4  * This file is part of the KD Chart library.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 #ifndef KCHARTLEGEND_H
21 #define KCHARTLEGEND_H
22 
23 #include "KChartAbstractAreaWidget.h"
24 #include "KChartPosition.h"
25 #include "KChartMarkerAttributes.h"
26 
27 class QTextTable;
28 
29 namespace KChart {
30 
31 class AbstractDiagram;
32 typedef QList<AbstractDiagram*> DiagramList;
33 typedef QList<const AbstractDiagram*> ConstDiagramList;
34 
35 /**
36   * @brief Legend defines the interface for the legend drawing class.
37   *
38   * Legend is the class for drawing legends for all kinds of diagrams ("chart types").
39   *
40   * Legend is drawn on chart level, not per diagram, but you can have more than one
41   * legend per chart, using KChart::Chart::addLegend().
42   *
43   * \note Legend is different from all other classes of KChart, since it can be
44   * displayed outside of the Chart's area.  If you want to, you can embedd the legend
45   * into your own widget, or into another part of a bigger layout, into which you might
46   * have inserted the Chart.
47   *
48   * On the other hand, please note that you MUST call Chart::addLegend to get your
49   * legend positioned into the correct place of your chart - if you want to have
50   * the legend shown inside of the chart (that's probably true for most cases).
51   */
52 class KCHART_EXPORT Legend : public AbstractAreaWidget
53 {
54     Q_OBJECT
55 
56     Q_DISABLE_COPY( Legend )
57     KCHART_DECLARE_PRIVATE_DERIVED_QWIDGET( Legend )
58 
59 public:
60     explicit Legend( QWidget* parent = nullptr );
61     explicit Legend( KChart::AbstractDiagram* diagram, QWidget* parent = nullptr );
62     virtual ~Legend();
63 
64 
65     enum LegendStyle { MarkersOnly     = 0,
66                        LinesOnly       = 1,
67                        MarkersAndLines = 2 };
68 
69 
70     void setLegendStyle( LegendStyle style );
71     LegendStyle legendStyle() const;
72 
73 
74 
75     /**
76       * Creates an exact copy of this legend.
77       */
78     virtual Legend * clone() const;
79 
80     /**
81      * Returns true if both legends have the same settings.
82      */
83     bool compare( const Legend* other ) const;
84 
85     void resizeEvent( QResizeEvent * event ) override; // TODO: should be protected
86 
87     virtual void paint( QPainter* painter ) override;
88     void paint( QPainter* painter, const QRect &rect);
89     void setVisible( bool visible ) override;
90 
91     /**
92         Specifies the reference area for font size of title text,
93         and for font size of the item texts, IF automatic area
94         detection is set.
95 
96         \note This parameter is ignored, if the Measure given for
97         setTitleTextAttributes (or setTextAttributes, resp.) is
98         not specifying automatic area detection.
99 
100         If no reference area is specified, but automatic area
101         detection is set, then the size of the legend's parent
102         widget will be used.
103 
104         \sa KChart::Measure, KChartEnums::MeasureCalculationMode
105     */
106     void setReferenceArea( const QWidget* area );
107     /**
108         Returns the reference area, that is used for font size of title text,
109         and for font size of the item texts, IF automatic area
110         detection is set.
111 
112         \sa setReferenceArea
113     */
114     const QWidget* referenceArea() const;
115 
116     /**
117       * The first diagram of the legend or 0 if there was none added to the legend.
118       * @return The first diagram of the legend or 0.
119       *
120       * \sa diagrams, addDiagram, removeDiagram, removeDiagrams, replaceDiagram, setDiagram
121       */
122     KChart::AbstractDiagram* diagram() const;
123 
124     /**
125       * The list of all diagrams associated with the legend.
126       * @return The list of all diagrams associated with the legend.
127       *
128       * \sa diagram, addDiagram, removeDiagram, removeDiagrams, replaceDiagram, setDiagram
129       */
130     DiagramList diagrams() const;
131 
132     /**
133      * @return The list of diagrams associated with this legend.
134      */
135     ConstDiagramList constDiagrams() const;
136 
137     /**
138       * Add the given diagram to the legend.
139       * @param newDiagram The diagram to add.
140       *
141       * \sa diagram, diagrams, removeDiagram, removeDiagrams, replaceDiagram, setDiagram
142       */
143     void addDiagram( KChart::AbstractDiagram* newDiagram );
144 
145     /**
146       * Removes the diagram from the legend's list of diagrams.
147       *
148       * \sa diagram, diagrams, addDiagram, removeDiagrams, replaceDiagram, setDiagram
149       */
150     void removeDiagram( KChart::AbstractDiagram* oldDiagram );
151 
152     /**
153       * Removes all diagrams from the legend's list of diagrams.
154       *
155       * \sa diagram, diagrams, addDiagram, removeDiagram, replaceDiagram, setDiagram
156       */
157     void removeDiagrams();
158 
159     /**
160       * Replaces the old diagram, or appends the
161       * new diagram, it there is none yet.
162       *
163       * @param newDiagram The diagram to be used instead of the old one.
164       * If this parameter is zero, the first diagram will just be removed.
165       *
166       * @param oldDiagram The diagram to be removed by the new one. This
167       * diagram will be deleted automatically. If the parameter is omitted,
168       * the very first diagram will be replaced. In case, there was no
169       * diagram yet, the new diagram will just be added.
170       *
171       * \sa diagram, diagrams, addDiagram, removeDiagram, removeDiagrams, setDiagram
172       */
173     void replaceDiagram( KChart::AbstractDiagram* newDiagram,
174                          KChart::AbstractDiagram* oldDiagram = nullptr );
175 
176     /**
177       * Returns the offset of the first dataset of \c diagram.
178       *
179       */
180     uint dataSetOffset( KChart::AbstractDiagram* diagram );
181 
182     /**
183       * @brief A convenience method doing the same as replaceDiagram( newDiagram, 0 );
184       *
185       * Replaces the first diagram by the given diagram.
186       * If the legend's list of diagram is empty the given diagram is added to the list.
187       *
188       * \sa diagram, diagrams, addDiagram, removeDiagram, removeDiagrams, replaceDiagram
189       */
190     void setDiagram( KChart::AbstractDiagram* newDiagram );
191 
192     /**
193      * \brief Specify the position of a non-floating legend.
194      *
195      * Use setFloatingPosition to set position and alignment
196      * if your legend is floating.
197      *
198      * \sa setAlignment, setFloatingPosition
199      */
200     void setPosition( Position position );
201 
202     /**
203      * Returns the position of a non-floating legend.
204      * \sa setPosition
205      */
206     Position position() const;
207 
208     /**
209      * \brief Specify the alignment of a non-floating legend.
210      *
211      * Use setFloatingPosition to set position and alignment
212      * if your legend is floating.
213      *
214      * \sa alignment, setPosition, setFloatingPosition
215      */
216     void setAlignment( Qt::Alignment );
217 
218     /**
219      * Returns the alignment of a non-floating legend.
220      * \sa setAlignment
221      */
222     Qt::Alignment alignment() const;
223 
224     /**
225      * \brief Specify the alignment of the text elements within the legend
226      *
227      * \sa textAlignment()
228      */
229     void setTextAlignment( Qt::Alignment );
230 
231     /**
232      * \brief Returns the alignment used while rendering text elements within the legend.
233      *
234      * \sa setTextAlignment()
235      */
236      Qt::Alignment textAlignment() const;
237 
238      /**
239       * \brief Specify the alignment of the legend symbol( alignment of Legend::LinesOnly)
240       *  within the legend
241       *
242       * \sa legendSymbolAlignment()
243       */
244      void setLegendSymbolAlignment(Qt::Alignment);
245 
246      /**
247       * \brief Returns the alignment used while drawing legend symbol(alignment of Legend::LinesOnly)
248       * within the legend.
249       *
250       * \sa setLegendSymbolAlignment()
251       */
252      Qt::Alignment legendSymbolAlignment() const;
253 
254     /**
255      * \brief Specify the position and alignment of a floating legend.
256      *
257      * Use setPosition and setAlignment to set position and alignment
258      * if your legend is non-floating.
259      *
260      * \note When setFloatingPosition is called, the Legend's position value is set to
261      * KChart::Position::Floating automatically.
262      *
263      * If your Chart is pointed to by m_chart, your could have the floating legend
264      * aligned exactly to the chart's coordinate plane's top-right corner
265      * with the following commands:
266 \verbatim
267 KChart::RelativePosition relativePosition;
268 relativePosition.setReferenceArea( m_chart->coordinatePlane() );
269 relativePosition.setReferencePosition( Position::NorthEast );
270 relativePosition.setAlignment( Qt::AlignTop | Qt::AlignRight );
271 relativePosition.setHorizontalPadding(
272     KChart::Measure( -1.0, KChartEnums::MeasureCalculationModeAbsolute ) );
273 relativePosition.setVerticalPadding(
274     KChart::Measure( 0.0, KChartEnums::MeasureCalculationModeAbsolute ) );
275 m_legend->setFloatingPosition( relativePosition );
276 \endverbatim
277      *
278      * To have the legend positioned at a fixed point, measured from the QPainter's top left corner,
279      * you could use the following code code:
280      *
281 \verbatim
282 KChart::RelativePosition relativePosition;
283 relativePosition.setReferencePoints( PositionPoints( QPointF( 0.0, 0.0 ) ) );
284 relativePosition.setReferencePosition( Position::NorthWest );
285 relativePosition.setAlignment( Qt::AlignTop | Qt::AlignLeft );
286 relativePosition.setHorizontalPadding(
287     KChart::Measure( 4.0, KChartEnums::MeasureCalculationModeAbsolute ) );
288 relativePosition.setVerticalPadding(
289     KChart::Measure( 4.0, KChartEnums::MeasureCalculationModeAbsolute ) );
290 m_legend->setFloatingPosition( relativePosition );
291 \endverbatim
292      * Actually that's exactly the code KChart is using as default position for any floating legends,
293      * so if you just say setPosition( KChart::Position::Floating ) without calling setFloatingPosition
294      * your legend will be positioned at point 4/4.
295      *
296      * \sa setPosition, setAlignment
297      */
298     void setFloatingPosition( const RelativePosition& relativePosition );
299 
300     /**
301      * Returns the position of a floating legend.
302      * \sa setFloatingPosition
303      */
304     const RelativePosition floatingPosition() const;
305 
306     void setOrientation( Qt::Orientation orientation );
307     Qt::Orientation orientation() const;
308 
309 
310     void setSortOrder( Qt::SortOrder order );
311     Qt::SortOrder sortOrder() const;
312 
313     void setShowLines( bool legendShowLines );
314     bool showLines() const;
315 
316 
317     /**
318         \brief Removes all legend texts that might have been set by setText.
319 
320         This resets the Legend to default behaviour: Texts are created automatically.
321     */
322     void resetTexts();
323     void setText( uint dataset, const QString& text );
324     QString text( uint dataset ) const;
325     const QMap<uint,QString> texts() const;
326 
327     /**
328      * Sets a list of datasets that are to be hidden in the legend.
329      *
330      * By passing an empty list, you show all datasets.
331      * All datasets are shown by default, which means
332      * that hiddenDatasets() returns an empty list.
333      */
334     void setHiddenDatasets( const QList<uint> hiddenDatasets );
335     const QList<uint> hiddenDatasets() const;
336     void setDatasetHidden( uint dataset, bool hidden );
337     bool datasetIsHidden( uint dataset ) const;
338 
339     uint datasetCount() const;
340 
341     void setDefaultColors();
342     void setRainbowColors();
343     void setSubduedColors( bool ordered = false );
344 
345     void setBrushesFromDiagram( KChart::AbstractDiagram* diagram );
346 
347     /**
348      * Note: there is no color() getter method, since setColor
349      * just sets a QBrush with the respective color, so the
350      * brush() getter method is sufficient.
351      */
352     void setColor( uint dataset, const QColor& color );
353 
354     void setBrush( uint dataset, const QBrush& brush );
355     QBrush brush( uint dataset ) const;
356     const QMap<uint,QBrush> brushes() const;
357 
358     void setPen( uint dataset, const QPen& pen );
359     QPen pen( uint dataset ) const;
360     const QMap<uint,QPen> pens() const;
361 
362     /**
363      * Note that any sizes specified via setMarkerAttributes are ignored,
364      * unless you disable the automatic size calculation, by saying
365      * setUseAutomaticMarkerSize( false )
366      */
367     void setMarkerAttributes( uint dataset, const MarkerAttributes& );
368     MarkerAttributes markerAttributes( uint dataset ) const;
369     const QMap<uint, MarkerAttributes> markerAttributes() const;
370 
371     /**
372      * This option is on by default, it means that Marker sizes in the Legend
373      * will be the same as the font height used for their respective label texts.
374      *
375      * Set this to false, if you want to specify the marker sizes via setMarkerAttributes
376      * or if you want the Legend to use the same marker sizes as they are used in the Diagrams.
377      */
378     void setUseAutomaticMarkerSize( bool useAutomaticMarkerSize );
379     bool useAutomaticMarkerSize() const;
380 
381     void setTextAttributes( const TextAttributes &a );
382     TextAttributes textAttributes() const;
383 
384     void setTitleText( const QString& text );
385     QString titleText() const;
386 
387     void setTitleTextAttributes( const TextAttributes &a );
388     TextAttributes titleTextAttributes() const;
389 
390     void setSpacing( uint space );
391     uint spacing() const;
392 
393     // called internally by KChart::Chart, when painting into a custom QPainter
394     void forceRebuild() override;
395 
396     QSize minimumSizeHint() const override;
397     QSize sizeHint() const override;
398     bool hasHeightForWidth() const override;
399     int heightForWidth( int width ) const override;
400     void needSizeHint() override;
401     void resizeLayout( const QSize& size ) override;
402 
403 Q_SIGNALS:
404     void destroyedLegend( Legend* );
405     /** Emitted upon change of a property of the Legend or any of its components. */
406     void propertiesChanged();
407 
408 private Q_SLOTS:
409     void emitPositionChanged();
410     void resetDiagram( AbstractDiagram* );
411     void activateTheLayout();
412     void setNeedRebuild();
413     void buildLegend();
414 }; // End of class Legend
415 
416 }
417 
418 
419 #endif // KCHARTLEGEND_H
420