~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to examples/itemviews/chart/pieview.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-08-24 04:09:09 UTC
  • Revision ID: james.westby@ubuntu.com-20050824040909-xmxe9jfr4a0w5671
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 2004-2005 Trolltech AS. All rights reserved.
 
4
**
 
5
** This file is part of the example classes of the Qt Toolkit.
 
6
**
 
7
** This file may be distributed under the terms of the Q Public License
 
8
** as defined by Trolltech AS of Norway and appearing in the file
 
9
** LICENSE.QPL included in the packaging of this file.
 
10
**
 
11
** This file may be distributed and/or modified under the terms of the
 
12
** GNU General Public License version 2 as published by the Free Software
 
13
** Foundation and appearing in the file LICENSE.GPL included in the
 
14
** packaging of this file.
 
15
**
 
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
 
17
**   information about Qt Commercial License Agreements.
 
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
 
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
 
20
**
 
21
** Contact info@trolltech.com if any conditions of this licensing are
 
22
** not clear to you.
 
23
**
 
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 
26
**
 
27
****************************************************************************/
 
28
 
 
29
#include <math.h>
 
30
#include <QtGui>
 
31
 
 
32
#ifndef M_PI
 
33
#define M_PI 3.1415927
 
34
#endif
 
35
 
 
36
#include "pieview.h"
 
37
 
 
38
PieView::PieView(QWidget *parent)
 
39
    : QAbstractItemView(parent)
 
40
{
 
41
    horizontalScrollBar()->setRange(0, 0);
 
42
    verticalScrollBar()->setRange(0, 0);
 
43
 
 
44
    margin = 8;
 
45
    totalSize = 300;
 
46
    pieSize = totalSize - 2*margin;
 
47
    validItems = 0;
 
48
    totalValue = 0.0;
 
49
}
 
50
 
 
51
void PieView::dataChanged(const QModelIndex &topLeft,
 
52
                          const QModelIndex &bottomRight)
 
53
{
 
54
    QAbstractItemView::dataChanged(topLeft, bottomRight);
 
55
 
 
56
    validItems = 0;
 
57
    totalValue = 0.0;
 
58
 
 
59
    for (int row = 0; row < model()->rowCount(rootIndex()); ++row) {
 
60
 
 
61
        QModelIndex index = model()->index(row, 1, rootIndex());
 
62
        double value = model()->data(index).toDouble();
 
63
 
 
64
        if (value > 0.0) {
 
65
            totalValue += value;
 
66
            validItems++;
 
67
        }
 
68
    }
 
69
    viewport()->update();
 
70
}
 
71
 
 
72
bool PieView::edit(const QModelIndex &index, EditTrigger trigger, QEvent *event)
 
73
{
 
74
    if (index.column() == 0)
 
75
        return QAbstractItemView::edit(index, trigger, event);
 
76
    else
 
77
        return false;
 
78
}
 
79
 
 
80
/*
 
81
    Returns the item that covers the coordinate given in the view.
 
82
*/
 
83
 
 
84
QModelIndex PieView::indexAt(const QPoint &point) const
 
