| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2016 The Qt Company Ltd. | 
| 4 | ** Contact: https://www.qt.io/licensing/ | 
| 5 | ** | 
| 6 | ** This file is part of the examples of the Qt Toolkit. | 
| 7 | ** | 
| 8 | ** $QT_BEGIN_LICENSE:BSD$ | 
| 9 | ** Commercial License Usage | 
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in | 
| 11 | ** accordance with the commercial license agreement provided with the | 
| 12 | ** Software or, alternatively, in accordance with the terms contained in | 
| 13 | ** a written agreement between you and The Qt Company. For licensing terms | 
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further | 
| 15 | ** information use the contact form at https://www.qt.io/contact-us. | 
| 16 | ** | 
| 17 | ** BSD License Usage | 
| 18 | ** Alternatively, you may use this file under the terms of the BSD license | 
| 19 | ** as follows: | 
| 20 | ** | 
| 21 | ** "Redistribution and use in source and binary forms, with or without | 
| 22 | ** modification, are permitted provided that the following conditions are | 
| 23 | ** met: | 
| 24 | **   * Redistributions of source code must retain the above copyright | 
| 25 | **     notice, this list of conditions and the following disclaimer. | 
| 26 | **   * Redistributions in binary form must reproduce the above copyright | 
| 27 | **     notice, this list of conditions and the following disclaimer in | 
| 28 | **     the documentation and/or other materials provided with the | 
| 29 | **     distribution. | 
| 30 | **   * Neither the name of The Qt Company Ltd nor the names of its | 
| 31 | **     contributors may be used to endorse or promote products derived | 
| 32 | **     from this software without specific prior written permission. | 
| 33 | ** | 
| 34 | ** | 
| 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
| 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
| 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
| 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
| 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
| 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 
| 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
| 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
| 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
| 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
| 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." | 
| 46 | ** | 
| 47 | ** $QT_END_LICENSE$ | 
| 48 | ** | 
| 49 | ****************************************************************************/ | 
| 50 |  | 
| 51 | #include "pieview.h" | 
| 52 |  | 
| 53 | #include <QtWidgets> | 
| 54 |  | 
| 55 | PieView::PieView(QWidget *parent) | 
| 56 |     : QAbstractItemView(parent) | 
| 57 | { | 
| 58 |     horizontalScrollBar()->setRange(min: 0, max: 0); | 
| 59 |     verticalScrollBar()->setRange(min: 0, max: 0); | 
| 60 | } | 
| 61 |  | 
| 62 | void PieView::dataChanged(const QModelIndex &topLeft, | 
| 63 |                           const QModelIndex &bottomRight, | 
| 64 |                           const QVector<int> &roles) | 
| 65 | { | 
| 66 |     QAbstractItemView::dataChanged(topLeft, bottomRight, roles); | 
| 67 |  | 
| 68 |     if (!roles.contains(t: Qt::DisplayRole)) | 
| 69 |         return; | 
| 70 |  | 
| 71 |     validItems = 0; | 
| 72 |     totalValue = 0.0; | 
| 73 |  | 
| 74 |     for (int row = 0; row < model()->rowCount(parent: rootIndex()); ++row) { | 
| 75 |  | 
| 76 |         QModelIndex index = model()->index(row, column: 1, parent: rootIndex()); | 
| 77 |         double value = model()->data(index, role: Qt::DisplayRole).toDouble(); | 
| 78 |  | 
| 79 |         if (value > 0.0) { | 
| 80 |             totalValue += value; | 
| 81 |             validItems++; | 
| 82 |         } | 
| 83 |     } | 
| 84 |     viewport()->update(); | 
| 85 | } | 
| 86 |  | 
| 87 | bool PieView::edit(const QModelIndex &index, EditTrigger trigger, QEvent *event) | 
| 88 | { | 
| 89 |     if (index.column() == 0) | 
| 90 |         return QAbstractItemView::edit(index, trigger, event); | 
| 91 |     else | 
| 92 |         return false; | 
| 93 | } | 
| 94 |  | 
| 95 | /* | 
| 96 |     Returns the item that covers the coordinate given in the view. | 
| 97 | */ | 
| 98 |  | 
| 99 | QModelIndex PieView::indexAt(const QPoint &point) const | 
| 100 | { | 
| 101 |     if (validItems == 0) | 
| 102 |         return QModelIndex(); | 
| 103 |  | 
| 104 |     // Transform the view coordinates into contents widget coordinates. | 
| 105 |     int wx = point.x() + horizontalScrollBar()->value(); | 
| 106 |     int wy = point.y() + verticalScrollBar()->value(); | 
| 107 |  | 
| 108 |     if (wx < totalSize) { | 
| 109 |         double cx = wx - totalSize / 2; | 
| 110 |         double cy = totalSize / 2 - wy; // positive cy for items above the center | 
| 111 |  | 
| 112 |         // Determine the distance from the center point of the pie chart. | 
| 113 |         double d = std::sqrt(x: std::pow(x: cx, y: 2) + std::pow(x: cy, y: 2)); | 
| 114 |  | 
| 115 |         if (d == 0 || d > pieSize / 2) | 
| 116 |             return QModelIndex(); | 
| 117 |  | 
| 118 |         // Determine the angle of the point. | 
| 119 |         double angle = qRadiansToDegrees(radians: std::atan2(y: cy, x: cx)); | 
| 120 |         if (angle < 0) | 
| 121 |             angle = 360 + angle; | 
| 122 |  | 
| 123 |         // Find the relevant slice of the pie. | 
| 124 |         double startAngle = 0.0; | 
| 125 |  | 
| 126 |         for (int row = 0; row < model()->rowCount(parent: rootIndex()); ++row) { | 
| 127 |  | 
| 128 |             QModelIndex index = model()->index(row, column: 1, parent: rootIndex()); | 
| 129 |             double value = model()->data(index).toDouble(); | 
| 130 |  | 
| 131 |             if (value > 0.0) { | 
| 132 |                 double sliceAngle = 360 * value / totalValue; | 
| 133 |  | 
| 134 |                 if (angle >= startAngle && angle < (startAngle + sliceAngle)) | 
| 135 |                     return model()->index(row, column: 1, parent: rootIndex()); | 
| 136 |  | 
| 137 |                 startAngle += sliceAngle; | 
| 138 |             } | 
| 139 |         } | 
| 140 |     } else { | 
| 141 |         double itemHeight = QFontMetrics(viewOptions().font).height(); | 
| 142 |         int listItem = int((wy - margin) / itemHeight); | 
| 143 |         int validRow = 0; | 
| 144 |  | 
| 145 |         for (int row = 0; row < model()->rowCount(parent: rootIndex()); ++row) { | 
| 146 |  | 
| 147 |             QModelIndex index = model()->index(row, column: 1, parent: rootIndex()); | 
| 148 |             if (model()->data(index).toDouble() > 0.0) { | 
| 149 |  | 
| 150 |                 if (listItem == validRow) | 
| 151 |                     return model()->index(row, column: 0, parent: rootIndex()); | 
| 152 |  | 
| 153 |                 // Update the list index that corresponds to the next valid row. | 
| 154 |                 ++validRow; | 
| 155 |             } | 
| 156 |         } | 
| 157 |     } | 
| 158 |  | 
| 159 |     return QModelIndex(); | 
| 160 | } | 
| 161 |  | 
| 162 | bool PieView::isIndexHidden(const QModelIndex & /*index*/) const | 
| 163 | { | 
| 164 |     return false; | 
| 165 | } | 
| 166 |  | 
| 167 | /* | 
| 168 |     Returns the rectangle of the item at position \a index in the | 
| 169 |     model. The rectangle is in contents coordinates. | 
| 170 | */ | 
| 171 |  | 
| 172 | QRect PieView::itemRect(const QModelIndex &index) const | 
| 173 | { | 
| 174 |     if (!index.isValid()) | 
| 175 |         return QRect(); | 
| 176 |  | 
| 177 |     // Check whether the index's row is in the list of rows represented | 
| 178 |     // by slices. | 
| 179 |     QModelIndex valueIndex; | 
| 180 |  | 
| 181 |     if (index.column() != 1) | 
| 182 |         valueIndex = model()->index(row: index.row(), column: 1, parent: rootIndex()); | 
| 183 |     else | 
| 184 |         valueIndex = index; | 
| 185 |  | 
| 186 |     if (model()->data(index: valueIndex).toDouble() <= 0.0) | 
| 187 |         return QRect(); | 
| 188 |  | 
| 189 |     int listItem = 0; | 
| 190 |     for (int row = index.row()-1; row >= 0; --row) { | 
| 191 |         if (model()->data(index: model()->index(row, column: 1, parent: rootIndex())).toDouble() > 0.0) | 
| 192 |             listItem++; | 
| 193 |     } | 
| 194 |  | 
| 195 |     switch (index.column()) { | 
| 196 |     case 0: { | 
| 197 |         const qreal itemHeight = QFontMetricsF(viewOptions().font).height(); | 
| 198 |  | 
| 199 |         return QRect(totalSize, | 
| 200 |                      qRound(d: margin + listItem * itemHeight), | 
| 201 |                      totalSize - margin, qRound(d: itemHeight)); | 
| 202 |     } | 
| 203 |     case 1: | 
| 204 |         return viewport()->rect(); | 
| 205 |     } | 
| 206 |     return QRect(); | 
| 207 | } | 
| 208 |  | 
| 209 | QRegion PieView::itemRegion(const QModelIndex &index) const | 
| 210 | { | 
| 211 |     if (!index.isValid()) | 
| 212 |         return QRegion(); | 
| 213 |  | 
| 214 |     if (index.column() != 1) | 
| 215 |         return itemRect(index); | 
| 216 |  | 
| 217 |     if (model()->data(index).toDouble() <= 0.0) | 
| 218 |         return QRegion(); | 
| 219 |  | 
| 220 |     double startAngle = 0.0; | 
| 221 |     for (int row = 0; row < model()->rowCount(parent: rootIndex()); ++row) { | 
| 222 |  | 
| 223 |         QModelIndex sliceIndex = model()->index(row, column: 1, parent: rootIndex()); | 
| 224 |         double value = model()->data(index: sliceIndex).toDouble(); | 
| 225 |  | 
| 226 |         if (value > 0.0) { | 
| 227 |             double angle = 360 * value / totalValue; | 
| 228 |  | 
| 229 |             if (sliceIndex == index) { | 
| 230 |                 QPainterPath slicePath; | 
| 231 |                 slicePath.moveTo(x: totalSize / 2, y: totalSize / 2); | 
| 232 |                 slicePath.arcTo(x: margin, y: margin, w: margin + pieSize, h: margin + pieSize, | 
| 233 |                                 startAngle, arcLength: angle); | 
| 234 |                 slicePath.closeSubpath(); | 
| 235 |  | 
| 236 |                 return QRegion(slicePath.toFillPolygon().toPolygon()); | 
| 237 |             } | 
| 238 |  | 
| 239 |             startAngle += angle; | 
| 240 |         } | 
| 241 |     } | 
| 242 |  | 
| 243 |     return QRegion(); | 
| 244 | } | 
| 245 |  | 
| 246 | int PieView::horizontalOffset() const | 
| 247 | { | 
| 248 |     return horizontalScrollBar()->value(); | 
| 249 | } | 
| 250 |  | 
| 251 | void PieView::mousePressEvent(QMouseEvent *event) | 
| 252 | { | 
| 253 |     QAbstractItemView::mousePressEvent(event); | 
| 254 |     origin = event->pos(); | 
| 255 |     if (!rubberBand) | 
| 256 |         rubberBand = new QRubberBand(QRubberBand::Rectangle, viewport()); | 
| 257 |     rubberBand->setGeometry(QRect(origin, QSize())); | 
| 258 |     rubberBand->show(); | 
| 259 | } | 
| 260 |  | 
| 261 | void PieView::mouseMoveEvent(QMouseEvent *event) | 
| 262 | { | 
| 263 |     if (rubberBand) | 
| 264 |         rubberBand->setGeometry(QRect(origin, event->pos()).normalized()); | 
| 265 |     QAbstractItemView::mouseMoveEvent(event); | 
| 266 | } | 
| 267 |  | 
| 268 | void PieView::mouseReleaseEvent(QMouseEvent *event) | 
| 269 | { | 
| 270 |     QAbstractItemView::mouseReleaseEvent(event); | 
| 271 |     if (rubberBand) | 
| 272 |         rubberBand->hide(); | 
| 273 |     viewport()->update(); | 
| 274 | } | 
| 275 |  | 
| 276 | QModelIndex PieView::moveCursor(QAbstractItemView::CursorAction cursorAction, | 
| 277 |                                 Qt::KeyboardModifiers /*modifiers*/) | 
| 278 | { | 
| 279 |     QModelIndex current = currentIndex(); | 
| 280 |  | 
| 281 |     switch (cursorAction) { | 
| 282 |         case MoveLeft: | 
| 283 |         case MoveUp: | 
| 284 |             if (current.row() > 0) | 
| 285 |                 current = model()->index(row: current.row() - 1, column: current.column(), | 
| 286 |                                          parent: rootIndex()); | 
| 287 |             else | 
| 288 |                 current = model()->index(row: 0, column: current.column(), parent: rootIndex()); | 
| 289 |             break; | 
| 290 |         case MoveRight: | 
| 291 |         case MoveDown: | 
| 292 |             if (current.row() < rows(index: current) - 1) | 
| 293 |                 current = model()->index(row: current.row() + 1, column: current.column(), | 
| 294 |                                          parent: rootIndex()); | 
| 295 |             else | 
| 296 |                 current = model()->index(row: rows(index: current) - 1, column: current.column(), | 
| 297 |                                          parent: rootIndex()); | 
| 298 |             break; | 
| 299 |         default: | 
| 300 |             break; | 
| 301 |     } | 
| 302 |  | 
| 303 |     viewport()->update(); | 
| 304 |     return current; | 
| 305 | } | 
| 306 |  | 
| 307 | void PieView::paintEvent(QPaintEvent *event) | 
| 308 | { | 
| 309 |     QItemSelectionModel *selections = selectionModel(); | 
| 310 |     QStyleOptionViewItem option = viewOptions(); | 
| 311 |  | 
| 312 |     QBrush background = option.palette.base(); | 
| 313 |     QPen foreground(option.palette.color(cr: QPalette::WindowText)); | 
| 314 |  | 
| 315 |     QPainter painter(viewport()); | 
| 316 |     painter.setRenderHint(hint: QPainter::Antialiasing); | 
| 317 |  | 
| 318 |     painter.fillRect(event->rect(), background); | 
| 319 |     painter.setPen(foreground); | 
| 320 |  | 
| 321 |     // Viewport rectangles | 
| 322 |     QRect pieRect = QRect(margin, margin, pieSize, pieSize); | 
| 323 |  | 
| 324 |     if (validItems <= 0) | 
| 325 |         return; | 
| 326 |  | 
| 327 |     painter.save(); | 
| 328 |     painter.translate(dx: pieRect.x() - horizontalScrollBar()->value(), | 
| 329 |                       dy: pieRect.y() - verticalScrollBar()->value()); | 
| 330 |     painter.drawEllipse(x: 0, y: 0, w: pieSize, h: pieSize); | 
| 331 |     double startAngle = 0.0; | 
| 332 |     int row; | 
| 333 |  | 
| 334 |     for (row = 0; row < model()->rowCount(parent: rootIndex()); ++row) { | 
| 335 |         QModelIndex index = model()->index(row, column: 1, parent: rootIndex()); | 
| 336 |         double value = model()->data(index).toDouble(); | 
| 337 |  | 
| 338 |         if (value > 0.0) { | 
| 339 |             double angle = 360 * value / totalValue; | 
| 340 |  | 
| 341 |             QModelIndex colorIndex = model()->index(row, column: 0, parent: rootIndex()); | 
| 342 |             QColor color = QColor(model()->data(index: colorIndex, role: Qt::DecorationRole).toString()); | 
| 343 |  | 
| 344 |             if (currentIndex() == index) | 
| 345 |                 painter.setBrush(QBrush(color, Qt::Dense4Pattern)); | 
| 346 |             else if (selections->isSelected(index)) | 
| 347 |                 painter.setBrush(QBrush(color, Qt::Dense3Pattern)); | 
| 348 |             else | 
| 349 |                 painter.setBrush(QBrush(color)); | 
| 350 |  | 
| 351 |             painter.drawPie(x: 0, y: 0, w: pieSize, h: pieSize, a: int(startAngle*16), alen: int(angle*16)); | 
| 352 |  | 
| 353 |             startAngle += angle; | 
| 354 |         } | 
| 355 |     } | 
| 356 |     painter.restore(); | 
| 357 |  | 
| 358 |     int keyNumber = 0; | 
| 359 |  | 
| 360 |     for (row = 0; row < model()->rowCount(parent: rootIndex()); ++row) { | 
| 361 |         QModelIndex index = model()->index(row, column: 1, parent: rootIndex()); | 
| 362 |         double value = model()->data(index).toDouble(); | 
| 363 |  | 
| 364 |         if (value > 0.0) { | 
| 365 |             QModelIndex labelIndex = model()->index(row, column: 0, parent: rootIndex()); | 
| 366 |  | 
| 367 |             QStyleOptionViewItem option = viewOptions(); | 
| 368 |             option.rect = visualRect(index: labelIndex); | 
| 369 |             if (selections->isSelected(index: labelIndex)) | 
| 370 |                 option.state |= QStyle::State_Selected; | 
| 371 |             if (currentIndex() == labelIndex) | 
| 372 |                 option.state |= QStyle::State_HasFocus; | 
| 373 |             itemDelegate()->paint(painter: &painter, option, index: labelIndex); | 
| 374 |  | 
| 375 |             ++keyNumber; | 
| 376 |         } | 
| 377 |     } | 
| 378 | } | 
| 379 |  | 
| 380 | void PieView::resizeEvent(QResizeEvent * /* event */) | 
| 381 | { | 
| 382 |     updateGeometries(); | 
| 383 | } | 
| 384 |  | 
| 385 | int PieView::rows(const QModelIndex &index) const | 
| 386 | { | 
| 387 |     return model()->rowCount(parent: model()->parent(child: index)); | 
| 388 | } | 
| 389 |  | 
| 390 | void PieView::rowsInserted(const QModelIndex &parent, int start, int end) | 
| 391 | { | 
| 392 |     for (int row = start; row <= end; ++row) { | 
| 393 |         QModelIndex index = model()->index(row, column: 1, parent: rootIndex()); | 
| 394 |         double value = model()->data(index).toDouble(); | 
| 395 |  | 
| 396 |         if (value > 0.0) { | 
| 397 |             totalValue += value; | 
| 398 |             ++validItems; | 
| 399 |         } | 
| 400 |     } | 
| 401 |  | 
| 402 |     QAbstractItemView::rowsInserted(parent, start, end); | 
| 403 | } | 
| 404 |  | 
| 405 | void PieView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) | 
| 406 | { | 
| 407 |     for (int row = start; row <= end; ++row) { | 
| 408 |         QModelIndex index = model()->index(row, column: 1, parent: rootIndex()); | 
| 409 |         double value = model()->data(index).toDouble(); | 
| 410 |         if (value > 0.0) { | 
| 411 |             totalValue -= value; | 
| 412 |             --validItems; | 
| 413 |         } | 
| 414 |     } | 
| 415 |  | 
| 416 |     QAbstractItemView::rowsAboutToBeRemoved(parent, start, end); | 
| 417 | } | 
| 418 |  | 
| 419 | void PieView::scrollContentsBy(int dx, int dy) | 
| 420 | { | 
| 421 |     viewport()->scroll(dx, dy); | 
| 422 | } | 
| 423 |  | 
| 424 | void PieView::scrollTo(const QModelIndex &index, ScrollHint) | 
| 425 | { | 
| 426 |     QRect area = viewport()->rect(); | 
| 427 |     QRect rect = visualRect(index); | 
| 428 |  | 
| 429 |     if (rect.left() < area.left()) { | 
| 430 |         horizontalScrollBar()->setValue( | 
| 431 |             horizontalScrollBar()->value() + rect.left() - area.left()); | 
| 432 |     } else if (rect.right() > area.right()) { | 
| 433 |         horizontalScrollBar()->setValue( | 
| 434 |             horizontalScrollBar()->value() + qMin( | 
| 435 |                 a: rect.right() - area.right(), b: rect.left() - area.left())); | 
| 436 |     } | 
| 437 |  | 
| 438 |     if (rect.top() < area.top()) { | 
| 439 |         verticalScrollBar()->setValue( | 
| 440 |             verticalScrollBar()->value() + rect.top() - area.top()); | 
| 441 |     } else if (rect.bottom() > area.bottom()) { | 
| 442 |         verticalScrollBar()->setValue( | 
| 443 |             verticalScrollBar()->value() + qMin( | 
| 444 |                 a: rect.bottom() - area.bottom(), b: rect.top() - area.top())); | 
| 445 |     } | 
| 446 |  | 
| 447 |     update(); | 
| 448 | } | 
| 449 |  | 
| 450 | /* | 
| 451 |     Find the indices corresponding to the extent of the selection. | 
| 452 | */ | 
| 453 |  | 
| 454 | void PieView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command) | 
| 455 | { | 
| 456 |     // Use content widget coordinates because we will use the itemRegion() | 
| 457 |     // function to check for intersections. | 
| 458 |  | 
| 459 |     QRect contentsRect = rect.translated( | 
| 460 |                             dx: horizontalScrollBar()->value(), | 
| 461 |                             dy: verticalScrollBar()->value()).normalized(); | 
| 462 |  | 
| 463 |     int rows = model()->rowCount(parent: rootIndex()); | 
| 464 |     int columns = model()->columnCount(parent: rootIndex()); | 
| 465 |     QModelIndexList indexes; | 
| 466 |  | 
| 467 |     for (int row = 0; row < rows; ++row) { | 
| 468 |         for (int column = 0; column < columns; ++column) { | 
| 469 |             QModelIndex index = model()->index(row, column, parent: rootIndex()); | 
| 470 |             QRegion region = itemRegion(index); | 
| 471 |             if (region.intersects(r: contentsRect)) | 
| 472 |                 indexes.append(t: index); | 
| 473 |         } | 
| 474 |     } | 
| 475 |  | 
| 476 |     if (indexes.size() > 0) { | 
| 477 |         int firstRow = indexes.at(i: 0).row(); | 
| 478 |         int lastRow = firstRow; | 
| 479 |         int firstColumn = indexes.at(i: 0).column(); | 
| 480 |         int lastColumn = firstColumn; | 
| 481 |  | 
| 482 |         for (int i = 1; i < indexes.size(); ++i) { | 
| 483 |             firstRow = qMin(a: firstRow, b: indexes.at(i).row()); | 
| 484 |             lastRow = qMax(a: lastRow, b: indexes.at(i).row()); | 
| 485 |             firstColumn = qMin(a: firstColumn, b: indexes.at(i).column()); | 
| 486 |             lastColumn = qMax(a: lastColumn, b: indexes.at(i).column()); | 
| 487 |         } | 
| 488 |  | 
| 489 |         QItemSelection selection( | 
| 490 |             model()->index(row: firstRow, column: firstColumn, parent: rootIndex()), | 
| 491 |             model()->index(row: lastRow, column: lastColumn, parent: rootIndex())); | 
| 492 |         selectionModel()->select(selection, command); | 
| 493 |     } else { | 
| 494 |         QModelIndex noIndex; | 
| 495 |         QItemSelection selection(noIndex, noIndex); | 
| 496 |         selectionModel()->select(selection, command); | 
| 497 |     } | 
| 498 |  | 
| 499 |     update(); | 
| 500 | } | 
| 501 |  | 
| 502 | void PieView::updateGeometries() | 
| 503 | { | 
| 504 |     horizontalScrollBar()->setPageStep(viewport()->width()); | 
| 505 |     horizontalScrollBar()->setRange(min: 0, max: qMax(a: 0, b: 2 * totalSize - viewport()->width())); | 
| 506 |     verticalScrollBar()->setPageStep(viewport()->height()); | 
| 507 |     verticalScrollBar()->setRange(min: 0, max: qMax(a: 0, b: totalSize - viewport()->height())); | 
| 508 | } | 
| 509 |  | 
| 510 | int PieView::verticalOffset() const | 
| 511 | { | 
| 512 |     return verticalScrollBar()->value(); | 
| 513 | } | 
| 514 |  | 
| 515 | /* | 
| 516 |     Returns the position of the item in viewport coordinates. | 
| 517 | */ | 
| 518 |  | 
| 519 | QRect PieView::visualRect(const QModelIndex &index) const | 
| 520 | { | 
| 521 |     QRect rect = itemRect(index); | 
| 522 |     if (!rect.isValid()) | 
| 523 |         return rect; | 
| 524 |  | 
| 525 |     return QRect(rect.left() - horizontalScrollBar()->value(), | 
| 526 |                  rect.top() - verticalScrollBar()->value(), | 
| 527 |                  rect.width(), rect.height()); | 
| 528 | } | 
| 529 |  | 
| 530 | /* | 
| 531 |     Returns a region corresponding to the selection in viewport coordinates. | 
| 532 | */ | 
| 533 |  | 
| 534 | QRegion PieView::visualRegionForSelection(const QItemSelection &selection) const | 
| 535 | { | 
| 536 |     int ranges = selection.count(); | 
| 537 |  | 
| 538 |     if (ranges == 0) | 
| 539 |         return QRect(); | 
| 540 |  | 
| 541 |     QRegion region; | 
| 542 |     for (int i = 0; i < ranges; ++i) { | 
| 543 |         const QItemSelectionRange &range = selection.at(i); | 
| 544 |         for (int row = range.top(); row <= range.bottom(); ++row) { | 
| 545 |             for (int col = range.left(); col <= range.right(); ++col) { | 
| 546 |                 QModelIndex index = model()->index(row, column: col, parent: rootIndex()); | 
| 547 |                 region += visualRect(index); | 
| 548 |             } | 
| 549 |         } | 
| 550 |     } | 
| 551 |     return region; | 
| 552 | } | 
| 553 |  |