1
/****************************************************************************
3
** Copyright (C) 2004-2005 Trolltech AS. All rights reserved.
5
** This file is part of the example classes of the Qt Toolkit.
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.
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.
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.
21
** Contact info@trolltech.com if any conditions of this licensing are
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.
27
****************************************************************************/
33
#define M_PI 3.1415927
38
PieView::PieView(QWidget *parent)
39
: QAbstractItemView(parent)
41
horizontalScrollBar()->setRange(0, 0);
42
verticalScrollBar()->setRange(0, 0);
46
pieSize = totalSize - 2*margin;
51
void PieView::dataChanged(const QModelIndex &topLeft,
52
const QModelIndex &bottomRight)
54
QAbstractItemView::dataChanged(topLeft, bottomRight);
59
for (int row = 0; row < model()->rowCount(rootIndex()); ++row) {
61
QModelIndex index = model()->index(row, 1, rootIndex());
62
double value = model()->data(index).toDouble();
72
bool PieView::edit(const QModelIndex &index, EditTrigger trigger, QEvent *event)
74
if (index.column() == 0)
75
return QAbstractItemView::edit(index, trigger, event);
81
Returns the item that covers the coordinate given in the view.
84
QModelIndex PieView::indexAt(const QPoint &point) const
89
// Transform the view coordinates into contents widget coordinates.
90
int wx = point.x() + horizontalScrollBar()->value();
91
int wy = point.y() + verticalScrollBar()->value();
94
double cx = wx - totalSize/2;
95
double cy = totalSize/2 - wy; // positive cy for items above the center
97
// Determine the distance from the center point of the pie chart.
98
double d = pow(pow(cx, 2) + pow(cy, 2), 0.5);
100
if (d == 0 || d > pieSize/2)
101
return QModelIndex();
103
// Determine the angle of the point.
104
double angle = (180 / M_PI) * acos(cx/d);
108
// Find the relevant slice of the pie.
109
double startAngle = 0.0;
111
for (int row = 0; row < model()->rowCount(rootIndex()); ++row) {
113
QModelIndex index = model()->index(row, 1, rootIndex());
114
double value = model()->data(index).toDouble();
117
double sliceAngle = 360*value/totalValue;
119
if (angle >= startAngle && angle < (startAngle + sliceAngle))
120
return model()->index(row, 1, rootIndex());
122
startAngle += sliceAngle;
126
double itemHeight = QFontMetrics(viewOptions().font).height();
127
int listItem = int((wy - margin) / itemHeight);
130
for (int row = 0; row < model()->rowCount(rootIndex()); ++row) {
132
QModelIndex index = model()->index(row, 1, rootIndex());
133
if (model()->data(index).toDouble() > 0.0) {
135
if (listItem == validRow)
136
return model()->index(row, 0, rootIndex());
138
// Update the list index that corresponds to the next valid row.
144
return QModelIndex();
147
bool PieView::isIndexHidden(const QModelIndex & /*index*/) const
153
Returns the rectangle of the item at position \a index in the
154
model. The rectangle is in contents coordinates.
157
QRect PieView::itemRect(const QModelIndex &index) const
159
if (!index.isValid())
162
// Check whether the index's row is in the list of rows represented
164
QModelIndex valueIndex;
166
if (index.column() != 1)
167
valueIndex = model()->index(index.row(), 1, rootIndex());
171
if (model()->data(valueIndex).toDouble() > 0.0) {
174
for (int row = index.row()-1; row >= 0; --row) {
175
if (model()->data(model()->index(row, 1, rootIndex())).toDouble() > 0.0)
181
switch (index.column()) {
183
itemHeight = QFontMetrics(viewOptions().font).height();
185
return QRect(totalSize,
186
int(margin + listItem*itemHeight),
187
totalSize - margin, int(itemHeight));
189
return viewport()->rect();
196
QRegion PieView::itemRegion(const QModelIndex &index) const
198
if (!index.isValid())
201
if (index.column() != 1)
202
return itemRect(index);
204
if (model()->data(index).toDouble() <= 0.0)
207
double startAngle = 0.0;
208
for (int row = 0; row < model()->rowCount(rootIndex()); ++row) {
210
QModelIndex sliceIndex = model()->index(row, 1, rootIndex());
211
double value = model()->data(sliceIndex).toDouble();
214
double angle = 360*value/totalValue;
216
if (sliceIndex == index) {
217
QPainterPath slicePath;
218
slicePath.moveTo(totalSize/2, totalSize/2);
219
slicePath.arcTo(margin, margin, margin+pieSize, margin+pieSize,
221
slicePath.closeSubpath();
223
return QRegion(slicePath.toFillPolygon().toPolygon());
233
int PieView::horizontalOffset() const
235
return horizontalScrollBar()->value();
238
void PieView::mouseReleaseEvent(QMouseEvent *event)
240
QAbstractItemView::mouseReleaseEvent(event);
241
selectionRect = QRect();
242
viewport()->update();
245
QModelIndex PieView::moveCursor(QAbstractItemView::CursorAction cursorAction,
246
Qt::KeyboardModifiers /*modifiers*/)
248
QModelIndex current = currentIndex();
250
switch (cursorAction) {
253
if (current.row() > 0)
254
return model()->index(current.row() - 1, current.column(),
257
return model()->index(0, current.column(), rootIndex());
261
if (current.row() < rows(current) - 1)
262
return model()->index(current.row() + 1, current.column(),
265
return model()->index(rows(current) - 1, current.column(),
272
viewport()->update();
275
void PieView::paintEvent(QPaintEvent *event)
277
QItemSelectionModel *selections = selectionModel();
278
QStyleOptionViewItem option = viewOptions();
279
QStyle::State state = option.state;
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));
286
QPainter painter(viewport());
287
painter.setRenderHint(QPainter::Antialiasing);
289
painter.fillRect(event->rect(), background);
290
painter.setPen(foreground);
292
// Viewport rectangles
293
QRect pieRect = QRect(margin, margin, pieSize, pieSize);
294
QPoint keyPoint = QPoint(totalSize - horizontalScrollBar()->value(),
295
margin - verticalScrollBar()->value());
297
if (validItems > 0) {
300
painter.translate(pieRect.x() - horizontalScrollBar()->value(),
301
pieRect.y() - verticalScrollBar()->value());
302
painter.drawEllipse(0, 0, pieSize, pieSize);
303
double startAngle = 0.0;
306
for (row = 0; row < model()->rowCount(rootIndex()); ++row) {
308
QModelIndex index = model()->index(row, 1, rootIndex());
309
double value = model()->data(index).toDouble();
312
double angle = 360*value/totalValue;
314
QModelIndex colorIndex = model()->index(row, 0, rootIndex());
315
QColor color = QColor(model()->data(colorIndex,
316
Qt::DecorationRole).toString());
318
if (currentIndex() == index)
319
painter.setBrush(QBrush(color, Qt::Dense4Pattern));
320
else if (selections->isSelected(index))
321
painter.setBrush(QBrush(color, Qt::Dense3Pattern));
323
painter.setBrush(QBrush(color));
325
painter.drawPie(0, 0, pieSize, pieSize, int(startAngle*16),
335
for (row = 0; row < model()->rowCount(rootIndex()); ++row) {
337
QModelIndex index = model()->index(row, 1, rootIndex());
338
double value = model()->data(index).toDouble();
341
QModelIndex labelIndex = model()->index(row, 0, rootIndex());
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);
356
if (!selectionRect.isEmpty()) {
357
QStyleOptionRubberBand band;
358
band.shape = QRubberBand::Rectangle;
359
band.rect = selectionRect;
361
style()->drawControl(QStyle::CE_RubberBand, &band, &painter);
366
void PieView::resizeEvent(QResizeEvent * /* event */)
371
int PieView::rows(const QModelIndex &index) const
373
return model()->rowCount(model()->parent(index));
376
void PieView::rowsInserted(const QModelIndex &parent, int start, int end)
378
for (int row = start; row <= end; ++row) {
380
QModelIndex index = model()->index(row, 1, rootIndex());
381
double value = model()->data(index).toDouble();
389
QAbstractItemView::rowsInserted(parent, start, end);
392
void PieView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
394
for (int row = start; row <= end; ++row) {
396
QModelIndex index = model()->index(row, 1, rootIndex());
397
double value = model()->data(index).toDouble();
404
QAbstractItemView::rowsAboutToBeRemoved(parent, start, end);
407
void PieView::scrollContentsBy(int dx, int dy)
409
viewport()->scroll(dx, dy);
412
void PieView::scrollTo(const QModelIndex &index, ScrollHint)
414
QRect area = viewport()->rect();
415
QRect rect = visualRect(index);
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()));
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()));
435
Find the indices corresponding to the extent of the selection.
438
void PieView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
440
// Use content widget coordinates because we will use the itemRegion()
441
// function to check for intersections.
443
QRect contentsRect = rect.translated(horizontalScrollBar()->value(),
444
verticalScrollBar()->value());
446
int rows = model()->rowCount(rootIndex());
447
int columns = model()->columnCount(rootIndex());
448
QModelIndexList indexes;
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);
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();
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());
472
QItemSelection selection(
473
model()->index(firstRow, firstColumn, rootIndex()),
474
model()->index(lastRow, lastColumn, rootIndex()));
475
selectionModel()->select(selection, command);
478
QItemSelection selection(noIndex, noIndex);
479
selectionModel()->select(selection, command);
482
selectionRect = rect;
483
viewport()->update();
486
void PieView::updateGeometries()
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()));
494
int PieView::verticalOffset() const
496
return verticalScrollBar()->value();
500
Returns the position of the item in viewport coordinates.
503
QRect PieView::visualRect(const QModelIndex &index) const
505
QRect rect = itemRect(index);
507
return QRect(rect.left() - horizontalScrollBar()->value(),
508
rect.top() - verticalScrollBar()->value(),
509
rect.width(), rect.height());
515
Returns a region corresponding to the selection in viewport coordinates.
518
QRegion PieView::visualRegionForSelection(const QItemSelection &selection) const
520
int ranges = selection.count();
525
// Note that we use the top and bottom functions of the selection range
526
// since the data is stored in rows.
528
int firstRow = selection.at(0).top();
529
int lastRow = selection.at(0).top();
531
for (int i = 0; i < ranges; ++i) {
532
firstRow = qMin(firstRow, selection.at(i).top());
533
lastRow = qMax(lastRow, selection.at(i).bottom());
536
QModelIndex firstItem = model()->index(qMin(firstRow, lastRow), 0, rootIndex());
537
QModelIndex lastItem = model()->index(qMax(firstRow, lastRow), 0, rootIndex());
539
QRect firstRect = visualRect(firstItem);
540
QRect lastRect = visualRect(lastItem);
542
return firstRect.unite(lastRect);