85
{
 
86
    if (validItems == 0)
 
87
        return QModelIndex();
 
88
 
 
89
    // Transform the view coordinates into contents widget coordinates.
 
90
    int wx = point.x() + horizontalScrollBar()->value();
 
91
    int wy = point.y() + verticalScrollBar()->value();
 
92
 
 
93
    if (wx < totalSize) {
 
94
        double cx = wx - totalSize/2;
 
95
        double cy = totalSize/2 - wy; // positive cy for items above the center
 
96
 
 
97
        // Determine the distance from the center point of the pie chart.
 
98
        double d = pow(pow(cx, 2) + pow(cy, 2), 0.5);
 
99
 
 
100
        if (d == 0 || d > pieSize/2)
 
101
            return QModelIndex();
 
102
 
 
103
        // Determine the angle of the point.
 
104
        double angle = (180 / M_PI) * acos(cx/d);
 
105
        if (cy < 0)
 
106
            angle = 360 - angle;
 
107
 
 
108
        // Find the relevant slice of the pie.
 
109
        double startAngle = 0.0;
 
110
 
 
111
        for (int row = 0; row < model()->rowCount(rootIndex()); ++row) {
 
112
 
 
113
            QModelIndex index = model()->index(row, 1, rootIndex());
 
114
            double value = model()->data(index).toDouble();
 
115
 
 
116
            if (value > 0.0) {
 
117
                double sliceAngle = 360*value/totalValue;
 
118
 
 
119
                if (angle >= startAngle && angle < (startAngle + sliceAngle))
 
120
                    return model()->index(row, 1, rootIndex());
 
121
 
 
122
                startAngle += sliceAngle;
 
123
            }
 
124
        }
 
125
    } else {
 
126
        double itemHeight = QFontMetrics(viewOptions().font).height();
 
127
        int listItem = int((wy - margin) / itemHeight);
 
128
        int validRow = 0;
 
129
 
 
130
        for (int row = 0; row < model()->rowCount(rootIndex()); ++row) {
 
131
 
 
132
            QModelIndex index = model()->index(row, 1, rootIndex());
 
133
            if (model()->data(index).toDouble() > 0.0) {
 
134
 
 
135
                if (listItem == validRow)
 
136
                    return model()->index(row, 0, rootIndex());
 
137
 
 
138
                // Update the list index that corresponds to the next valid row.
 
139
                validRow++;
 
140
            }
 
141
        }
 
142
    }
 
143
 
 
144
    return QModelIndex();
 
145
}
 
146
 
 
147
bool PieView::isIndexHidden(const QModelIndex & /*index*/) const
 
148
{
 
149
    return false;
 
150
}
 
151
 
 
152
/*
 
153
    Returns the rectangle of the item at position \a index in the
 
154
    model. The rectangle is in contents coordinates.
 
155
*/
 
156
 
 
157
QRect PieView::itemRect(const QModelIndex &index) const
 
158
{
 
159
    if (!index.isValid())
 
160
        return QRect();
 
161
 
 
162
    // Check whether the index's row is in the list of rows represented
 
163
    // by slices.
 
164
    QModelIndex valueIndex;
 
165
 
 
166
    if (index.column() != 1)
 
167
        valueIndex = model()->index(index.row(), 1, rootIndex());
 
168
    else
 
169
        valueIndex = index;
 
170
 
 
171
    if (model()->data(valueIndex).toDouble() > 0.0) {
 
172
 
 
173
        int listItem = 0;
 
174
        for (int row = index.row()-1; row >= 0; --row) {
 
175
            if (model()->data(model()->index(row, 1, rootIndex())).toDouble() > 0.0)
 
176
                listItem++;
 
177
        }
 
178
 
 
179
        double itemHeight;
 
180
 
 
181
        switch (index.column()) {
 
182
        case 0:
 
183
            itemHeight = QFontMetrics(viewOptions().font).height();
 
184
 
 
185
            return QRect(totalSize,
 
186
                         int(margin + listItem*itemHeight),
 
187
                         totalSize - margin, int(itemHeight));
 
188
        case 1:
 
189
            return viewport()->rect();
 
190
        }
 
191
 
 
192
    }
 
193
    return QRect();
 
194
}
 
195
 
 
196
QRegion PieView::itemRegion(const QModelIndex &index) const
 
197
{
 
198
    if (!index.isValid())
 
199
        return QRegion();
 
200
 
 
201
    if (index.column() != 1)
 
202
        return itemRect(index);
 
203
 
 
204
    if (model()->data(index).toDouble() <= 0.0)
 
205
        return QRegion();
 
206
 
 
207
    double startAngle = 0.0;
 
208
    for (int row = 0; row < model()->rowCount(rootIndex()); ++row) {
 
209
 
 
210
        QModelIndex sliceIndex = model()->index(row, 1, rootIndex());
 
211
        double value = model()->data(sliceIndex).toDouble();
 
212
 
 
213
        if (value > 0.0) {
 
214
            double angle = 360*value/totalValue;
 
215
 
 
216
            if (sliceIndex == index) {
 
217
                QPainterPath slicePath;
 
218
                slicePath.moveTo(totalSize/2, totalSize/2);
 
219
                slicePath.arcTo(margin, margin, margin+pieSize, margin+pieSize,
 
220
                                startAngle, angle);
 
221
                slicePath.closeSubpath();
 
222
 
 
223
                return QRegion(slicePath.toFillPolygon().toPolygon());
 
224
            }
 
225
 
 
226
            startAngle += angle;
 
227
        }
 
228
    }
 
229
 
 
230
    return QRegion();
 
231
}
 
