From ea054b48ad070f46c5236c3caa4cd0b989ee0948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Soutad=C3=A9?= Date: Wed, 21 Sep 2011 20:47:54 +0200 Subject: [PATCH] Add pieview --- src/view/AccountPanel.cpp | 56 ++-- src/view/AccountPanel.hpp | 11 +- src/view/pieview.cpp | 565 ++++++++++++++++++++++++++++++++++++++ src/view/pieview.hpp | 110 ++++++++ 4 files changed, 714 insertions(+), 28 deletions(-) create mode 100644 src/view/pieview.cpp create mode 100644 src/view/pieview.hpp diff --git a/src/view/AccountPanel.cpp b/src/view/AccountPanel.cpp index 970dc74..c10866f 100644 --- a/src/view/AccountPanel.cpp +++ b/src/view/AccountPanel.cpp @@ -52,7 +52,7 @@ AccountPanel::AccountPanel(KissCount* kiss, wxUI *parent) : KissPanel(kiss, pare _tree->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); // ColorScheme* colorScheme = new ColorScheme(wxUI::categoryColors, WXSIZEOF(wxUI::categoryColors)); - // _pie = new PiePlot(); + _pie = new PieView; _calendar = new QCalendarWidget(this); _calendar->setGridVisible(false); _calendar->setFirstDayOfWeek(Qt::Monday); @@ -78,16 +78,23 @@ AccountPanel::AccountPanel(KissCount* kiss, wxUI *parent) : KissPanel(kiss, pare nbCategories = (user->GetCategoriesNumber() <= wxUI::MAX_CATEGORY) ? user->GetCategoriesNumber() : wxUI::MAX_CATEGORY; + _dataset = new QStandardItemModel(user->GetCategoriesNumber(), 2, this); _categoriesValues = new double[user->GetCategoriesNumber()]; for(i=0; iGetCategoriesNumber(); i++) + { _categoriesValues[i] = 0.0; - + _dataset->setData(_dataset->index(i, 0, QModelIndex()), _categories[i]); + _dataset->setData(_dataset->index(i, 1, QModelIndex()), 0.0); + _dataset->setData(_dataset->index(i, 0, QModelIndex()), wxUI::categoryColors[i], Qt::DecorationRole); + } // _dataset = new CategorySimpleDataset(_categories, nbCategories); // _dataset->AddSerie(_("Serie 1"), _categoriesValues, nbCategories); // _dataset->SetRenderer(new CategoryRenderer(*colorScheme)); // _pie->SetDataset(_dataset); // _pie->SetColorScheme(colorScheme); + _pie->setModel(_dataset); + _pie->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); // _pie->SetLegend(new Legend(wxBOTTOM, wxCENTER)); @@ -117,7 +124,7 @@ AccountPanel::AccountPanel(KissCount* kiss, wxUI *parent) : KissPanel(kiss, pare _statsGrid = new QTableWidget(this); _statsGrid->verticalHeader()->setHidden(true); _statsGrid->horizontalHeader()->setHidden(true); - _statsGrid->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + _statsGrid->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); // chart = new wxChartPanel(this); // chart->SetChart(new Chart(_pie, _("Cost repartition"))); @@ -143,9 +150,9 @@ AccountPanel::AccountPanel(KissCount* kiss, wxUI *parent) : KissPanel(kiss, pare groupBox->setLayout(vbox); } - // wxString modes[3] = {_("Virtual"), _("Real"), _("Check")}; - // _radioMode = new wxRadioBox(this, DISPLAY_MODE_ID, _("Mode"), wxDefaultPosition, wxDefaultSize, - // 3, modes); + connect(_virtual, SIGNAL(clicked()), this, SLOT(OnModeChange())); + connect(_real, SIGNAL(clicked()), this, SLOT(OnModeChange())); + connect(_check, SIGNAL(clicked()), this, SLOT(OnModeChange())); // _tree.SetIndent(5); @@ -167,7 +174,7 @@ AccountPanel::AccountPanel(KissCount* kiss, wxUI *parent) : KissPanel(kiss, pare // vbox2->Add(_grid, 0, wxGROW|wxALL, 2); hbox->addLayout(vbox2); vbox->addWidget(_statsGrid); - // vbox->Add(chart, 0, wxALIGN_CENTER_HORIZONTAL|wxUP, 10); + vbox->addWidget(_pie); hbox->addLayout(vbox); ChangeUser(); @@ -212,7 +219,7 @@ void AccountPanel::InitStatsGrid(User* user) { int i; int nb_categories = user->GetCategoriesNumber(); - DEFAULT_FONT(font); + QFont font; if (!_statsGrid->rowCount()) { @@ -229,6 +236,7 @@ void AccountPanel::InitStatsGrid(User* user) _statsGrid->setItem(TOTAL_CREDIT, 0, new QTableWidgetItem(_("Total Credit"))); _statsGrid->setItem(TOTAL_DEBIT, 0, new QTableWidgetItem(_("Total Debit"))); + font = _statsGrid->item(TOTAL_DEBIT, 0)->font(); for(i=0; isetItem(CATS_STATS+i+1, 0, new QTableWidgetItem(_categories[i])); _statsGrid->setItem(CATS_STATS+i+1, 1, new QTableWidgetItem("")); - _statsGrid->item(CATS_STATS+i+1, 1)->setTextAlignment(Qt::AlignRight); + _statsGrid->item(CATS_STATS+i+1, 1)->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); } else { _statsGrid->setItem(CATS_STATS+i, 0, new QTableWidgetItem(_categories[i])); _statsGrid->setItem(CATS_STATS+i, 1, new QTableWidgetItem("")); - _statsGrid->item(CATS_STATS+i, 1)->setTextAlignment(Qt::AlignRight); + _statsGrid->item(CATS_STATS+i, 1)->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); } } @@ -267,12 +275,12 @@ void AccountPanel::InitStatsGrid(User* user) _statsGrid->item(BALANCE, 0)->setFont(font); _statsGrid->item(BALANCE, 1)->setFont(font); - _statsGrid->item(CUR_CREDIT, 1)->setTextAlignment(Qt::AlignRight); - _statsGrid->item(CUR_DEBIT, 1)->setTextAlignment(Qt::AlignRight); - _statsGrid->item(TOTAL_CREDIT, 1)->setTextAlignment(Qt::AlignRight); - _statsGrid->item(TOTAL_DEBIT, 1)->setTextAlignment(Qt::AlignRight); - _statsGrid->item(BALANCE, 1)->setTextAlignment(Qt::AlignRight); - _statsGrid->item(NON_FIX, 1)->setTextAlignment(Qt::AlignRight); + _statsGrid->item(CUR_CREDIT, 1)->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + _statsGrid->item(CUR_DEBIT, 1)->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + _statsGrid->item(TOTAL_CREDIT, 1)->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + _statsGrid->item(TOTAL_DEBIT, 1)->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + _statsGrid->item(BALANCE, 1)->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + _statsGrid->item(NON_FIX, 1)->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); _accountsGrid->resizeColumnToContents(0); } @@ -470,9 +478,9 @@ void AccountPanel::InitAccountsGrid(User* user, int month, int year) } } _accountsInitValues[it->id] = value; - _accountsGrid->item(curLine, ACCOUNT_INIT)->setTextAlignment(Qt::AlignRight); - _accountsGrid->item(curLine, ACCOUNT_CUR)->setTextAlignment(Qt::AlignRight); - _accountsGrid->item(curLine, ACCOUNT_FINAL)->setTextAlignment(Qt::AlignRight); + _accountsGrid->item(curLine, ACCOUNT_INIT)->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + _accountsGrid->item(curLine, ACCOUNT_CUR)->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + _accountsGrid->item(curLine, ACCOUNT_FINAL)->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); } _accountsGrid->resizeColumnsToContents(); @@ -668,8 +676,8 @@ void AccountPanel::UpdateStats() _statsGrid->item(TOTAL_CREDIT, 1)->setText(v.sprintf("%.2lf", totalCredit)); _statsGrid->item(TOTAL_DEBIT, 1)->setText(v.sprintf("%.2lf", totalDebit)); - // _statsGrid->SetCellTextColour(BALANCE, 1, (balance >= 0) ? wxColor(0x00, 0xFF, 0x00) : wxColor(0xFF, 0x00, 0x00)); _statsGrid->item(BALANCE, 1)->setText(v.sprintf("%.2lf", balance)); + _statsGrid->item(BALANCE, 1)->setForeground((balance >= 0) ? QBrush(Qt::green) : QBrush(Qt::red)); for(i=0; iGetCategoriesNumber(); i++) { @@ -681,6 +689,8 @@ void AccountPanel::UpdateStats() _statsGrid->item(CATS_STATS+i+1, 1)->setText(v.sprintf("%.2lf (%02d %%)", _categoriesValues[i], (int)percents)); else _statsGrid->item(CATS_STATS+i, 1)->setText(v.sprintf("%.2lf (%02d %%)", _categoriesValues[i], (int)percents)); + + _dataset->setData(_dataset->index(i, 1, QModelIndex()), _categoriesValues[i]); } value = totalDebit - _categoriesValues[0]; @@ -700,7 +710,7 @@ void AccountPanel::UpdateStats() _accountsGrid->item(i, ACCOUNT_INIT)->setText(v.sprintf("%.2lf", value)); value = curAccountAmount[accountIt->id]; _accountsGrid->item(i, ACCOUNT_CUR)->setText(v.sprintf("%.2lf", value)); - // _accountsGrid->SetCellTextColour(i, ACCOUNT_CUR, (value >= 0.0) ? wxColor(0x00, 0x00, 0x00) : wxColor(0xFF, 0x00, 0x00)); + _accountsGrid->item(i, ACCOUNT_CUR)->setForeground((value >= 0) ? QBrush(Qt::black) : QBrush(Qt::red)); value = finalAccountAmount[accountIt->id]; _accountsGrid->item(i, ACCOUNT_FINAL)->setText(v.sprintf("%.2lf", value)); } @@ -712,7 +722,7 @@ void AccountPanel::UpdateStats() _accountsGrid->item(i, ACCOUNT_INIT)->setText(v.sprintf("%.2lf (%.2lf)", value, value-value2)); value = curAccountAmount[accountIt->id]; _accountsGrid->item(i, ACCOUNT_CUR)->setText(v.sprintf("%.2lf (%.2lf)", value, value-value2)); - // _accountsGrid->SetCellTextColour(i, ACCOUNT_CUR, (value >= 0.0) ? wxColor(0x00, 0x00, 0x00) : wxColor(0xFF, 0x00, 0x00)); + _accountsGrid->item(i, ACCOUNT_CUR)->setForeground((value >= 0) ? QBrush(Qt::black) : QBrush(Qt::red)); value = finalAccountAmount[accountIt->id]; _accountsGrid->item(i, ACCOUNT_FINAL)->setText(v.sprintf("%.2lf (%.2lf)", value, value-value2)); } @@ -727,8 +737,6 @@ void AccountPanel::UpdateStats() _statsGrid->resizeColumnToContents(1); - // _pie->DatasetChanged(_dataset); - layout(); } diff --git a/src/view/AccountPanel.hpp b/src/view/AccountPanel.hpp index e3f06a1..034848d 100644 --- a/src/view/AccountPanel.hpp +++ b/src/view/AccountPanel.hpp @@ -31,6 +31,8 @@ #include #include #include +#include +#include "pieview.hpp" #include "view.hpp" @@ -59,6 +61,9 @@ public: void ShowMonth(int month, int year); void GenerateMonth(int month, int year); + int _curMonth, _curYear; + +private slots: void OnOperationModified(); void OnAccountModified(); void OnTreeRightClick(); @@ -71,21 +76,19 @@ public: void OnUnGroup(); void OnUpdateNextMonths(); - int _curMonth, _curYear; - private: QTreeWidget *_tree; QCalendarWidget* _calendar; // GridAccount* _grid; QTableWidget* _accountsGrid, *_statsGrid; - // PiePlot* _pie; + PieView* _pie; double *_categoriesValues; // wxRadioBox *_radioMode; std::map _categoriesIndexes; std::vector* _curOperations; QString* _categories, *_accounts; std::map _accountsInitValues; - // CategorySimpleDataset* _dataset; + QStandardItemModel* _dataset; int _fixCosts; QRadioButton *_virtual, *_real, *_check; diff --git a/src/view/pieview.cpp b/src/view/pieview.cpp new file mode 100644 index 0000000..defb8b8 --- /dev/null +++ b/src/view/pieview.cpp @@ -0,0 +1,565 @@ + /**************************************************************************** + ** + ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). + ** All rights reserved. + ** Contact: Nokia Corporation (qt-info@nokia.com) + ** + ** This file is part of the examples of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:BSD$ + ** You may use this file under the terms of the BSD license as follows: + ** + ** "Redistribution and use in source and binary forms, with or without + ** modification, are permitted provided that the following conditions are + ** met: + ** * Redistributions of source code must retain the above copyright + ** notice, this list of conditions and the following disclaimer. + ** * Redistributions in binary form must reproduce the above copyright + ** notice, this list of conditions and the following disclaimer in + ** the documentation and/or other materials provided with the + ** distribution. + ** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor + ** the names of its contributors may be used to endorse or promote + ** products derived from this software without specific prior written + ** permission. + ** + ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + + #include + #include + + #ifndef M_PI + #define M_PI 3.1415927 + #endif + + #include "pieview.hpp" + + PieView::PieView(QWidget *parent) + : QAbstractItemView(parent) + { + horizontalScrollBar()->setRange(0, 0); + verticalScrollBar()->setRange(0, 0); + + margin = 8; + totalSize = 150; + pieSize = totalSize - 2*margin; + validItems = 0; + totalValue = 0.0; + rubberBand = 0; + } + + void PieView::dataChanged(const QModelIndex &topLeft, + const QModelIndex &bottomRight) + { + QAbstractItemView::dataChanged(topLeft, bottomRight); + + validItems = 0; + totalValue = 0.0; + + for (int row = 0; row < model()->rowCount(rootIndex()); ++row) { + + QModelIndex index = model()->index(row, 1, rootIndex()); + double value = model()->data(index).toDouble(); + + if (value > 0.0) { + totalValue += value; + validItems++; + } + } + viewport()->update(); + } + + bool PieView::edit(const QModelIndex &index, EditTrigger trigger, QEvent *event) + { + if (index.column() == 0) + return QAbstractItemView::edit(index, trigger, event); + else + return false; + } + + /* + Returns the item that covers the coordinate given in the view. + */ + + QModelIndex PieView::indexAt(const QPoint &point) const + { + if (validItems == 0) + return QModelIndex(); + + // Transform the view coordinates into contents widget coordinates. + int wx = point.x() + horizontalScrollBar()->value(); + int wy = point.y() + verticalScrollBar()->value(); + + if (wx < totalSize) { + double cx = wx - totalSize/2; + double cy = totalSize/2 - wy; // positive cy for items above the center + + // Determine the distance from the center point of the pie chart. + double d = pow(pow(cx, 2) + pow(cy, 2), 0.5); + + if (d == 0 || d > pieSize/2) + return QModelIndex(); + + // Determine the angle of the point. + double angle = (180 / M_PI) * acos(cx/d); + if (cy < 0) + angle = 360 - angle; + + // Find the relevant slice of the pie. + double startAngle = 0.0; + + for (int row = 0; row < model()->rowCount(rootIndex()); ++row) { + + QModelIndex index = model()->index(row, 1, rootIndex()); + double value = model()->data(index).toDouble(); + + if (value > 0.0) { + double sliceAngle = 360*value/totalValue; + + if (angle >= startAngle && angle < (startAngle + sliceAngle)) + return model()->index(row, 1, rootIndex()); + + startAngle += sliceAngle; + } + } + } else { + double itemHeight = QFontMetrics(viewOptions().font).height(); + int listItem = int((wy - margin) / itemHeight); + int validRow = 0; + + for (int row = 0; row < model()->rowCount(rootIndex()); ++row) { + + QModelIndex index = model()->index(row, 1, rootIndex()); + if (model()->data(index).toDouble() > 0.0) { + + if (listItem == validRow) + return model()->index(row, 0, rootIndex()); + + // Update the list index that corresponds to the next valid row. + validRow++; + } + } + } + + return QModelIndex(); + } + + bool PieView::isIndexHidden(const QModelIndex & /*index*/) const + { + return false; + } + + /* + Returns the rectangle of the item at position \a index in the + model. The rectangle is in contents coordinates. + */ + + QRect PieView::itemRect(const QModelIndex &index) const + { + if (!index.isValid()) + return QRect(); + + // Check whether the index's row is in the list of rows represented + // by slices. + QModelIndex valueIndex; + + if (index.column() != 1) + valueIndex = model()->index(index.row(), 1, rootIndex()); + else + valueIndex = index; + + if (model()->data(valueIndex).toDouble() > 0.0) { + + int listItem = 0; + for (int row = index.row()-1; row >= 0; --row) { + if (model()->data(model()->index(row, 1, rootIndex())).toDouble() > 0.0) + listItem++; + } + + double itemHeight; + + switch (index.column()) { + case 0: + itemHeight = QFontMetrics(viewOptions().font).height(); + + return QRect(margin, + //int(margin + listItem*itemHeight), + pieSize+margin*4, + totalSize - margin, int((margin + listItem*itemHeight)*2)); + // return QRect(totalSize, + // int(margin + listItem*itemHeight), + // totalSize - margin, int(itemHeight)); + case 1: + return viewport()->rect(); + } + + } + return QRect(); + } + + QRegion PieView::itemRegion(const QModelIndex &index) const + { + if (!index.isValid()) + return QRegion(); + + if (index.column() != 1) + return itemRect(index); + + if (model()->data(index).toDouble() <= 0.0) + return QRegion(); + + double startAngle = 0.0; + for (int row = 0; row < model()->rowCount(rootIndex()); ++row) { + + QModelIndex sliceIndex = model()->index(row, 1, rootIndex()); + double value = model()->data(sliceIndex).toDouble(); + + if (value > 0.0) { + double angle = 360*value/totalValue; + + if (sliceIndex == index) { + QPainterPath slicePath; + slicePath.moveTo(totalSize/2, totalSize/2); + slicePath.arcTo(margin, margin, margin+pieSize, margin+pieSize, + startAngle, angle); + slicePath.closeSubpath(); + + return QRegion(slicePath.toFillPolygon().toPolygon()); + } + + startAngle += angle; + } + } + + return QRegion(); + } + + int PieView::horizontalOffset() const + { + return horizontalScrollBar()->value(); + } + + void PieView::mousePressEvent(QMouseEvent *event) + { + QAbstractItemView::mousePressEvent(event); + origin = event->pos(); + if (!rubberBand) + rubberBand = new QRubberBand(QRubberBand::Rectangle, viewport()); + rubberBand->setGeometry(QRect(origin, QSize())); + rubberBand->show(); + } + + void PieView::mouseMoveEvent(QMouseEvent *event) + { + if (rubberBand) + rubberBand->setGeometry(QRect(origin, event->pos()).normalized()); + QAbstractItemView::mouseMoveEvent(event); + } + + void PieView::mouseReleaseEvent(QMouseEvent *event) + { + QAbstractItemView::mouseReleaseEvent(event); + if (rubberBand) + rubberBand->hide(); + viewport()->update(); + } + + QModelIndex PieView::moveCursor(QAbstractItemView::CursorAction cursorAction, + Qt::KeyboardModifiers /*modifiers*/) + { + QModelIndex current = currentIndex(); + + switch (cursorAction) { + case MoveLeft: + case MoveUp: + if (current.row() > 0) + current = model()->index(current.row() - 1, current.column(), + rootIndex()); + else + current = model()->index(0, current.column(), rootIndex()); + break; + case MoveRight: + case MoveDown: + if (current.row() < rows(current) - 1) + current = model()->index(current.row() + 1, current.column(), + rootIndex()); + else + current = model()->index(rows(current) - 1, current.column(), + rootIndex()); + break; + default: + break; + } + + viewport()->update(); + return current; + } + + void PieView::paintEvent(QPaintEvent *event) + { + QItemSelectionModel *selections = selectionModel(); + QStyleOptionViewItem option = viewOptions(); + QStyle::State state = option.state; + + QBrush background = option.palette.base(); + QPen foreground(option.palette.color(QPalette::WindowText)); + QPen textPen(option.palette.color(QPalette::Text)); + QPen highlightedPen(option.palette.color(QPalette::HighlightedText)); + + QPainter painter(viewport()); + painter.setRenderHint(QPainter::Antialiasing); + + painter.fillRect(event->rect(), background); + painter.setPen(foreground); + + // Viewport rectangles + QRect pieRect = QRect(margin, margin, pieSize, pieSize); + QPoint keyPoint = QPoint(totalSize - horizontalScrollBar()->value(), + margin - verticalScrollBar()->value()); + + if (validItems > 0) { + + painter.save(); + painter.translate(pieRect.x() - horizontalScrollBar()->value(), + pieRect.y() - verticalScrollBar()->value()); + painter.drawEllipse(0, 0, pieSize, pieSize); + double startAngle = 0.0; + int row; + + for (row = 0; row < model()->rowCount(rootIndex()); ++row) { + + QModelIndex index = model()->index(row, 1, rootIndex()); + double value = model()->data(index).toDouble(); + + if (value > 0.0) { + double angle = 360*value/totalValue; + + QModelIndex colorIndex = model()->index(row, 0, rootIndex()); + QColor color = QColor(model()->data(colorIndex, + Qt::DecorationRole).toString()); + + if (currentIndex() == index) + painter.setBrush(QBrush(color, Qt::Dense4Pattern)); + else if (selections->isSelected(index)) + painter.setBrush(QBrush(color, Qt::Dense3Pattern)); + else + painter.setBrush(QBrush(color)); + + painter.drawPie(0, 0, pieSize, pieSize, int(startAngle*16), + int(angle*16)); + + startAngle += angle; + } + } + painter.restore(); + + int keyNumber = 0; + + for (row = 0; row < model()->rowCount(rootIndex()); ++row) { + + QModelIndex index = model()->index(row, 1, rootIndex()); + double value = model()->data(index).toDouble(); + + if (value > 0.0) { + QModelIndex labelIndex = model()->index(row, 0, rootIndex()); + + QStyleOptionViewItem option = viewOptions(); + option.rect = visualRect(labelIndex); + if (selections->isSelected(labelIndex)) + option.state |= QStyle::State_Selected; + if (currentIndex() == labelIndex) + option.state |= QStyle::State_HasFocus; + itemDelegate()->paint(&painter, option, labelIndex); + + keyNumber++; + } + } + } + } + + void PieView::resizeEvent(QResizeEvent * /* event */) + { + updateGeometries(); + } + + int PieView::rows(const QModelIndex &index) const + { + return model()->rowCount(model()->parent(index)); + } + + void PieView::rowsInserted(const QModelIndex &parent, int start, int end) + { + for (int row = start; row <= end; ++row) { + + QModelIndex index = model()->index(row, 1, rootIndex()); + double value = model()->data(index).toDouble(); + + if (value > 0.0) { + totalValue += value; + validItems++; + } + } + + QAbstractItemView::rowsInserted(parent, start, end); + } + + void PieView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) + { + for (int row = start; row <= end; ++row) { + + QModelIndex index = model()->index(row, 1, rootIndex()); + double value = model()->data(index).toDouble(); + if (value > 0.0) { + totalValue -= value; + validItems--; + } + } + + QAbstractItemView::rowsAboutToBeRemoved(parent, start, end); + } + + void PieView::scrollContentsBy(int dx, int dy) + { + viewport()->scroll(dx, dy); + } + + void PieView::scrollTo(const QModelIndex &index, ScrollHint) + { + QRect area = viewport()->rect(); + QRect rect = visualRect(index); + + if (rect.left() < area.left()) + horizontalScrollBar()->setValue( + horizontalScrollBar()->value() + rect.left() - area.left()); + else if (rect.right() > area.right()) + horizontalScrollBar()->setValue( + horizontalScrollBar()->value() + qMin( + rect.right() - area.right(), rect.left() - area.left())); + + if (rect.top() < area.top()) + verticalScrollBar()->setValue( + verticalScrollBar()->value() + rect.top() - area.top()); + else if (rect.bottom() > area.bottom()) + verticalScrollBar()->setValue( + verticalScrollBar()->value() + qMin( + rect.bottom() - area.bottom(), rect.top() - area.top())); + + update(); + } + + /* + Find the indices corresponding to the extent of the selection. + */ + + void PieView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command) + { + // Use content widget coordinates because we will use the itemRegion() + // function to check for intersections. + + QRect contentsRect = rect.translated( + horizontalScrollBar()->value(), + verticalScrollBar()->value()).normalized(); + + int rows = model()->rowCount(rootIndex()); + int columns = model()->columnCount(rootIndex()); + QModelIndexList indexes; + + for (int row = 0; row < rows; ++row) { + for (int column = 0; column < columns; ++column) { + QModelIndex index = model()->index(row, column, rootIndex()); + QRegion region = itemRegion(index); + if (!region.intersect(contentsRect).isEmpty()) + indexes.append(index); + } + } + + if (indexes.size() > 0) { + int firstRow = indexes[0].row(); + int lastRow = indexes[0].row(); + int firstColumn = indexes[0].column(); + int lastColumn = indexes[0].column(); + + for (int i = 1; i < indexes.size(); ++i) { + firstRow = qMin(firstRow, indexes[i].row()); + lastRow = qMax(lastRow, indexes[i].row()); + firstColumn = qMin(firstColumn, indexes[i].column()); + lastColumn = qMax(lastColumn, indexes[i].column()); + } + + QItemSelection selection( + model()->index(firstRow, firstColumn, rootIndex()), + model()->index(lastRow, lastColumn, rootIndex())); + selectionModel()->select(selection, command); + } else { + QModelIndex noIndex; + QItemSelection selection(noIndex, noIndex); + selectionModel()->select(selection, command); + } + + update(); + } + + void PieView::updateGeometries() + { + horizontalScrollBar()->setPageStep(viewport()->width()); + horizontalScrollBar()->setRange(0, qMax(0, totalSize - viewport()->width())); + verticalScrollBar()->setPageStep(viewport()->height()); + verticalScrollBar()->setRange(0, qMax(0, 2*totalSize - viewport()->height())); + } + + int PieView::verticalOffset() const + { + return verticalScrollBar()->value(); + } + + /* + Returns the position of the item in viewport coordinates. + */ + + QRect PieView::visualRect(const QModelIndex &index) const + { + QRect rect = itemRect(index); + if (rect.isValid()) + return QRect(rect.left() - horizontalScrollBar()->value(), + rect.top() - verticalScrollBar()->value(), + rect.width(), rect.height()); + else + return rect; + } + + /* + Returns a region corresponding to the selection in viewport coordinates. + */ + + QRegion PieView::visualRegionForSelection(const QItemSelection &selection) const + { + int ranges = selection.count(); + + if (ranges == 0) + return QRect(); + + QRegion region; + for (int i = 0; i < ranges; ++i) { + QItemSelectionRange range = selection.at(i); + for (int row = range.top(); row <= range.bottom(); ++row) { + for (int col = range.left(); col <= range.right(); ++col) { + QModelIndex index = model()->index(row, col, rootIndex()); + region += visualRect(index); + } + } + } + return region; + } diff --git a/src/view/pieview.hpp b/src/view/pieview.hpp new file mode 100644 index 0000000..79964d2 --- /dev/null +++ b/src/view/pieview.hpp @@ -0,0 +1,110 @@ + /**************************************************************************** + ** + ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). + ** All rights reserved. + ** Contact: Nokia Corporation (qt-info@nokia.com) + ** + ** This file is part of the examples of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:BSD$ + ** You may use this file under the terms of the BSD license as follows: + ** + ** "Redistribution and use in source and binary forms, with or without + ** modification, are permitted provided that the following conditions are + ** met: + ** * Redistributions of source code must retain the above copyright + ** notice, this list of conditions and the following disclaimer. + ** * Redistributions in binary form must reproduce the above copyright + ** notice, this list of conditions and the following disclaimer in + ** the documentation and/or other materials provided with the + ** distribution. + ** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor + ** the names of its contributors may be used to endorse or promote + ** products derived from this software without specific prior written + ** permission. + ** + ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + + #ifndef PIEVIEW_H + #define PIEVIEW_H + + #include + #include + #include + #include + #include + #include + #include + #include + #include + + class QRubberBand; + + class PieView : public QAbstractItemView + { + Q_OBJECT + + public: + PieView(QWidget *parent = 0); + + QRect visualRect(const QModelIndex &index) const; + void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible); + QModelIndex indexAt(const QPoint &point) const; + + protected slots: + void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void rowsInserted(const QModelIndex &parent, int start, int end); + void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + + protected: + bool edit(const QModelIndex &index, EditTrigger trigger, QEvent *event); + QModelIndex moveCursor(QAbstractItemView::CursorAction cursorAction, + Qt::KeyboardModifiers modifiers); + + int horizontalOffset() const; + int verticalOffset() const; + + bool isIndexHidden(const QModelIndex &index) const; + + void setSelection(const QRect&, QItemSelectionModel::SelectionFlags command); + + void mousePressEvent(QMouseEvent *event); + + void mouseMoveEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + + void paintEvent(QPaintEvent *event); + void resizeEvent(QResizeEvent *event); + void scrollContentsBy(int dx, int dy); + + QRegion visualRegionForSelection(const QItemSelection &selection) const; + + private: + QRect itemRect(const QModelIndex &item) const; + QRegion itemRegion(const QModelIndex &index) const; + int rows(const QModelIndex &index = QModelIndex()) const; + void updateGeometries(); + + int margin; + int totalSize; + int pieSize; + int validItems; + double totalValue; + QPoint origin; + QRubberBand *rubberBand; + }; + + #endif