##// END OF EJS Templates
split legend layout to vertical and horizontal functions
sauimone -
r810:c712716bc681
parent child
Show More
@@ -1,235 +1,236
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2012 Digia Plc
4 4 ** All rights reserved.
5 5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 6 **
7 7 ** This file is part of the Qt Commercial Charts Add-on.
8 8 **
9 9 ** $QT_BEGIN_LICENSE$
10 10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 11 ** accordance with the Qt Commercial License Agreement provided with the
12 12 ** Software or, alternatively, in accordance with the terms contained in
13 13 ** a written agreement between you and Digia.
14 14 **
15 15 ** If you have questions regarding the use of this file, please use
16 16 ** contact form at http://qt.digia.com
17 17 ** $QT_END_LICENSE$
18 18 **
19 19 ****************************************************************************/
20 20
21 21 #include "barchartitem_p.h"
22 22 #include "bar_p.h"
23 23 #include "barvalue_p.h"
24 24 #include "qbarset.h"
25 25 #include "qbarseries.h"
26 26 #include "qchart.h"
27 27 #include "qchartaxis.h"
28 28 #include "qchartaxiscategories.h"
29 29 #include "chartpresenter_p.h"
30 30 #include "chartanimator_p.h"
31 31 #include "chartdataset_p.h"
32 32 #include <QDebug>
33 33 #include <QToolTip>
34 34
35 35 QTCOMMERCIALCHART_BEGIN_NAMESPACE
36 36
37 37 BarChartItem::BarChartItem(QBarSeries *series, ChartPresenter *presenter) :
38 38 ChartItem(presenter),
39 39 m_layoutSet(false),
40 40 m_series(series)
41 41 {
42 42 connect(series, SIGNAL(showToolTip(QPoint,QString)), this, SLOT(showToolTip(QPoint,QString)));
43 43 connect(series, SIGNAL(updatedBars()), this, SLOT(handleLayoutChanged()));
44 44 //TODO: connect(series,SIGNAL("position or size has changed"), this, SLOT(handleLayoutChanged()));
45 45 connect(series, SIGNAL(restructuredBar(int)), this, SLOT(handleModelChanged(int)));
46 46 setZValue(ChartPresenter::BarSeriesZValue);
47 47 dataChanged();
48 48 }
49 49
50 50 BarChartItem::~BarChartItem()
51 51 {
52 52 disconnect(this,SLOT(showToolTip(QPoint,QString)));
53 53 }
54 54
55 55 void BarChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
56 56 {
57 57 if (!m_layoutSet) {
58 58 qWarning() << "BarChartItem::paint called without layout set. Aborting.";
59 59 return;
60 60 }
61 61
62 62 foreach(QGraphicsItem* i, childItems())
63 63 i->paint(painter,option,widget);
64 64 }
65 65
66 66 QRectF BarChartItem::boundingRect() const
67 67 {
68 68 return m_rect;
69 69 }
70 70
71 71 void BarChartItem::dataChanged()
72 72 {
73 73 // TODO: performance optimizations. Do we really need to delete and create items every time data is changed or can we reuse them?
74 74 // Delete old bars
75 75 foreach (QGraphicsItem *item, childItems())
76 76 delete item;
77 77
78 78 m_bars.clear();
79 79 m_floatingValues.clear();
80 80 m_layout.clear();
81 81
82 82 // Create new graphic items for bars
83 83 for (int c = 0; c < m_series->categoryCount(); c++) {
84 84 QString category = m_series->categoryName(c);
85 85 for (int s = 0; s < m_series->barsetCount(); s++) {
86 86 QBarSet *set = m_series->barsetAt(s);
87 87 Bar *bar = new Bar(category,this);
88 88 childItems().append(bar);
89 89 m_bars.append(bar);
90 90 connect(bar, SIGNAL(clicked(QString)), set, SIGNAL(clicked(QString)));
91 91 connect(bar, SIGNAL(rightClicked(QString)), set, SIGNAL(rightClicked(QString)));
92 92 connect(bar, SIGNAL(hoverEntered(QPoint)), set, SLOT(barHoverEnterEvent(QPoint)));
93 93 connect(bar, SIGNAL(hoverLeaved()), set, SLOT(barHoverLeaveEvent()));
94 94 m_layout.append(QRectF(0, 0, 0, 0));
95 95 }
96 96 }
97 97
98 98 // Create floating values
99 99 for (int category = 0; category < m_series->categoryCount(); category++) {
100 100 for (int s = 0; s < m_series->barsetCount(); s++) {
101 101 QBarSet *set = m_series->barsetAt(s);
102 102 BarValue *value = new BarValue(*set, this);
103 103 childItems().append(value);
104 104 m_floatingValues.append(value);
105 105 connect(set, SIGNAL(toggleFloatingValues()), value, SLOT(toggleVisible()));
106 106 }
107 107 }
108 108 }
109
109 110 QVector<QRectF> BarChartItem::calculateLayout()
110 111 {
111 112 QVector<QRectF> layout;
112 113
113 114 // Use temporary qreals for accurancy (we might get some compiler warnings... :)
114 115 qreal categoryCount = m_series->categoryCount();
115 116 qreal setCount = m_series->barsetCount();
116 117
117 118 qreal width = geometry().width();
118 119 qreal height = geometry().height();
119 120
120 121 qreal max = m_series->max();
121 122
122 123 // Domain:
123 124 if (m_domainMaxY > max) {
124 125 max = m_domainMaxY;
125 126 }
126 127
127 128 qreal scale = (height / max);
128 129 qreal categoryWidth = width / categoryCount;
129 130 qreal barWidth = categoryWidth / (setCount+1);
130 131
131 132 int itemIndex(0);
132 133 for (int category = 0; category < categoryCount; category++) {
133 134 qreal xPos = categoryWidth * category + barWidth / 2;
134 135 qreal yPos = height;
135 136 for (int set = 0; set < setCount; set++) {
136 137 qreal barHeight = m_series->valueAt(set, category) * scale;
137 138 Bar* bar = m_bars.at(itemIndex);
138 139
139 140 QRectF rect(xPos, yPos - barHeight, barWidth, barHeight);
140 141 layout.append(rect);
141 142 bar->setPen(m_series->barsetAt(set)->pen());
142 143 bar->setBrush(m_series->barsetAt(set)->brush());
143 144 itemIndex++;
144 145 xPos += barWidth;
145 146 }
146 147 }
147 148
148 149 // Position floating values
149 150 itemIndex = 0;
150 151 for (int category = 0; category < m_series->categoryCount(); category++) {
151 152 qreal xPos = categoryWidth * category + barWidth;
152 153 qreal yPos = height;
153 154 for (int set=0; set < m_series->barsetCount(); set++) {
154 155 qreal barHeight = m_series->valueAt(set, category) * scale;
155 156 BarValue* value = m_floatingValues.at(itemIndex);
156 157
157 158 QBarSet* barSet = m_series->barsetAt(set);
158 159 value->resize(100, 50); // TODO: proper layout for this.
159 160 value->setPos(xPos, yPos-barHeight / 2);
160 161 value->setPen(barSet->floatingValuePen());
161 162
162 163 if (!qFuzzyIsNull(m_series->valueAt(set,category))) {
163 164 value->setText(QString::number(m_series->valueAt(set, category)));
164 165 } else {
165 166 value->setText(QString(""));
166 167 }
167 168
168 169 itemIndex++;
169 170 xPos += barWidth;
170 171 }
171 172 }
172 173
173 174 return layout;
174 175 }
175 176
176 177 void BarChartItem::applyLayout(const QVector<QRectF> &layout)
177 178 {
178 179 if (animator())
179 180 animator()->updateLayout(this, m_layout, layout);
180 181 else
181 182 setLayout(layout);
182 183 }
183 184
184 185 void BarChartItem::setLayout(const QVector<QRectF> &layout)
185 186 {
186 187 m_layout = layout;
187 188
188 189 for (int i=0; i < m_bars.count(); i++)
189 190 m_bars.at(i)->setRect(layout.at(i));
190 191
191 192 update();
192 193 }
193 194
194 195 //handlers
195 196
196 197 void BarChartItem::handleModelChanged(int index)
197 198 {
198 199 Q_UNUSED(index)
199 200 dataChanged();
200 201 }
201 202
202 203 void BarChartItem::handleDomainChanged(qreal minX, qreal maxX, qreal minY, qreal maxY)
203 204 {
204 205 m_domainMinX = minX;
205 206 m_domainMaxX = maxX;
206 207 m_domainMinY = minY;
207 208 m_domainMaxY = maxY;
208 209 handleLayoutChanged();
209 210 }
210 211
211 212 void BarChartItem::handleGeometryChanged(const QRectF &rect)
212 213 {
213 214 m_rect = rect;
214 215 handleLayoutChanged();
215 216 m_layoutSet = true;
216 217 setPos(rect.topLeft());
217 218 }
218 219
219 220 void BarChartItem::handleLayoutChanged()
220 221 {
221 222 QVector<QRectF> layout = calculateLayout();
222 223 applyLayout(layout);
223 224 update();
224 225 }
225 226
226 227
227 228 void BarChartItem::showToolTip(QPoint pos, QString tip)
228 229 {
229 230 // TODO: cool tooltip instead of default
230 231 QToolTip::showText(pos, tip);
231 232 }
232 233
233 234 #include "moc_barchartitem_p.cpp"
234 235
235 236 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,724 +1,751
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2012 Digia Plc
4 4 ** All rights reserved.
5 5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 6 **
7 7 ** This file is part of the Qt Commercial Charts Add-on.
8 8 **
9 9 ** $QT_BEGIN_LICENSE$
10 10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 11 ** accordance with the Qt Commercial License Agreement provided with the
12 12 ** Software or, alternatively, in accordance with the terms contained in
13 13 ** a written agreement between you and Digia.
14 14 **
15 15 ** If you have questions regarding the use of this file, please use
16 16 ** contact form at http://qt.digia.com
17 17 ** $QT_END_LICENSE$
18 18 **
19 19 ****************************************************************************/
20 20
21 21 #include "qlegend.h"
22 22 #include "qchart_p.h"
23 23 #include "qseries.h"
24 24 #include "legendmarker_p.h"
25 25 #include "legendscrollbutton_p.h"
26 26 #include "qxyseries.h"
27 27 #include "qlineseries.h"
28 28 #include "qareaseries.h"
29 29 #include "qscatterseries.h"
30 30 #include "qsplineseries.h"
31 31 #include "qbarseries.h"
32 32 #include "qstackedbarseries.h"
33 33 #include "qpercentbarseries.h"
34 34 #include "qbarset.h"
35 35 #include "qpieseries.h"
36 36 #include "qpieslice.h"
37 37 #include "chartpresenter_p.h"
38 38 #include <QPainter>
39 39 #include <QPen>
40 40
41 41 #include <QGraphicsSceneEvent>
42 42
43 43 QTCOMMERCIALCHART_BEGIN_NAMESPACE
44 44
45 45 /*!
46 46 \class QLegend
47 47 \brief part of QtCommercial chart API.
48 48
49 49 QLegend is a graphical object, whics displays legend of the chart. Legend state is updated by QChart, when
50 50 series have been changed. By default, legend is drawn by QChart, but user can set a new parent to legend and
51 51 handle the drawing manually.
52 52 User isn't supposed to create or delete legend objects, but can reference it via QChart class.
53 53
54 54 \mainclass
55 55
56 56 \sa QChart, QSeries
57 57 */
58 58
59 59 /*!
60 60 \enum QLegend::Layout
61 61
62 62 This enum describes the possible position for legend inside chart.
63 63
64 64 \value LayoutTop
65 65 \value LayoutBottom
66 66 \value LayoutLeft
67 67 \value LayoutRight
68 68 */
69 69
70 70
71 71 /*!
72 72 \fn void QLegend::clicked(QSeries* series, Qt::MouseButton button)
73 73 \brief Notifies when series has been clicked on legend \a series \a button
74 74 */
75 75
76 76 /*!
77 77 \fn void QLegend::clicked(QBarSet* barset, Qt::MouseButton button)
78 78 \brief Notifies when barset has been clicked on legend \a barset \a button
79 79 */
80 80
81 81 /*!
82 82 \fn void QLegend::clicked(QPieSlice* slice, Qt::MouseButton button)
83 83 \brief Notifies when pie slice has been clicked on legend \a slice \a button
84 84 */
85 85
86 86 /*!
87 87 Constructs the legend object and sets the parent to \a parent
88 88 */
89 89 QLegend::QLegend(QChart *chart):QGraphicsWidget(chart),
90 90 m_margin(5),
91 91 m_pos(0,0),
92 92 m_minimumSize(50,20), // TODO: magic numbers
93 93 m_maximumSize(150,100),
94 94 m_size(m_minimumSize),
95 95 m_brush(Qt::darkGray), // TODO: default should come from theme
96 96 m_alignment(QLegend::AlignmentTop),
97 97 mFirstMarker(0),
98 98 m_attachedToChart(true),
99 99 m_chart(chart)
100 100 {
101 101 m_scrollButtonLeft = new LegendScrollButton(LegendScrollButton::ScrollButtonIdLeft, this);
102 102 m_scrollButtonRight = new LegendScrollButton(LegendScrollButton::ScrollButtonIdRight, this);
103 103 m_scrollButtonUp = new LegendScrollButton(LegendScrollButton::ScrollButtonIdUp, this);
104 104 m_scrollButtonDown = new LegendScrollButton(LegendScrollButton::ScrollButtonIdDown, this);
105 105 setZValue(ChartPresenter::LegendZValue);
106 106 }
107 107
108 108 /*!
109 109 Paints the legend to given \a painter. Paremeters \a option and \a widget arent used.
110 110 */
111 111 void QLegend::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
112 112 {
113 113 Q_UNUSED(option)
114 114 Q_UNUSED(widget)
115 115
116 116 painter->setOpacity(opacity());
117 117 painter->setPen(m_pen);
118 118 painter->setBrush(m_brush);
119 119 // painter->drawRect(boundingRect());
120 120 }
121 121
122 122 /*!
123 123 Bounding rect of legend.
124 124 */
125 125 QRectF QLegend::boundingRect() const
126 126 {
127 127 return QRectF(m_pos,m_size);
128 128 }
129 129
130 130 /*!
131 131 Sets the \a brush of legend. Brush affects the background of legend.
132 132 */
133 133 void QLegend::setBrush(const QBrush &brush)
134 134 {
135 135 if (m_brush != brush) {
136 136 m_brush = brush;
137 137 update();
138 138 }
139 139 }
140 140
141 141 /*!
142 142 Returns the brush used by legend.
143 143 */
144 144 QBrush QLegend::brush() const
145 145 {
146 146 return m_brush;
147 147 }
148 148
149 149 /*!
150 150 Sets the \a pen of legend. Pen affects the legend borders.
151 151 */
152 152 void QLegend::setPen(const QPen &pen)
153 153 {
154 154 if (m_pen != pen) {
155 155 m_pen = pen;
156 156 update();
157 157 }
158 158 }
159 159
160 160 /*!
161 161 Returns the pen used by legend
162 162 */
163 163
164 164 QPen QLegend::pen() const
165 165 {
166 166 return m_pen;
167 167 }
168 168
169 169 /*!
170 170 Sets the \a preferred layout for legend. Legend tries to paint itself on the defined position in chart.
171 171 \sa QLegend::Layout
172 172 */
173 173 void QLegend::setAlignmnent(QLegend::Alignments alignment)
174 174 {
175 175 // if (!m_attachedToChart) {
176 176 m_alignment = alignment;
177 177 updateLayout();
178 178 m_chart->resize(m_chart->size());
179 179 // }
180 180 }
181 181
182 182 /*!
183 183 Returns the preferred layout for legend
184 184 */
185 185 QLegend::Alignments QLegend::alignment() const
186 186 {
187 187 return m_alignment;
188 188 }
189 189
190 190 /*!
191 191 Returns the maximum size of legend.
192 192 */
193 193 QSizeF QLegend::maximumSize() const
194 194 {
195 195 return m_maximumSize;
196 196 }
197 197
198 198 /*!
199 199 Sets the maximum \a size for legend. The legend can't grow bigger than this size. If there are
200 200 more series than legend can fit to this size, scroll buttons are displayed.
201 201 */
202 202 void QLegend::setMaximumSize(const QSizeF size)
203 203 {
204 204 m_maximumSize = size;
205 205 updateLayout();
206 206 }
207 207
208 208 /*!
209 209 Returns the current size of legend.
210 210 */
211 211 QSizeF QLegend::size() const
212 212 {
213 213 return m_size;
214 214 }
215 215
216 216 /*!
217 217 Sets the \a size of legend. If size is bigger than maximum size of legend, the legend is resized to the maximum size.
218 218 \sa setMmaximumSize()
219 219 */
220 220 void QLegend::setSize(const QSizeF size)
221 221 {
222 222 m_size = size;
223 223 if (m_size.width() > m_maximumSize.width()) {
224 224 m_size.setWidth(m_maximumSize.width());
225 225 }
226 226 if (m_size.height() > m_maximumSize.height()) {
227 227 m_size.setHeight(m_maximumSize.height());
228 228 }
229 229 }
230 230
231 231 /*!
232 232 Sets position of legend to \a pos
233 233 */
234 234 void QLegend::setPos(const QPointF &pos)
235 235 {
236 236 m_pos = pos;
237 237 updateLayout();
238 238 }
239 239
240 240 /*!
241 241 \internal \a series \a domain Should be called when series is added to chart.
242 242 */
243 243 void QLegend::handleSeriesAdded(QSeries *series, Domain *domain)
244 244 {
245 245 Q_UNUSED(domain)
246 246
247 247 switch (series->type())
248 248 {
249 249 case QSeries::SeriesTypeLine: {
250 250 QLineSeries *lineSeries = static_cast<QLineSeries *>(series);
251 251 appendMarkers(lineSeries);
252 252 break;
253 253 }
254 254 case QSeries::SeriesTypeArea: {
255 255 QAreaSeries *areaSeries = static_cast<QAreaSeries *>(series);
256 256 appendMarkers(areaSeries);
257 257 break;
258 258 }
259 259 case QSeries::SeriesTypeBar: {
260 260 QBarSeries *barSeries = static_cast<QBarSeries *>(series);
261 261 appendMarkers(barSeries);
262 262 break;
263 263 }
264 264 case QSeries::SeriesTypeStackedBar: {
265 265 QStackedBarSeries *stackedBarSeries = static_cast<QStackedBarSeries *>(series);
266 266 appendMarkers(stackedBarSeries);
267 267 break;
268 268 }
269 269 case QSeries::SeriesTypePercentBar: {
270 270 QPercentBarSeries *percentBarSeries = static_cast<QPercentBarSeries *>(series);
271 271 appendMarkers(percentBarSeries);
272 272 break;
273 273 }
274 274 case QSeries::SeriesTypeScatter: {
275 275 QScatterSeries *scatterSeries = static_cast<QScatterSeries *>(series);
276 276 appendMarkers(scatterSeries);
277 277 break;
278 278 }
279 279 case QSeries::SeriesTypePie: {
280 280 QPieSeries *pieSeries = static_cast<QPieSeries *>(series);
281 281 appendMarkers(pieSeries);
282 282 connect(pieSeries,SIGNAL(added(QList<QPieSlice*>)),this,SLOT(handleAdded(QList<QPieSlice*>)));
283 283 break;
284 284 }
285 285 case QSeries::SeriesTypeSpline: {
286 286 QSplineSeries *splineSeries = static_cast<QSplineSeries *>(series);
287 287 appendMarkers(splineSeries);
288 288 break;
289 289 }
290 290 default: {
291 291 qWarning()<< "QLegend::handleSeriesAdded" << series->type() << "unknown series type.";
292 292 break;
293 293 }
294 294 }
295 295
296 296 updateLayout();
297 297 }
298 298
299 299 /*!
300 300 \internal \a series Should be called when series is removed from chart.
301 301 */
302 302 void QLegend::handleSeriesRemoved(QSeries *series)
303 303 {
304 304 switch (series->type())
305 305 {
306 306 case QSeries::SeriesTypeArea: {
307 307 QAreaSeries *areaSeries = static_cast<QAreaSeries *>(series);
308 308 deleteMarkers(areaSeries);
309 309 break;
310 310 }
311 311 case QSeries::SeriesTypePie: {
312 312 QPieSeries *pieSeries = static_cast<QPieSeries *>(series);
313 313 disconnect(pieSeries, SIGNAL(added(QList<QPieSlice *>)), this, SLOT(handleAdded(QList<QPieSlice *>)));
314 314 deleteMarkers(series);
315 315 break;
316 316 }
317 317 default: {
318 318 // All other types
319 319 deleteMarkers(series);
320 320 break;
321 321 }
322 322 }
323 323
324 324 updateLayout();
325 325 }
326 326
327 327 /*!
328 328 \internal \a slices Should be called when slices are added to pie chart.
329 329 */
330 330 void QLegend::handleAdded(QList<QPieSlice *> slices)
331 331 {
332 332 QPieSeries* series = static_cast<QPieSeries *> (sender());
333 333 foreach(QPieSlice* s, slices) {
334 334 LegendMarker* marker = new LegendMarker(series, s, this);
335 335 marker->setName(s->label());
336 336 marker->setBrush(s->brush());
337 337 connect(marker, SIGNAL(clicked(QPieSlice*,Qt::MouseButton)),
338 338 this, SIGNAL(clicked(QPieSlice*,Qt::MouseButton)));
339 339 connect(s, SIGNAL(changed()), marker, SLOT(changed()));
340 340 connect(s, SIGNAL(destroyed()), marker, SLOT(deleteLater()));
341 341 connect(marker, SIGNAL(destroyed()), this, SLOT(handleMarkerDestroyed()));
342 342 m_markers.append(marker);
343 343 childItems().append(marker);
344 344 }
345 345 updateLayout();
346 346 }
347 347
348 348 /*!
349 349 \internal \a slices Should be called when slices are removed from pie chart. Currently unused,
350 350 because removed slices are also deleted and we listen destroyed signal
351 351 */
352 352 void QLegend::handleRemoved(QList<QPieSlice *> slices)
353 353 {
354 354 Q_UNUSED(slices)
355 355 }
356 356
357 357
358 358 /*!
359 359 \internal Notifies legend that some marker has been removed. Sent by legend markers when destroyed
360 360 */
361 361 void QLegend::handleMarkerDestroyed()
362 362 {
363 363 LegendMarker* m = static_cast<LegendMarker *> (sender());
364 364 m_markers.removeOne(m);
365 365 updateLayout();
366 366 }
367 367
368 368 /*!
369 369 \internal \a event Handles clicked signals from scroll buttons
370 370 */
371 371 void QLegend::scrollButtonClicked(LegendScrollButton *scrollButton)
372 372 {
373 373 Q_ASSERT(scrollButton);
374 374
375 375 switch (scrollButton->id()) {
376 376 case LegendScrollButton::ScrollButtonIdLeft:
377 377 case LegendScrollButton::ScrollButtonIdUp: {
378 378 // Lower limit is same in these cases
379 379 mFirstMarker--;
380 380 checkFirstMarkerBounds();
381 381 break;
382 382 }
383 383 case LegendScrollButton::ScrollButtonIdRight:
384 384 case LegendScrollButton::ScrollButtonIdDown: {
385 385 mFirstMarker++;
386 386 checkFirstMarkerBounds();
387 387 break;
388 388 }
389 389 default: {
390 390 break;
391 391 }
392 392 }
393 393 updateLayout();
394 394 }
395 395
396 396 /*!
397 397 Detaches the legend from chart. Chart won't change layout of the legend.
398 398 */
399 399 void QLegend::detachFromChart()
400 400 {
401 401 m_attachedToChart = false;
402 402 }
403 403
404 404 /*!
405 405 Attaches the legend to chart. Chart may change layout of the legend.
406 406 */
407 407 void QLegend::attachToChart()
408 408 {
409 409 m_attachedToChart = true;
410 410 }
411 411
412 412 /*!
413 413 Returns true, if legend is attached to chart.
414 414 */
415 415 bool QLegend::attachedToChart()
416 416 {
417 417 return m_attachedToChart;
418 418 }
419 419
420 420 /*!
421 421 \internal Helper function. Appends markers from \a series to legend.
422 422 */
423 423 void QLegend::appendMarkers(QAreaSeries* series)
424 424 {
425 425 LegendMarker* marker = new LegendMarker(series,this);
426 426 connect(marker, SIGNAL(clicked(QSeries *, Qt::MouseButton)), this, SIGNAL(clicked(QSeries *, Qt::MouseButton)));
427 427 connect(marker, SIGNAL(destroyed()), this, SLOT(handleMarkerDestroyed()));
428 428 connect(series,SIGNAL(updated()),marker,SLOT(changed()));
429 429 marker->changed();
430 430 m_markers.append(marker);
431 431 childItems().append(marker);
432 432 }
433 433
434 434 /*!
435 435 \internal Helper function. Appends markers from \a series to legend.
436 436 */
437 437 void QLegend::appendMarkers(QXYSeries* series)
438 438 {
439 439 LegendMarker* marker = new LegendMarker(series,this);
440 440 connect(marker, SIGNAL(clicked(QSeries *, Qt::MouseButton)), this, SIGNAL(clicked(QSeries *, Qt::MouseButton)));
441 441 connect(marker, SIGNAL(destroyed()), this, SLOT(handleMarkerDestroyed()));
442 442 connect(series,SIGNAL(updated()),marker,SLOT(changed()));
443 443 marker->changed();
444 444 m_markers.append(marker);
445 445 childItems().append(marker);
446 446 }
447 447
448 448 /*!
449 449 \internal Helper function. Appends markers from \a series to legend.
450 450 */
451 451 void QLegend::appendMarkers(QBarSeries *series)
452 452 {
453 453 foreach(QBarSet* set, series->barSets()) {
454 454 LegendMarker* marker = new LegendMarker(series, set, this);
455 455 connect(marker, SIGNAL(clicked(QBarSet *, Qt::MouseButton)),
456 456 this, SIGNAL(clicked(QBarSet *, Qt::MouseButton)));
457 457 connect(set, SIGNAL(valueChanged()), marker, SLOT(changed()));
458 458 connect(marker, SIGNAL(destroyed()), this, SLOT(handleMarkerDestroyed()));
459 459 marker->changed();
460 460 m_markers.append(marker);
461 461 childItems().append(marker);
462 462 }
463 463 }
464 464
465 465 /*!
466 466 \internal Helper function. Appends markers from \a series to legend.
467 467 */
468 468 void QLegend::appendMarkers(QPieSeries *series)
469 469 {
470 470 foreach(QPieSlice* slice, series->slices()) {
471 471 LegendMarker* marker = new LegendMarker(series, slice, this);
472 472 connect(marker, SIGNAL(clicked(QPieSlice *, Qt::MouseButton)),
473 473 this, SIGNAL(clicked(QPieSlice *, Qt::MouseButton)));
474 474 connect(slice, SIGNAL(changed()), marker, SLOT(changed()));
475 475 connect(slice, SIGNAL(destroyed()), marker, SLOT(deleteLater()));
476 476 connect(marker, SIGNAL(destroyed()), this, SLOT(handleMarkerDestroyed()));
477 477 marker->changed();
478 478 m_markers.append(marker);
479 479 childItems().append(marker);
480 480 }
481 481 }
482 482
483 483 /*!
484 484 \internal Deletes all markers that are created from \a series
485 485 */
486 486 void QLegend::deleteMarkers(QSeries *series)
487 487 {
488 488 // Search all markers that belong to given series and delete them.
489 489 foreach (LegendMarker *m, m_markers) {
490 490 if (m->series() == series) {
491 491 m_markers.removeOne(m);
492 492 delete m;
493 493 }
494 494 }
495 495 }
496 496
497 497 /*!
498 498 \internal Updates layout of legend. Tries to fit as many markers as possible up to the maximum size of legend.
499 499 If items don't fit, sets the visibility of scroll buttons accordingly.
500 500 Causes legend to be resized.
501 501 */
502 502 void QLegend::updateLayout()
503 503 {
504 504 // Calculate layout for markers and text
505 505 if (m_markers.count() <= 0) {
506 506 // Nothing to do
507 507 return;
508 508 }
509 509
510 // Find out widest item.
511 QSizeF markerMaxSize = maximumMarkerSize();
512 510 checkFirstMarkerBounds();
513 511
514 // Use max height as scroll button size
515 rescaleScrollButtons(QSize(markerMaxSize.height() ,markerMaxSize.height()));
516
517 qreal totalWidth = 0;
518 qreal totalHeight = 0;
519 512 switch (m_alignment)
520 513 {
521 514 // Both cases organise items horizontally
522 515 case QLegend::AlignmentBottom:
523 516 case QLegend::AlignmentTop: {
517 layoutHorizontal();
518 break;
519 }
520 // Both cases organize items vertically
521 case QLegend::AlignmentLeft:
522 case QLegend::AlignmentRight: {
523 layoutVertical();
524 break;
525 }
526 default: {
527 break;
528 }
529 }
524 530
525 qreal xStep = markerMaxSize.width();
526 qreal x = m_pos.x() + m_margin;
527 qreal y = m_pos.y() + m_margin;
528 int column = 0;
529 int maxColumns = 1;
530 qreal scrollButtonWidth = 0;
531 }
531 532
532 // Set correct visibility for scroll scrollbuttons
533 if (scrollButtonsVisible()) {
534 m_scrollButtonLeft->setVisible(true);
535 m_scrollButtonRight->setVisible(true);
536 // scrollbuttons visible, so add their width to total width
537 totalWidth += (m_scrollButtonLeft->boundingRect().width() + m_margin) * 2;
538 scrollButtonWidth = m_scrollButtonLeft->boundingRect().width() + m_margin;
539 // start position changes by scrollbutton width
540 x += scrollButtonWidth;
541 } else {
542 m_scrollButtonLeft->setVisible(false);
543 m_scrollButtonRight->setVisible(false);
544 }
545 m_scrollButtonUp->setVisible(false);
546 m_scrollButtonDown->setVisible(false);
533 /*!
534 \internal Organizes markers horizontally.
535 Causes legend to be resized.
536 */
537 void QLegend::layoutHorizontal()
538 {
539 // Find out widest item.
540 QSizeF markerMaxSize = maximumMarkerSize();
541 // Use max height as scroll button size
542 rescaleScrollButtons(QSize(markerMaxSize.height() ,markerMaxSize.height()));
543
544 qreal totalWidth = 0;
545 qreal totalHeight = 0;
546
547 qreal xStep = markerMaxSize.width();
548 qreal x = m_pos.x() + m_margin;
549 qreal y = m_pos.y() + m_margin;
550 int column = 0;
551 int maxColumns = 1;
552 qreal scrollButtonWidth = 0;
553
554 // Set correct visibility for scroll scrollbuttons
555 if (scrollButtonsVisible()) {
556 m_scrollButtonLeft->setVisible(true);
557 m_scrollButtonRight->setVisible(true);
558 // scrollbuttons visible, so add their width to total width
559 totalWidth += (m_scrollButtonLeft->boundingRect().width() + m_margin) * 2;
560 scrollButtonWidth = m_scrollButtonLeft->boundingRect().width() + m_margin;
561 // start position changes by scrollbutton width
562 x += scrollButtonWidth;
563 } else {
564 m_scrollButtonLeft->setVisible(false);
565 m_scrollButtonRight->setVisible(false);
566 }
567 m_scrollButtonUp->setVisible(false);
568 m_scrollButtonDown->setVisible(false);
547 569
548 for (int i=0; i < m_markers.count(); i++) {
549 LegendMarker *m = m_markers.at(i);
550 if (i < mFirstMarker) {
551 // Markers before first are not visible.
570 for (int i=0; i < m_markers.count(); i++) {
571 LegendMarker *m = m_markers.at(i);
572 if (i < mFirstMarker) {
573 // Markers before first are not visible.
574 m->setVisible(false);
575 } else {
576 if ((x + xStep + scrollButtonWidth + m_margin) > (m_pos.x() + m_maximumSize.width())) {
577 // This marker would go outside legend rect.
552 578 m->setVisible(false);
553 579 } else {
554 if ((x + xStep + scrollButtonWidth + m_margin) > (m_pos.x() + m_maximumSize.width())) {
555 // This marker would go outside legend rect.
556 m->setVisible(false);
557 } else {
558 // This marker is ok
559 m->setVisible(true);
560 m->setPos(x, y);
561 x += xStep;
562 column++;
563 }
580 // This marker is ok
581 m->setVisible(true);
582 m->setPos(x, y);
583 x += xStep;
584 column++;
564 585 }
565 maxColumns = column;
566 586 }
587 maxColumns = column;
588 }
567 589
568 m_scrollButtonLeft->setPos(m_pos.x() + m_margin, y);
569 m_scrollButtonRight->setPos(x + m_margin, y);
590 m_scrollButtonLeft->setPos(m_pos.x() + m_margin, y);
591 m_scrollButtonRight->setPos(x + m_margin, y);
570 592
571 totalWidth += maxColumns * markerMaxSize.width() + m_margin * 2;
572 totalHeight = markerMaxSize.height() + m_margin * 2;
593 totalWidth += maxColumns * markerMaxSize.width() + m_margin * 2;
594 totalHeight = markerMaxSize.height() + m_margin * 2;
573 595
574 break;
596 m_size.setWidth(totalWidth);
597 m_size.setHeight(totalHeight);
598 }
599
600 /*!
601 \internal Organizes markers vertically.
602 Causes legend to be resized.
603 */
604 void QLegend::layoutVertical()
605 {
606 // Find out widest item.
607 QSizeF markerMaxSize = maximumMarkerSize();
608 // Use max height as scroll button size
609 rescaleScrollButtons(QSize(markerMaxSize.height() ,markerMaxSize.height()));
610
611 qreal totalWidth = 0;
612 qreal totalHeight = 0;
613
614 qreal yStep = markerMaxSize.height();
615 qreal x = m_pos.x() + m_margin;
616 qreal y = m_pos.y() + m_margin;
617 int row = 1;
618 int maxRows = 1;
619 qreal scrollButtonHeight = 0;
620
621 // Set correct visibility for scroll scrollbuttons
622 if (scrollButtonsVisible()) {
623 m_scrollButtonUp->setVisible(true);
624 m_scrollButtonDown->setVisible(true);
625 totalHeight += (m_scrollButtonUp->boundingRect().height() + m_margin) * 2; // scrollbuttons visible, so add their height to total height
626 scrollButtonHeight = m_scrollButtonUp->boundingRect().height();
627 y += scrollButtonHeight + m_margin; // start position changes by scrollbutton height
628 } else {
629 m_scrollButtonUp->setVisible(false);
630 m_scrollButtonDown->setVisible(false);
575 631 }
576 // Both cases organize items vertically
577 case QLegend::AlignmentLeft:
578 case QLegend::AlignmentRight: {
579 qreal yStep = markerMaxSize.height();
580 qreal x = m_pos.x() + m_margin;
581 qreal y = m_pos.y() + m_margin;
582 int row = 1;
583 int maxRows = 1;
584 qreal scrollButtonHeight = 0;
585
586 // Set correct visibility for scroll scrollbuttons
587 if (scrollButtonsVisible()) {
588 m_scrollButtonUp->setVisible(true);
589 m_scrollButtonDown->setVisible(true);
590 totalHeight += (m_scrollButtonUp->boundingRect().height() + m_margin) * 2; // scrollbuttons visible, so add their height to total height
591 scrollButtonHeight = m_scrollButtonUp->boundingRect().height();
592 y += scrollButtonHeight + m_margin; // start position changes by scrollbutton height
593 } else {
594 m_scrollButtonUp->setVisible(false);
595 m_scrollButtonDown->setVisible(false);
596 }
597 m_scrollButtonLeft->setVisible(false);
598 m_scrollButtonRight->setVisible(false);
632 m_scrollButtonLeft->setVisible(false);
633 m_scrollButtonRight->setVisible(false);
599 634
600 for (int i=0; i < m_markers.count(); i++) {
601 LegendMarker* m = m_markers.at(i);
602 if (i < mFirstMarker) {
603 // Markers before first are not visible.
635 for (int i=0; i < m_markers.count(); i++) {
636 LegendMarker* m = m_markers.at(i);
637 if (i < mFirstMarker) {
638 // Markers before first are not visible.
639 m->setVisible(false);
640 } else {
641 if ((y + yStep + scrollButtonHeight) > (m_pos.y() + m_maximumSize.height())) {
642 // This marker would go outside legend rect.
604 643 m->setVisible(false);
605 644 } else {
606 if ((y + yStep + scrollButtonHeight) > (m_pos.y() + m_maximumSize.height())) {
607 // This marker would go outside legend rect.
608 m->setVisible(false);
609 } else {
610 // This marker is ok
611 m->setVisible(true);
612 m->setPos(x, y);
613 y += yStep;
614 row++;
615 }
645 // This marker is ok
646 m->setVisible(true);
647 m->setPos(x, y);
648 y += yStep;
649 row++;
616 650 }
617 maxRows = row;
618 651 }
652 maxRows = row;
653 }
619 654
620 m_scrollButtonUp->setPos(m_pos.x() + m_margin, m_pos.y() + m_margin);
621 m_scrollButtonDown->setPos(m_pos.x() + m_margin, y + m_margin);
655 m_scrollButtonUp->setPos(m_pos.x() + m_margin, m_pos.y() + m_margin);
656 m_scrollButtonDown->setPos(m_pos.x() + m_margin, y + m_margin);
622 657
623 totalWidth += markerMaxSize.width() + m_margin * 2;
624 totalHeight = maxRows * markerMaxSize.height() + m_margin * 4 + scrollButtonHeight; // TODO: check this
625 break;
626 }
627 default: {
628 break;
629 }
630 }
658 totalWidth += markerMaxSize.width() + m_margin * 2;
659 totalHeight = maxRows * markerMaxSize.height() + m_margin * 4 + scrollButtonHeight; // TODO: check this
631 660
632 661 m_size.setWidth(totalWidth);
633 662 m_size.setHeight(totalHeight);
634
635 update();
636 663 }
637 664
638 665 /*!
639 666 \internal Sets the size of scroll buttons to \a size
640 667 */
641 668 void QLegend::rescaleScrollButtons(const QSize &size)
642 669 {
643 670 QPolygonF left;
644 671 left << QPointF(size.width(), 0) << QPointF(0, size.height() / 2) << QPointF(size.width(), size.height());
645 672 QPolygonF right;
646 673 right << QPointF(0, 0) << QPointF(size.width(), size.height() / 2) << QPointF(0, size.height());
647 674 QPolygonF up;
648 675 up << QPointF(0, size.height()) << QPointF(size.width() / 2,0) << QPointF(size.width(), size.height());
649 676 QPolygonF down;
650 677 down << QPointF(0, 0) << QPointF(size.width() / 2, size.height()) << QPointF(size.width(), 0);
651 678
652 679 m_scrollButtonLeft->setPolygon(left);
653 680 m_scrollButtonRight->setPolygon(right);
654 681 m_scrollButtonUp->setPolygon(up);
655 682 m_scrollButtonDown->setPolygon(down);
656 683 }
657 684
658 685 /*!
659 686 \internal Finds out maximum size of single marker. Marker sizes depend on series names.
660 687 */
661 688 QSizeF QLegend::maximumMarkerSize()
662 689 {
663 690 QSizeF max(0,0);
664 691 foreach (LegendMarker* m, m_markers) {
665 692 if (m->boundingRect().width() > max.width())
666 693 max.setWidth(m->boundingRect().width());
667 694 if (m->boundingRect().height() > max.height())
668 695 max.setHeight(m->boundingRect().height());
669 696 }
670 697 return max;
671 698 }
672 699
673 700 /*!
674 701 \internal Checks that first marker is in acceptable bounds. Bounds range from 0 to (maximum number of markers - visible markers)
675 702 If scrollbuttons are visible, they affect the number of visible markers.
676 703 */
677 704 void QLegend::checkFirstMarkerBounds()
678 705 {
679 706 if ((m_alignment == QLegend::AlignmentLeft) || (m_alignment == QLegend::AlignmentRight)) {
680 707 // Bounds limited by height.
681 708 int max;
682 709 if (scrollButtonsVisible()) {
683 710 max = (m_maximumSize.height() - m_scrollButtonLeft->boundingRect().height() * 2 - m_margin * 4) / maximumMarkerSize().height();
684 711 } else {
685 712 max = m_maximumSize.height() / maximumMarkerSize().height();
686 713 }
687 714
688 715 if (mFirstMarker > m_markers.count() - max)
689 716 mFirstMarker = m_markers.count() - max;
690 717 } else {
691 718 // Bounds limited by width
692 719 int max;
693 720 if (scrollButtonsVisible()) {
694 721 max = (m_maximumSize.width() - m_scrollButtonLeft->boundingRect().width() * 2 - m_margin*4) / maximumMarkerSize().width();
695 722 } else {
696 723 max = m_maximumSize.width() / maximumMarkerSize().width();
697 724 }
698 725
699 726 if (mFirstMarker > m_markers.count() - max)
700 727 mFirstMarker = m_markers.count() - max;
701 728 }
702 729
703 730 if (mFirstMarker < 0)
704 731 mFirstMarker = 0;
705 732 }
706 733
707 734 /*!
708 735 \internal Helper function. Visibility of scroll buttons isn't quite obvious, so helper function clarifies the logic.
709 736 */
710 737 bool QLegend::scrollButtonsVisible()
711 738 {
712 739 // Just a helper to clarify, what the magic below means :)
713 740 if ((m_alignment == QLegend::AlignmentTop) || (m_alignment == QLegend::AlignmentBottom)) {
714 741 return (maximumMarkerSize().width() * m_markers.count() + m_margin * 2 > m_maximumSize.width());
715 742 } else if ((m_alignment == QLegend::AlignmentLeft) || (m_alignment == QLegend::AlignmentRight)) {
716 743 return (maximumMarkerSize().height() * m_markers.count() + m_margin * 2 > m_maximumSize.height());
717 744 }
718 745
719 746 return (maximumMarkerSize().height() * m_markers.count() + m_margin * 2 > m_maximumSize.height());
720 747 }
721 748
722 749 #include "moc_qlegend.cpp"
723 750
724 751 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,145 +1,147
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2012 Digia Plc
4 4 ** All rights reserved.
5 5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 6 **
7 7 ** This file is part of the Qt Commercial Charts Add-on.
8 8 **
9 9 ** $QT_BEGIN_LICENSE$
10 10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 11 ** accordance with the Qt Commercial License Agreement provided with the
12 12 ** Software or, alternatively, in accordance with the terms contained in
13 13 ** a written agreement between you and Digia.
14 14 **
15 15 ** If you have questions regarding the use of this file, please use
16 16 ** contact form at http://qt.digia.com
17 17 ** $QT_END_LICENSE$
18 18 **
19 19 ****************************************************************************/
20 20
21 21 #ifndef QLEGEND_H
22 22 #define QLEGEND_H
23 23
24 24 #include <QChartGlobal>
25 25 #include <QGraphicsWidget>
26 26 #include <QPen>
27 27 #include <QBrush>
28 28
29 29 QTCOMMERCIALCHART_BEGIN_NAMESPACE
30 30
31 31 class Domain;
32 32 class LegendMarker;
33 33 class QPieSlice;
34 34 class QXYSeries;
35 35 class QBarSet;
36 36 class QBarSeries;
37 37 class QPieSeries;
38 38 class QAreaSeries;
39 39 class LegendScrollButton;
40 40 class QSeries;
41 41 class QChart;
42 42
43 43 class QTCOMMERCIALCHART_EXPORT QLegend : public QGraphicsWidget
44 44 {
45 45 Q_OBJECT
46 46 public:
47 47
48 48 // We only support these alignments (for now)
49 49 enum Alignment {
50 50 AlignmentTop = Qt::AlignTop,
51 51 AlignmentBottom = Qt::AlignBottom,
52 52 AlignmentLeft = Qt::AlignLeft,
53 53 AlignmentRight = Qt::AlignRight
54 54 };
55 55
56 56 Q_DECLARE_FLAGS(Alignments, Alignment)
57 57
58 58 private:
59 59 explicit QLegend(QChart *chart);
60 60
61 61 public:
62 62 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
63 63 QRectF boundingRect() const;
64 64
65 65 void setBrush(const QBrush &brush);
66 66 QBrush brush() const;
67 67
68 68 void setPen(const QPen &pen);
69 69 QPen pen() const;
70 70
71 71 void setAlignmnent(QLegend::Alignments alignment);
72 72 QLegend::Alignments alignment() const;
73 73
74 74 QSizeF maximumSize() const;
75 75 void setMaximumSize(const QSizeF size);
76 76
77 77 QSizeF size() const;
78 78 void setSize(const QSizeF size);
79 79 void setPos(const QPointF &pos);
80 80
81 81 void scrollButtonClicked(LegendScrollButton *scrollButton);
82 82
83 83 void detachFromChart();
84 84 void attachToChart();
85 85 bool attachedToChart();
86 86
87 87 Q_SIGNALS:
88 88 // for interactions.
89 89 void clicked(QSeries *series, Qt::MouseButton button);
90 90 void clicked(QBarSet *barset, Qt::MouseButton button);
91 91 void clicked(QPieSlice *slice, Qt::MouseButton button);
92 92 void legendGeometryChanged();
93 93
94 94 public Q_SLOTS:
95 95 // PIMPL --->
96 96 void handleSeriesAdded(QSeries *series, Domain *domain);
97 97 void handleSeriesRemoved(QSeries *series);
98 98 void handleAdded(QList<QPieSlice *> slices);
99 99 void handleRemoved(QList<QPieSlice *> slices);
100 100 void handleMarkerDestroyed();
101 101
102 102 // PIMPL <---
103 103
104 104 private:
105 105 // PIMPL --->
106 106 void appendMarkers(QAreaSeries *series);
107 107 void appendMarkers(QXYSeries *series);
108 108 void appendMarkers(QBarSeries *series);
109 109 void appendMarkers(QPieSeries *series);
110 110 void deleteMarkers(QSeries *series);
111 111 void updateLayout();
112 void layoutHorizontal();
113 void layoutVertical();
112 114 void rescaleScrollButtons(const QSize &size);
113 115 QSizeF maximumMarkerSize();
114 116 void checkFirstMarkerBounds();
115 117 bool scrollButtonsVisible();
116 118
117 119 qreal m_margin;
118 120 QPointF m_pos;
119 121 QSizeF m_minimumSize;
120 122 QSizeF m_maximumSize;
121 123 QSizeF m_size;
122 124
123 125 QList<LegendMarker *> m_markers;
124 126
125 127 QBrush m_brush;
126 128 QPen m_pen;
127 129 QLegend::Alignments m_alignment;
128 130
129 131 int mFirstMarker;
130 132
131 133 LegendScrollButton *m_scrollButtonLeft;
132 134 LegendScrollButton *m_scrollButtonRight;
133 135 LegendScrollButton *m_scrollButtonUp;
134 136 LegendScrollButton *m_scrollButtonDown;
135 137
136 138 bool m_attachedToChart;
137 139
138 140 QChart *m_chart;
139 141 friend class QChart;
140 142 // <--- PIMPL
141 143 };
142 144
143 145 QTCOMMERCIALCHART_END_NAMESPACE
144 146
145 147 #endif // QLEGEND_H
General Comments 0
You need to be logged in to leave comments. Login now