232
 
 
233
int PieView::horizontalOffset() const
 
234
{
 
235
    return horizontalScrollBar()->value();
 
236
}
 
237
 
 
238
void PieView::mouseReleaseEvent(QMouseEvent *event)
 
239
{
 
240
    QAbstractItemView::mouseReleaseEvent(event);
 
241
    selectionRect = QRect();
 
242
    viewport()->update();
 
243
}
 
244
 
 
245
QModelIndex PieView::moveCursor(QAbstractItemView::CursorAction cursorAction,
 
246
                                Qt::KeyboardModifiers /*modifiers*/)
 
247
{
 
248
    QModelIndex current = currentIndex();
 
249
 
 
250
    switch (cursorAction) {
 
251
        case MoveLeft:
 
252
        case MoveUp:
 
253
            if (current.row() > 0)
 
254
                return model()->index(current.row() - 1, current.column(),
 
255
                                      rootIndex());
 
256
            else
 
257
                return model()->index(0, current.column(), rootIndex());
 
258
            break;
 
259
        case MoveRight:
 
260
        case MoveDown:
 
261
            if (current.row() < rows(current) - 1)
 
262
                return model()->index(current.row() + 1, current.column(),
 
263
                                      rootIndex());
 
264
            else
 
265
                return model()->index(rows(current) - 1, current.column(),
 
266
                                      rootIndex());
 
267
            break;
 
268
        default:
 
269
            return current;
 
270
    }
 
271
 
 
272
    viewport()->update();
 
273
}
 
274
 
 
275
void PieView::paintEvent(QPaintEvent *event)
 
276
{
 
277
    QItemSelectionModel *selections = selectionModel();
 
278
    QStyleOptionViewItem option = viewOptions();
 
279
    QStyle::State state = option.state;
 
280
 
 
281
    QBrush background = option.palette.base();
 
282
    QPen foreground(option.palette.color(QPalette::Foreground));
 
283
    QPen textPen(option.palette.color(QPalette::Text));
 
284
    QPen highlightedPen(option.palette.color(QPalette::HighlightedText));
 
285
 
 
286
    QPainter painter(viewport());
 
287
    painter.setRenderHint(QPainter::Antialiasing);
 
288
 
 
289
    painter.fillRect(event->rect(), background);
 
290
    painter.setPen(foreground);
 
291
 
 
292
    // Viewport rectangles
 
293
    QRect pieRect = QRect(margin, margin, pieSize, pieSize);
 
294
    QPoint keyPoint = QPoint(totalSize - horizontalScrollBar()->value(),
 
295
                             margin - verticalScrollBar()->value());
 
296
 
 
297
    if (validItems > 0) {
 
298
 
 
299
        painter.save();
 
300
        painter.translate(pieRect.x() - horizontalScrollBar()->value(),
 
301
                          pieRect.y() - verticalScrollBar()->value());
 
302
        painter.drawEllipse(0, 0, pieSize, pieSize);
 
303
        double startAngle = 0.0;
 
304
        int row;
 
305
 
 
306
        for (row = 0; row < model()->rowCount(rootIndex()); ++row) {
 
307
 
 
308
            QModelIndex index = model()->index(row, 1, rootIndex());
 
309
            double value = model()->data(index).toDouble();
 
310
 
 
311
            if (value > 0.0) {
 
312
                double angle = 360*value/totalValue;
 
313
 
 
314
                QModelIndex colorIndex = model()->index(row, 0, rootIndex());
 
315
                QColor color = QColor(model()->data(colorIndex,
 
316
                                Qt::DecorationRole).toString());
 
317
 
 
318
                if (currentIndex() == index)
 
319
                    painter.setBrush(QBrush(color, Qt::Dense4Pattern));
 
320
                else if (selections->isSelected(index))
 
321
                    painter.setBrush(QBrush(color, Qt::Dense3Pattern));
 
322
                else
 
323
                    painter.setBrush(QBrush(color));
 
324
 
 
325
                painter.drawPie(0, 0, pieSize, pieSize, int(startAngle*16),
 
326
                                int(angle*16));
 
327
 
 
328
                startAngle += angle;
 
329
            }
 
330
        }
 
331
        painter.restore();
 
332
 
 
333
        int keyNumber = 0;
 
334
 
 
335
        for (row = 0; row < model()->rowCount(rootIndex()); ++row) {
 
336
 
 
337
            QModelIndex index = model()->index(row, 1, rootIndex());
 
338
            double value = model()->data(index).toDouble();
 
339
 
 
340
            if (value > 0.0) {
 
341
                QModelIndex labelIndex = model()->index(row, 0, rootIndex());
 
342
 
 
343
                QStyleOptionViewItem option = viewOptions();
 
344
                option.rect = visualRect(labelIndex);
 
345
                if (selections->isSelected(labelIndex))
 
346
                    option.state |= QStyle::State_Selected;
 
347
                if (currentIndex() == labelIndex)
 
348
                    option.state |= QStyle::State_HasFocus;
 
349
                itemDelegate()->paint(&painter, option, labelIndex);
 
350
 
 
351
                keyNumber++;
 
352
            }
 
353
        }
 
354
    }
 
355
 
 
356
    if (!selectionRect.isEmpty()) {
 
357
        QStyleOptionRubberBand band;
 
358
        band.shape = QRubberBand::Rectangle;
 
359
        band.rect = selectionRect;
 
360
        painter.save();
 
361
        style()->drawControl(QStyle::CE_RubberBand, &band, &painter);
 
362
        painter.restore();
 
363
    }
 
364
}
 
365
 
 
366
void PieView::resizeEvent(QResizeEvent * /* event */)
 
367
{
 
368
    updateGeometries();
 
369
}
 
370
 
 
371
int PieView::rows(const QModelIndex &index) const
 
372
{
 
373
    return model()->rowCount(model()->parent(index));
 
374
}
 
375
 
 
376
void PieView::rowsInserted(const QModelIndex &parent, int start, int end)
 
377
{
 
378
    for (int row = start; row <= end; ++row) {
 
379
 
 
380
        QModelIndex index = model()->index(row, 1, rootIndex());
 
381
        double value = model()->data(index).toDouble();
 
382
 
 
383
        if (value > 0.0) {
 
384
            totalValue += value;
 
385
            validItems++;
 
386
        }
 
387
    }
 
388
 
 
389
    QAbstractItemView::rowsInserted(parent, start, end);
 
390
}
 
391
 
 
392
void PieView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
 
393
{
 
394
    for (int row = start; row <= end; ++row) {
 
395
 
 
396
        QModelIndex index = model()->index(row, 1, rootIndex());
 
397
        double value = model()->data(index).toDouble();
 
398
        if (value > 0.0) {
 
399
            totalValue -= value;
 
400
            validItems--;
 
401
        }
 
402
    }
 
403
 
 
404
    QAbstractItemView::rowsAboutToBeRemoved(parent, start, end);
 
405
}
 
406
 
 
407
void PieView::scrollContentsBy(int dx, int dy)
 
408
{
 
409
    viewport()->scroll(dx, dy);
 
410
}
 
411
 
 
412
void PieView::scrollTo(const QModelIndex &index, ScrollHint)
 
413
{
 
414
    QRect area = viewport()->rect();
 
415
    QRect rect = visualRect(index);
 
416
 
 
417
    if (rect.left() < area.left())
 
418
        horizontalScrollBar()->setValue(
 
419
            horizontalScrollBar()->value() + rect.left() - area.left());
 
420
    else if (rect.right() > area.right())
 
421
        horizontalScrollBar()->setValue(
 
422
            horizontalScrollBar()->value() + qMin(
 
423
                rect.right() - area.right(), rect.left() - area.left()));
 
424
 
 
425
    if (rect.top() < area.top())
 
426
        verticalScrollBar()->setValue(
 
427
            verticalScrollBar()->value() + rect.top() - area.top());
 
428
    else if (rect.bottom() > area.bottom())
 
429
        verticalScrollBar()->setValue(
 
430
            verticalScrollBar()->value() + qMin(
 
431
                rect.bottom() - area.bottom(), rect.top() - area.top()));
 
432
}
 
433
 
 
434
/*
 
435
    Find the indices corresponding to the extent of the selection.
 
436
*/
 
437
 
 
438
void PieView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
 
439
{
 
440
    // Use content widget coordinates because we will use the itemRegion()
 
441
    // function to check for intersections.
 
442
 
 
443
    QRect contentsRect = rect.translated(horizontalScrollBar()->value(),
 
444
                                         verticalScrollBar()->value());
 
445
 
 
446
    int rows = model()->rowCount(rootIndex());
 
447
    int columns = model()->columnCount(rootIndex());
 
448
    QModelIndexList indexes;
 
449
 
 
450
    for (int row = 0; row < rows; ++row) {
 
451
        for (int column = 0; column < columns; ++column) {
 
452
            QModelIndex index = model()->index(row, column, rootIndex());
 
453
            QRegion region = itemRegion(index);
 
454
            if (!region.intersect(contentsRect).isEmpty())
 
455
                indexes.append(index);
 
456
        }
 
457
    }
 
458
 
 
459
    if (indexes.size() > 0) {
 
460
        int firstRow = indexes[0].row();
 
461
        int lastRow = indexes[0].row();
 
462
        int firstColumn = indexes[0].column();
 
463
        int lastColumn = indexes[0].column();
 
464
 
 
465
        for (int i = 1; i < indexes.size(); ++i) {
 
466
            firstRow = qMin(firstRow, indexes[i].row());
 
467
            lastRow = qMax(lastRow, indexes[i].row());
 
468
            firstColumn = qMin(firstColumn, indexes[i].column());
 
469
            lastColumn = qMax(lastColumn, indexes[i].column());
 
470
        }
 
471
 
 
472
        QItemSelection selection(
 
473
            model()->index(firstRow, firstColumn, rootIndex()),
 
474
            model()->index(lastRow, lastColumn, rootIndex()));
 
475
        selectionModel()->select(selection, command);
 
476
    } else {
 
477
        QModelIndex noIndex;
 
478
        QItemSelection selection(noIndex, noIndex);
 
479
        selectionModel()->select(selection, command);
 
480
    }
 
481
 
 
482
    selectionRect = rect;
 
483
    viewport()->update();
 
484
}
 
485
 
 
486
void PieView::updateGeometries()
 
487
{
 
488
    horizontalScrollBar()->setPageStep(viewport()->width());
 
489
    horizontalScrollBar()->setRange(0, qMax(0, 2*totalSize - viewport()->width()));
 
490
    verticalScrollBar()->setPageStep(viewport()->height());
 
491
    verticalScrollBar()->setRange(0, qMax(0, totalSize - viewport()->height()));
 
492
}
 
493
 
 
494
int PieView::verticalOffset() const
 
495
{
 
496
    return verticalScrollBar()->value();
 
497
}
 
498
 
 
499
/*
 
500
    Returns the position of the item in viewport coordinates.
 
501
*/
 
502
 
 
503
QRect PieView::visualRect(const QModelIndex &index) const
 
504
{
 
505
    QRect rect = itemRect(index);
 
506
    if (rect.isValid())
 
507
        return QRect(rect.left() - horizontalScrollBar()->value(),
 
508
                     rect.top() - verticalScrollBar()->value(),
 
509
                     rect.width(), rect.height());
 
510
    else
 
511
        return rect;
 
512
}
 
513
 
 
514
/*
 
515
    Returns a region corresponding to the selection in viewport coordinates.
 
516
*/
 
517
 
 
518
QRegion PieView::visualRegionForSelection(const QItemSelection &selection) const
 
519
{
 
520
    int ranges = selection.count();
 
521
 
 
522
    if (ranges == 0)
 
523
        return QRect();
 
524
 
 
525
    // Note that we use the top and bottom functions of the selection range
 
526
    // since the data is stored in rows.
 
527
 
 
528
    int firstRow = selection.at(0).top();
 
529
    int lastRow = selection.at(0).top();
 
530
 
 
531
    for (int i = 0; i < ranges; ++i) {
 
532
        firstRow = qMin(firstRow, selection.at(i).top());
 
533
        lastRow = qMax(lastRow, selection.at(i).bottom());
 
534
    }
 
535
 
 
536
    QModelIndex firstItem = model()->index(qMin(firstRow, lastRow), 0, rootIndex());
 
537
    QModelIndex lastItem = model()->index(qMax(firstRow, lastRow), 0, rootIndex());
 
538
 
 
539
    QRect firstRect = visualRect(firstItem);
 
540
    QRect lastRect = visualRect(lastItem);
 
541
 
 
542
    return firstRect.unite(lastRect);
 
543
}