1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qtableview.h"
43
44 #ifndef QT_NO_TABLEVIEW
45 #include <qheaderview.h>
46 #include <qitemdelegate.h>
47 #include <qapplication.h>
48 #include <qpainter.h>
49 #include <qstyle.h>
50 #include <qsize.h>
51 #include <qevent.h>
52 #include <qbitarray.h>
53 #include <qscrollbar.h>
54 #include <qabstractbutton.h>
55 #include <private/qtableview_p.h>
56 #ifndef QT_NO_ACCESSIBILITY
57 #include <qaccessible.h>
58 #endif
59
60 QT_BEGIN_NAMESPACE
61
62 /** \internal
63 Add a span to the collection. the collection takes the ownership.
64 */
addSpan(QSpanCollection::Span * span)65 void QSpanCollection::addSpan(QSpanCollection::Span *span)
66 {
67 spans.append(span);
68 Index::iterator it_y = index.lowerBound(-span->top());
69 if (it_y == index.end() || it_y.key() != -span->top()) {
70 //there is no spans that starts with the row in the index, so create a sublist for it.
71 SubIndex sub_index;
72 if (it_y != index.end()) {
73 //the previouslist is the list of spans that sarts _before_ the row of the span.
74 // and which may intersect this row.
75 const SubIndex previousList = it_y.value();
76 foreach(Span *s, previousList) {
77 //If a subspans intersect the row, we need to split it into subspans
78 if(s->bottom() >= span->top())
79 sub_index.insert(-s->left(), s);
80 }
81 }
82 it_y = index.insert(-span->top(), sub_index);
83 //we will insert span to *it_y in the later loop
84 }
85
86 //insert the span as supspan in all the lists that intesects the span
87 while(-it_y.key() <= span->bottom()) {
88 (*it_y).insert(-span->left(), span);
89 if(it_y == index.begin())
90 break;
91 --it_y;
92 }
93 }
94
95
96 /** \internal
97 * Has to be called after the height and width of a span is changed.
98 *
99 * old_height is the height before the change
100 *
101 * if the size of the span is now 0x0 the span will be deleted.
102 */
updateSpan(QSpanCollection::Span * span,int old_height)103 void QSpanCollection::updateSpan(QSpanCollection::Span *span, int old_height)
104 {
105 if (old_height < span->height()) {
106 //add the span as subspan in all the lists that intersect the new covered columns
107 Index::iterator it_y = index.lowerBound(-(span->top() + old_height - 1));
108 Q_ASSERT(it_y != index.end()); //it_y must exist since the span is in the list
109 while (-it_y.key() <= span->bottom()) {
110 (*it_y).insert(-span->left(), span);
111 if(it_y == index.begin())
112 break;
113 --it_y;
114 }
115 } else if (old_height > span->height()) {
116 //remove the span from all the subspans lists that intersect the columns not covered anymore
117 Index::iterator it_y = index.lowerBound(-qMax(span->bottom(), span->top())); //qMax useful if height is 0
118 Q_ASSERT(it_y != index.end()); //it_y must exist since the span is in the list
119 while (-it_y.key() <= span->top() + old_height -1) {
120 if (-it_y.key() > span->bottom()) {
121 int removed = (*it_y).remove(-span->left());
122 Q_ASSERT(removed == 1); Q_UNUSED(removed);
123 if (it_y->isEmpty()) {
124 it_y = index.erase(it_y);
125 }
126 }
127 if(it_y == index.begin())
128 break;
129 --it_y;
130 }
131 }
132
133 if (span->width() == 0 && span->height() == 0) {
134 spans.removeOne(span);
135 delete span;
136 }
137 }
138
139 /** \internal
140 * \return a spans that spans over cell x,y (column,row) or 0 if there is none.
141 */
spanAt(int x,int y) const142 QSpanCollection::Span *QSpanCollection::spanAt(int x, int y) const
143 {
144 Index::const_iterator it_y = index.lowerBound(-y);
145 if (it_y == index.end())
146 return 0;
147 SubIndex::const_iterator it_x = (*it_y).lowerBound(-x);
148 if (it_x == (*it_y).end())
149 return 0;
150 Span *span = *it_x;
151 if (span->right() >= x && span->bottom() >= y)
152 return span;
153 return 0;
154 }
155
156
157 /** \internal
158 * remove and deletes all spans inside the collection
159 */
clear()160 void QSpanCollection::clear()
161 {
162 qDeleteAll(spans);
163 index.clear();
164 spans.clear();
165 }
166
167 /** \internal
168 * return a list to all the spans that spans over cells in the given rectangle
169 */
spansInRect(int x,int y,int w,int h) const170 QList<QSpanCollection::Span *> QSpanCollection::spansInRect(int x, int y, int w, int h) const
171 {
172 QSet<Span *> list;
173 Index::const_iterator it_y = index.lowerBound(-y);
174 if(it_y == index.end())
175 --it_y;
176 while(-it_y.key() <= y + h) {
177 SubIndex::const_iterator it_x = (*it_y).lowerBound(-x);
178 if (it_x == (*it_y).end())
179 --it_x;
180 while(-it_x.key() <= x + w) {
181 Span *s = *it_x;
182 if (s->bottom() >= y && s->right() >= x)
183 list << s;
184 if (it_x == (*it_y).begin())
185 break;
186 --it_x;
187 }
188 if(it_y == index.begin())
189 break;
190 --it_y;
191 }
192 return list.toList();
193 }
194
195 #undef DEBUG_SPAN_UPDATE
196
197 #ifdef DEBUG_SPAN_UPDATE
operator <<(QDebug str,const QSpanCollection::Span & span)198 QDebug operator<<(QDebug str, const QSpanCollection::Span &span)
199 {
200 str << "(" << span.top() << "," << span.left() << "," << span.bottom() << "," << span.right() << ")";
201 return str;
202 }
203 #endif
204
205 /** \internal
206 * Updates the span collection after row insertion.
207 */
updateInsertedRows(int start,int end)208 void QSpanCollection::updateInsertedRows(int start, int end)
209 {
210 #ifdef DEBUG_SPAN_UPDATE
211 qDebug() << Q_FUNC_INFO;
212 qDebug() << start << end;
213 qDebug() << index;
214 #endif
215 if (spans.isEmpty())
216 return;
217
218 int delta = end - start + 1;
219 #ifdef DEBUG_SPAN_UPDATE
220 qDebug("Before");
221 #endif
222 for (SpanList::iterator it = spans.begin(); it != spans.end(); ++it) {
223 Span *span = *it;
224 #ifdef DEBUG_SPAN_UPDATE
225 qDebug() << span << *span;
226 #endif
227 if (span->m_bottom < start)
228 continue;
229 if (span->m_top >= start)
230 span->m_top += delta;
231 span->m_bottom += delta;
232 }
233
234 #ifdef DEBUG_SPAN_UPDATE
235 qDebug("After");
236 foreach (QSpanCollection::Span *span, spans)
237 qDebug() << span << *span;
238 #endif
239
240 for (Index::iterator it_y = index.begin(); it_y != index.end(); ) {
241 int y = -it_y.key();
242 if (y < start) {
243 ++it_y;
244 continue;
245 }
246
247 index.insert(-y - delta, it_y.value());
248 it_y = index.erase(it_y);
249 }
250 #ifdef DEBUG_SPAN_UPDATE
251 qDebug() << index;
252 #endif
253 }
254
255 /** \internal
256 * Updates the span collection after column insertion.
257 */
updateInsertedColumns(int start,int end)258 void QSpanCollection::updateInsertedColumns(int start, int end)
259 {
260 #ifdef DEBUG_SPAN_UPDATE
261 qDebug() << Q_FUNC_INFO;
262 qDebug() << start << end;
263 qDebug() << index;
264 #endif
265 if (spans.isEmpty())
266 return;
267
268 int delta = end - start + 1;
269 #ifdef DEBUG_SPAN_UPDATE
270 qDebug("Before");
271 #endif
272 for (SpanList::iterator it = spans.begin(); it != spans.end(); ++it) {
273 Span *span = *it;
274 #ifdef DEBUG_SPAN_UPDATE
275 qDebug() << span << *span;
276 #endif
277 if (span->m_right < start)
278 continue;
279 if (span->m_left >= start)
280 span->m_left += delta;
281 span->m_right += delta;
282 }
283
284 #ifdef DEBUG_SPAN_UPDATE
285 qDebug("After");
286 foreach (QSpanCollection::Span *span, spans)
287 qDebug() << span << *span;
288 #endif
289
290 for (Index::iterator it_y = index.begin(); it_y != index.end(); ++it_y) {
291 SubIndex &subindex = it_y.value();
292 for (SubIndex::iterator it = subindex.begin(); it != subindex.end(); ) {
293 int x = -it.key();
294 if (x < start) {
295 ++it;
296 continue;
297 }
298 subindex.insert(-x - delta, it.value());
299 it = subindex.erase(it);
300 }
301 }
302 #ifdef DEBUG_SPAN_UPDATE
303 qDebug() << index;
304 #endif
305 }
306
307 /** \internal
308 * Cleans a subindex from to be deleted spans. The update argument is used
309 * to move the spans inside the subindex, in case their anchor changed.
310 * \return true if no span in this subindex starts at y, and should thus be deleted.
311 */
cleanSpanSubIndex(QSpanCollection::SubIndex & subindex,int y,bool update)312 bool QSpanCollection::cleanSpanSubIndex(QSpanCollection::SubIndex &subindex, int y, bool update)
313 {
314 if (subindex.isEmpty())
315 return true;
316
317 bool should_be_deleted = true;
318 SubIndex::iterator it = subindex.end();
319 do {
320 --it;
321 int x = -it.key();
322 Span *span = it.value();
323 if (span->will_be_deleted) {
324 it = subindex.erase(it);
325 continue;
326 }
327 if (update && span->m_left != x) {
328 subindex.insert(-span->m_left, span);
329 it = subindex.erase(it);
330 }
331 if (should_be_deleted && span->m_top == y)
332 should_be_deleted = false;
333 } while (it != subindex.begin());
334
335 return should_be_deleted;
336 }
337
338 /** \internal
339 * Updates the span collection after row removal.
340 */
updateRemovedRows(int start,int end)341 void QSpanCollection::updateRemovedRows(int start, int end)
342 {
343 #ifdef DEBUG_SPAN_UPDATE
344 qDebug() << Q_FUNC_INFO;
345 qDebug() << start << end;
346 qDebug() << index;
347 #endif
348 if (spans.isEmpty())
349 return;
350
351 SpanList spansToBeDeleted;
352 int delta = end - start + 1;
353 #ifdef DEBUG_SPAN_UPDATE
354 qDebug("Before");
355 #endif
356 for (SpanList::iterator it = spans.begin(); it != spans.end(); ) {
357 Span *span = *it;
358 #ifdef DEBUG_SPAN_UPDATE
359 qDebug() << span << *span;
360 #endif
361 if (span->m_bottom < start) {
362 ++it;
363 continue;
364 }
365 if (span->m_top < start) {
366 if (span->m_bottom <= end)
367 span->m_bottom = start - 1;
368 else
369 span->m_bottom -= delta;
370 } else {
371 if (span->m_bottom > end) {
372 if (span->m_top <= end)
373 span->m_top = start;
374 else
375 span->m_top -= delta;
376 span->m_bottom -= delta;
377 } else {
378 span->will_be_deleted = true;
379 }
380 }
381 if (span->m_top == span->m_bottom && span->m_left == span->m_right)
382 span->will_be_deleted = true;
383 if (span->will_be_deleted) {
384 spansToBeDeleted.append(span);
385 it = spans.erase(it);
386 } else {
387 ++it;
388 }
389 }
390
391 #ifdef DEBUG_SPAN_UPDATE
392 qDebug("After");
393 foreach (QSpanCollection::Span *span, spans)
394 qDebug() << span << *span;
395 #endif
396 if (spans.isEmpty()) {
397 qDeleteAll(spansToBeDeleted);
398 index.clear();
399 return;
400 }
401
402 Index::iterator it_y = index.end();
403 do {
404 --it_y;
405 int y = -it_y.key();
406 SubIndex &subindex = it_y.value();
407 if (y < start) {
408 if (cleanSpanSubIndex(subindex, y))
409 it_y = index.erase(it_y);
410 } else if (y >= start && y <= end) {
411 bool span_at_start = false;
412 SubIndex spansToBeMoved;
413 for (SubIndex::iterator it = subindex.begin(); it != subindex.end(); ++it) {
414 Span *span = it.value();
415 if (span->will_be_deleted)
416 continue;
417 if (!span_at_start && span->m_top == start)
418 span_at_start = true;
419 spansToBeMoved.insert(it.key(), span);
420 }
421
422 if (y == start && span_at_start)
423 subindex.clear();
424 else
425 it_y = index.erase(it_y);
426
427 if (span_at_start) {
428 Index::iterator it_start;
429 if (y == start)
430 it_start = it_y;
431 else {
432 it_start = index.find(-start);
433 if (it_start == index.end())
434 it_start = index.insert(-start, SubIndex());
435 }
436 SubIndex &start_subindex = it_start.value();
437 for (SubIndex::iterator it = spansToBeMoved.begin(); it != spansToBeMoved.end(); ++it)
438 start_subindex.insert(it.key(), it.value());
439 }
440 } else {
441 if (y == end + 1) {
442 Index::iterator it_top = index.find(-y + delta);
443 if (it_top == index.end())
444 it_top = index.insert(-y + delta, SubIndex());
445 for (SubIndex::iterator it = subindex.begin(); it != subindex.end(); ) {
446 Span *span = it.value();
447 if (!span->will_be_deleted)
448 it_top.value().insert(it.key(), span);
449 ++it;
450 }
451 } else {
452 index.insert(-y + delta, subindex);
453 }
454 it_y = index.erase(it_y);
455 }
456 } while (it_y != index.begin());
457
458 #ifdef DEBUG_SPAN_UPDATE
459 qDebug() << index;
460 qDebug("Deleted");
461 foreach (QSpanCollection::Span *span, spansToBeDeleted)
462 qDebug() << span << *span;
463 #endif
464 qDeleteAll(spansToBeDeleted);
465 }
466
467 /** \internal
468 * Updates the span collection after column removal.
469 */
updateRemovedColumns(int start,int end)470 void QSpanCollection::updateRemovedColumns(int start, int end)
471 {
472 #ifdef DEBUG_SPAN_UPDATE
473 qDebug() << Q_FUNC_INFO;
474 qDebug() << start << end;
475 qDebug() << index;
476 #endif
477 if (spans.isEmpty())
478 return;
479
480 SpanList toBeDeleted;
481 int delta = end - start + 1;
482 #ifdef DEBUG_SPAN_UPDATE
483 qDebug("Before");
484 #endif
485 for (SpanList::iterator it = spans.begin(); it != spans.end(); ) {
486 Span *span = *it;
487 #ifdef DEBUG_SPAN_UPDATE
488 qDebug() << span << *span;
489 #endif
490 if (span->m_right < start) {
491 ++it;
492 continue;
493 }
494 if (span->m_left < start) {
495 if (span->m_right <= end)
496 span->m_right = start - 1;
497 else
498 span->m_right -= delta;
499 } else {
500 if (span->m_right > end) {
501 if (span->m_left <= end)
502 span->m_left = start;
503 else
504 span->m_left -= delta;
505 span->m_right -= delta;
506 } else {
507 span->will_be_deleted = true;
508 }
509 }
510 if (span->m_top == span->m_bottom && span->m_left == span->m_right)
511 span->will_be_deleted = true;
512 if (span->will_be_deleted) {
513 toBeDeleted.append(span);
514 it = spans.erase(it);
515 } else {
516 ++it;
517 }
518 }
519
520 #ifdef DEBUG_SPAN_UPDATE
521 qDebug("After");
522 foreach (QSpanCollection::Span *span, spans)
523 qDebug() << span << *span;
524 #endif
525 if (spans.isEmpty()) {
526 qDeleteAll(toBeDeleted);
527 index.clear();
528 return;
529 }
530
531 for (Index::iterator it_y = index.begin(); it_y != index.end(); ) {
532 int y = -it_y.key();
533 if (cleanSpanSubIndex(it_y.value(), y, true))
534 it_y = index.erase(it_y);
535 else
536 ++it_y;
537 }
538
539 #ifdef DEBUG_SPAN_UPDATE
540 qDebug() << index;
541 qDebug("Deleted");
542 foreach (QSpanCollection::Span *span, toBeDeleted)
543 qDebug() << span << *span;
544 #endif
545 qDeleteAll(toBeDeleted);
546 }
547
548 #ifdef QT_BUILD_INTERNAL
549 /*!
550 \internal
551 Checks whether the span index structure is self-consistent, and consistent with the spans list.
552 */
checkConsistency() const553 bool QSpanCollection::checkConsistency() const
554 {
555 for (Index::const_iterator it_y = index.begin(); it_y != index.end(); ++it_y) {
556 int y = -it_y.key();
557 const SubIndex &subIndex = it_y.value();
558 for (SubIndex::const_iterator it = subIndex.begin(); it != subIndex.end(); ++it) {
559 int x = -it.key();
560 Span *span = it.value();
561 if (!spans.contains(span) || span->left() != x
562 || y < span->top() || y > span->bottom())
563 return false;
564 }
565 }
566
567 foreach (const Span *span, spans) {
568 if (span->width() < 1 || span->height() < 1
569 || (span->width() == 1 && span->height() == 1))
570 return false;
571 for (int y = span->top(); y <= span->bottom(); ++y) {
572 Index::const_iterator it_y = index.find(-y);
573 if (it_y == index.end()) {
574 if (y == span->top())
575 return false;
576 else
577 continue;
578 }
579 const SubIndex &subIndex = it_y.value();
580 SubIndex::const_iterator it = subIndex.find(-span->left());
581 if (it == subIndex.end() || it.value() != span)
582 return false;
583 }
584 }
585 return true;
586 }
587 #endif
588
589 class QTableCornerButton : public QAbstractButton
590 {
591 Q_OBJECT
592 public:
QTableCornerButton(QWidget * parent)593 QTableCornerButton(QWidget *parent) : QAbstractButton(parent) {}
paintEvent(QPaintEvent *)594 void paintEvent(QPaintEvent*) {
595 QStyleOptionHeader opt;
596 opt.init(this);
597 QStyle::State state = QStyle::State_None;
598 if (isEnabled())
599 state |= QStyle::State_Enabled;
600 if (isActiveWindow())
601 state |= QStyle::State_Active;
602 if (isDown())
603 state |= QStyle::State_Sunken;
604 opt.state = state;
605 opt.rect = rect();
606 opt.position = QStyleOptionHeader::OnlyOneSection;
607 QPainter painter(this);
608 style()->drawControl(QStyle::CE_Header, &opt, &painter, this);
609 }
610 };
611
init()612 void QTableViewPrivate::init()
613 {
614 Q_Q(QTableView);
615
616 q->setEditTriggers(editTriggers|QAbstractItemView::AnyKeyPressed);
617
618 QHeaderView *vertical = new QHeaderView(Qt::Vertical, q);
619 vertical->setClickable(true);
620 vertical->setHighlightSections(true);
621 q->setVerticalHeader(vertical);
622
623 QHeaderView *horizontal = new QHeaderView(Qt::Horizontal, q);
624 horizontal->setClickable(true);
625 horizontal->setHighlightSections(true);
626 q->setHorizontalHeader(horizontal);
627
628 tabKeyNavigation = true;
629
630 cornerWidget = new QTableCornerButton(q);
631 cornerWidget->setFocusPolicy(Qt::NoFocus);
632 QObject::connect(cornerWidget, SIGNAL(clicked()), q, SLOT(selectAll()));
633 }
634
635 /*!
636 \internal
637 Trims away indices that are hidden in the treeview due to hidden horizontal or vertical sections.
638 */
trimHiddenSelections(QItemSelectionRange * range) const639 void QTableViewPrivate::trimHiddenSelections(QItemSelectionRange *range) const
640 {
641 Q_ASSERT(range && range->isValid());
642
643 int top = range->top();
644 int left = range->left();
645 int bottom = range->bottom();
646 int right = range->right();
647
648 while (bottom >= top && verticalHeader->isSectionHidden(bottom))
649 --bottom;
650 while (right >= left && horizontalHeader->isSectionHidden(right))
651 --right;
652
653 if (top > bottom || left > right) { // everything is hidden
654 *range = QItemSelectionRange();
655 return;
656 }
657
658 while (verticalHeader->isSectionHidden(top) && top <= bottom)
659 ++top;
660 while (horizontalHeader->isSectionHidden(left) && left <= right)
661 ++left;
662
663 if (top > bottom || left > right) { // everything is hidden
664 *range = QItemSelectionRange();
665 return;
666 }
667
668 QModelIndex bottomRight = model->index(bottom, right, range->parent());
669 QModelIndex topLeft = model->index(top, left, range->parent());
670 *range = QItemSelectionRange(topLeft, bottomRight);
671 }
672
673 /*!
674 \internal
675 Sets the span for the cell at (\a row, \a column).
676 */
setSpan(int row,int column,int rowSpan,int columnSpan)677 void QTableViewPrivate::setSpan(int row, int column, int rowSpan, int columnSpan)
678 {
679 if (row < 0 || column < 0 || rowSpan <= 0 || columnSpan <= 0) {
680 qWarning() << "QTableView::setSpan: invalid span given: (" << row << ',' << column << ',' << rowSpan << ',' << columnSpan << ')';
681 return;
682 }
683 QSpanCollection::Span *sp = spans.spanAt(column, row);
684 if (sp) {
685 if (sp->top() != row || sp->left() != column) {
686 qWarning() << "QTableView::setSpan: span cannot overlap";
687 return;
688 }
689 if (rowSpan == 1 && columnSpan == 1) {
690 rowSpan = columnSpan = 0;
691 }
692 const int old_height = sp->height();
693 sp->m_bottom = row + rowSpan - 1;
694 sp->m_right = column + columnSpan - 1;
695 spans.updateSpan(sp, old_height);
696 return;
697 } else if (rowSpan == 1 && columnSpan == 1) {
698 qWarning() << "QTableView::setSpan: single cell span won't be added";
699 return;
700 }
701 sp = new QSpanCollection::Span(row, column, rowSpan, columnSpan);
702 spans.addSpan(sp);
703 }
704
705 /*!
706 \internal
707 Gets the span information for the cell at (\a row, \a column).
708 */
span(int row,int column) const709 QSpanCollection::Span QTableViewPrivate::span(int row, int column) const
710 {
711 QSpanCollection::Span *sp = spans.spanAt(column, row);
712 if (sp)
713 return *sp;
714
715 return QSpanCollection::Span(row, column, 1, 1);
716 }
717
718 /*!
719 \internal
720 Returns the logical index of the last section that's part of the span.
721 */
sectionSpanEndLogical(const QHeaderView * header,int logical,int span) const722 int QTableViewPrivate::sectionSpanEndLogical(const QHeaderView *header, int logical, int span) const
723 {
724 int visual = header->visualIndex(logical);
725 for (int i = 1; i < span; ) {
726 if (++visual >= header->count())
727 break;
728 logical = header->logicalIndex(visual);
729 ++i;
730 }
731 return logical;
732 }
733
734 /*!
735 \internal
736 Returns the size of the span starting at logical index \a logical
737 and spanning \a span sections.
738 */
sectionSpanSize(const QHeaderView * header,int logical,int span) const739 int QTableViewPrivate::sectionSpanSize(const QHeaderView *header, int logical, int span) const
740 {
741 int endLogical = sectionSpanEndLogical(header, logical, span);
742 return header->sectionPosition(endLogical)
743 - header->sectionPosition(logical)
744 + header->sectionSize(endLogical);
745 }
746
747 /*!
748 \internal
749 Returns true if the section at logical index \a logical is part of the span
750 starting at logical index \a spanLogical and spanning \a span sections;
751 otherwise, returns false.
752 */
spanContainsSection(const QHeaderView * header,int logical,int spanLogical,int span) const753 bool QTableViewPrivate::spanContainsSection(const QHeaderView *header, int logical, int spanLogical, int span) const
754 {
755 if (logical == spanLogical)
756 return true; // it's the start of the span
757 int visual = header->visualIndex(spanLogical);
758 for (int i = 1; i < span; ) {
759 if (++visual >= header->count())
760 break;
761 spanLogical = header->logicalIndex(visual);
762 if (logical == spanLogical)
763 return true;
764 ++i;
765 }
766 return false;
767 }
768
769 /*!
770 \internal
771 Returns the visual rect for the given \a span.
772 */
visualSpanRect(const QSpanCollection::Span & span) const773 QRect QTableViewPrivate::visualSpanRect(const QSpanCollection::Span &span) const
774 {
775 Q_Q(const QTableView);
776 // vertical
777 int row = span.top();
778 int rowp = verticalHeader->sectionViewportPosition(row);
779 int rowh = rowSpanHeight(row, span.height());
780 // horizontal
781 int column = span.left();
782 int colw = columnSpanWidth(column, span.width());
783 if (q->isRightToLeft())
784 column = span.right();
785 int colp = horizontalHeader->sectionViewportPosition(column);
786
787 const int i = showGrid ? 1 : 0;
788 if (q->isRightToLeft())
789 return QRect(colp + i, rowp, colw - i, rowh - i);
790 return QRect(colp, rowp, colw - i, rowh - i);
791 }
792
793 /*!
794 \internal
795 Draws the spanning cells within rect \a area, and clips them off as
796 preparation for the main drawing loop.
797 \a drawn is a QBitArray of visualRowCountxvisualCoulumnCount which say if particular cell has been drawn
798 */
drawAndClipSpans(const QRegion & area,QPainter * painter,const QStyleOptionViewItemV4 & option,QBitArray * drawn,int firstVisualRow,int lastVisualRow,int firstVisualColumn,int lastVisualColumn)799 void QTableViewPrivate::drawAndClipSpans(const QRegion &area, QPainter *painter,
800 const QStyleOptionViewItemV4 &option, QBitArray *drawn,
801 int firstVisualRow, int lastVisualRow, int firstVisualColumn, int lastVisualColumn)
802 {
803 bool alternateBase = false;
804 QRegion region = viewport->rect();
805
806 QList<QSpanCollection::Span *> visibleSpans;
807 bool sectionMoved = verticalHeader->sectionsMoved() || horizontalHeader->sectionsMoved();
808
809 if (!sectionMoved) {
810 visibleSpans = spans.spansInRect(logicalColumn(firstVisualColumn), logicalRow(firstVisualRow),
811 lastVisualColumn - firstVisualColumn + 1, lastVisualRow - firstVisualRow + 1);
812 } else {
813 QSet<QSpanCollection::Span *> set;
814 for(int x = firstVisualColumn; x <= lastVisualColumn; x++)
815 for(int y = firstVisualRow; y <= lastVisualRow; y++)
816 set.insert(spans.spanAt(x,y));
817 set.remove(0);
818 visibleSpans = set.toList();
819 }
820
821 foreach (QSpanCollection::Span *span, visibleSpans) {
822 int row = span->top();
823 int col = span->left();
824 QModelIndex index = model->index(row, col, root);
825 if (!index.isValid())
826 continue;
827 QRect rect = visualSpanRect(*span);
828 rect.translate(scrollDelayOffset);
829 if (!area.intersects(rect))
830 continue;
831 QStyleOptionViewItemV4 opt = option;
832 opt.rect = rect;
833 alternateBase = alternatingColors && (span->top() & 1);
834 if (alternateBase)
835 opt.features |= QStyleOptionViewItemV2::Alternate;
836 else
837 opt.features &= ~QStyleOptionViewItemV2::Alternate;
838 drawCell(painter, opt, index);
839 region -= rect;
840 for (int r = span->top(); r <= span->bottom(); ++r) {
841 const int vr = visualRow(r);
842 if (vr < firstVisualRow || vr > lastVisualRow)
843 continue;
844 for (int c = span->left(); c <= span->right(); ++c) {
845 const int vc = visualColumn(c);
846 if (vc < firstVisualColumn || vc > lastVisualColumn)
847 continue;
848 drawn->setBit((vr - firstVisualRow) * (lastVisualColumn - firstVisualColumn + 1)
849 + vc - firstVisualColumn);
850 }
851 }
852
853 }
854 painter->setClipRegion(region);
855 }
856
857 /*!
858 \internal
859 Updates spans after row insertion.
860 */
_q_updateSpanInsertedRows(const QModelIndex & parent,int start,int end)861 void QTableViewPrivate::_q_updateSpanInsertedRows(const QModelIndex &parent, int start, int end)
862 {
863 Q_UNUSED(parent)
864 spans.updateInsertedRows(start, end);
865 }
866
867 /*!
868 \internal
869 Updates spans after column insertion.
870 */
_q_updateSpanInsertedColumns(const QModelIndex & parent,int start,int end)871 void QTableViewPrivate::_q_updateSpanInsertedColumns(const QModelIndex &parent, int start, int end)
872 {
873 Q_UNUSED(parent)
874 spans.updateInsertedColumns(start, end);
875 }
876
877 /*!
878 \internal
879 Updates spans after row removal.
880 */
_q_updateSpanRemovedRows(const QModelIndex & parent,int start,int end)881 void QTableViewPrivate::_q_updateSpanRemovedRows(const QModelIndex &parent, int start, int end)
882 {
883 Q_UNUSED(parent)
884 spans.updateRemovedRows(start, end);
885 }
886
887 /*!
888 \internal
889 Updates spans after column removal.
890 */
_q_updateSpanRemovedColumns(const QModelIndex & parent,int start,int end)891 void QTableViewPrivate::_q_updateSpanRemovedColumns(const QModelIndex &parent, int start, int end)
892 {
893 Q_UNUSED(parent)
894 spans.updateRemovedColumns(start, end);
895 }
896
897 /*!
898 \internal
899 Draws a table cell.
900 */
drawCell(QPainter * painter,const QStyleOptionViewItemV4 & option,const QModelIndex & index)901 void QTableViewPrivate::drawCell(QPainter *painter, const QStyleOptionViewItemV4 &option, const QModelIndex &index)
902 {
903 Q_Q(QTableView);
904 QStyleOptionViewItemV4 opt = option;
905
906 if (selectionModel && selectionModel->isSelected(index))
907 opt.state |= QStyle::State_Selected;
908 if (index == hover)
909 opt.state |= QStyle::State_MouseOver;
910 if (option.state & QStyle::State_Enabled) {
911 QPalette::ColorGroup cg;
912 if ((model->flags(index) & Qt::ItemIsEnabled) == 0) {
913 opt.state &= ~QStyle::State_Enabled;
914 cg = QPalette::Disabled;
915 } else {
916 cg = QPalette::Normal;
917 }
918 opt.palette.setCurrentColorGroup(cg);
919 }
920
921 if (index == q->currentIndex()) {
922 const bool focus = (q->hasFocus() || viewport->hasFocus()) && q->currentIndex().isValid();
923 if (focus)
924 opt.state |= QStyle::State_HasFocus;
925 }
926
927 q->style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, q);
928
929 q->itemDelegate(index)->paint(painter, opt, index);
930 }
931
932 /*!
933 \class QTableView
934
935 \brief The QTableView class provides a default model/view
936 implementation of a table view.
937
938 \ingroup model-view
939 \ingroup advanced
940
941
942 A QTableView implements a table view that displays items from a
943 model. This class is used to provide standard tables that were
944 previously provided by the QTable class, but using the more
945 flexible approach provided by Qt's model/view architecture.
946
947 The QTableView class is one of the \l{Model/View Classes}
948 and is part of Qt's \l{Model/View Programming}{model/view framework}.
949
950 QTableView implements the interfaces defined by the
951 QAbstractItemView class to allow it to display data provided by
952 models derived from the QAbstractItemModel class.
953
954 \section1 Navigation
955
956 You can navigate the cells in the table by clicking on a cell with the
957 mouse, or by using the arrow keys. Because QTableView enables
958 \l{QAbstractItemView::tabKeyNavigation}{tabKeyNavigation} by default, you
959 can also hit Tab and Backtab to move from cell to cell.
960
961 \section1 Visual Appearance
962
963 The table has a vertical header that can be obtained using the
964 verticalHeader() function, and a horizontal header that is available
965 through the horizontalHeader() function. The height of each row in the
966 table can be found by using rowHeight(); similarly, the width of
967 columns can be found using columnWidth(). Since both of these are plain
968 widgets, you can hide either of them using their hide() functions.
969
970 Rows and columns can be hidden and shown with hideRow(), hideColumn(),
971 showRow(), and showColumn(). They can be selected with selectRow()
972 and selectColumn(). The table will show a grid depending on the
973 \l showGrid property.
974
975 The items shown in a table view, like those in the other item views, are
976 rendered and edited using standard \l{QItemDelegate}{delegates}. However,
977 for some tasks it is sometimes useful to be able to insert widgets in a
978 table instead. Widgets are set for particular indexes with the
979 \l{QAbstractItemView::}{setIndexWidget()} function, and
980 later retrieved with \l{QAbstractItemView::}{indexWidget()}.
981
982 \table
983 \row \o \inlineimage qtableview-resized.png
984 \o By default, the cells in a table do not expand to fill the available space.
985
986 You can make the cells fill the available space by stretching the last
987 header section. Access the relevant header using horizontalHeader()
988 or verticalHeader() and set the header's \l{QHeaderView::}{stretchLastSection}
989 property.
990
991 To distribute the available space according to the space requirement of
992 each column or row, call the view's resizeColumnsToContents() or
993 resizeRowsToContents() functions.
994 \endtable
995
996 \section1 Coordinate Systems
997
998 For some specialized forms of tables it is useful to be able to
999 convert between row and column indexes and widget coordinates.
1000 The rowAt() function provides the y-coordinate within the view of the
1001 specified row; the row index can be used to obtain a corresponding
1002 y-coordinate with rowViewportPosition(). The columnAt() and
1003 columnViewportPosition() functions provide the equivalent conversion
1004 operations between x-coordinates and column indexes.
1005
1006 \section1 Styles
1007
1008 QTableView is styled appropriately for each platform. The following images show
1009 how it looks on three different platforms. Go to the \l{Qt Widget Gallery} to see
1010 its appearance in other styles.
1011
1012 \table 100%
1013 \row \o \inlineimage windowsxp-tableview.png Screenshot of a Windows XP style table view
1014 \o \inlineimage macintosh-tableview.png Screenshot of a Macintosh style table view
1015 \o \inlineimage plastique-tableview.png Screenshot of a Plastique style table view
1016 \row \o A \l{Windows XP Style Widget Gallery}{Windows XP style} table view.
1017 \o A \l{Macintosh Style Widget Gallery}{Macintosh style} table view.
1018 \o A \l{Plastique Style Widget Gallery}{Plastique style} table view.
1019 \endtable
1020
1021 \sa QTableWidget, {View Classes}, QAbstractItemModel, QAbstractItemView,
1022 {Chart Example}, {Pixelator Example}, {Table Model Example}
1023 */
1024
1025 /*!
1026 Constructs a table view with a \a parent to represent the data.
1027
1028 \sa QAbstractItemModel
1029 */
1030
QTableView(QWidget * parent)1031 QTableView::QTableView(QWidget *parent)
1032 : QAbstractItemView(*new QTableViewPrivate, parent)
1033 {
1034 Q_D(QTableView);
1035 d->init();
1036 }
1037
1038 /*!
1039 \internal
1040 */
QTableView(QTableViewPrivate & dd,QWidget * parent)1041 QTableView::QTableView(QTableViewPrivate &dd, QWidget *parent)
1042 : QAbstractItemView(dd, parent)
1043 {
1044 Q_D(QTableView);
1045 d->init();
1046 }
1047
1048 /*!
1049 Destroys the table view.
1050 */
~QTableView()1051 QTableView::~QTableView()
1052 {
1053 }
1054
1055 /*!
1056 \reimp
1057 */
setModel(QAbstractItemModel * model)1058 void QTableView::setModel(QAbstractItemModel *model)
1059 {
1060 Q_D(QTableView);
1061 if (model == d->model)
1062 return;
1063 //let's disconnect from the old model
1064 if (d->model && d->model != QAbstractItemModelPrivate::staticEmptyModel()) {
1065 disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
1066 this, SLOT(_q_updateSpanInsertedRows(QModelIndex,int,int)));
1067 disconnect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)),
1068 this, SLOT(_q_updateSpanInsertedColumns(QModelIndex,int,int)));
1069 disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
1070 this, SLOT(_q_updateSpanRemovedRows(QModelIndex,int,int)));
1071 disconnect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
1072 this, SLOT(_q_updateSpanRemovedColumns(QModelIndex,int,int)));
1073 }
1074 if (model) { //and connect to the new one
1075 connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
1076 this, SLOT(_q_updateSpanInsertedRows(QModelIndex,int,int)));
1077 connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)),
1078 this, SLOT(_q_updateSpanInsertedColumns(QModelIndex,int,int)));
1079 connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
1080 this, SLOT(_q_updateSpanRemovedRows(QModelIndex,int,int)));
1081 connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
1082 this, SLOT(_q_updateSpanRemovedColumns(QModelIndex,int,int)));
1083 }
1084 d->verticalHeader->setModel(model);
1085 d->horizontalHeader->setModel(model);
1086 QAbstractItemView::setModel(model);
1087 }
1088
1089 /*!
1090 \reimp
1091 */
setRootIndex(const QModelIndex & index)1092 void QTableView::setRootIndex(const QModelIndex &index)
1093 {
1094 Q_D(QTableView);
1095 if (index == d->root) {
1096 viewport()->update();
1097 return;
1098 }
1099 d->verticalHeader->setRootIndex(index);
1100 d->horizontalHeader->setRootIndex(index);
1101 QAbstractItemView::setRootIndex(index);
1102 }
1103
1104 /*!
1105 \internal
1106 */
doItemsLayout()1107 void QTableView::doItemsLayout()
1108 {
1109 Q_D(QTableView);
1110 QAbstractItemView::doItemsLayout();
1111 if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
1112 const int max = verticalScrollBar()->maximum();
1113 if (max > 0 && verticalScrollBar()->value() == max)
1114 d->verticalHeader->setOffsetToLastSection();
1115 else
1116 d->verticalHeader->setOffsetToSectionPosition(verticalScrollBar()->value());
1117 } else {
1118 d->verticalHeader->setOffset(verticalScrollBar()->value());
1119 }
1120 if (!d->verticalHeader->updatesEnabled())
1121 d->verticalHeader->setUpdatesEnabled(true);
1122 }
1123
1124 /*!
1125 \reimp
1126 */
setSelectionModel(QItemSelectionModel * selectionModel)1127 void QTableView::setSelectionModel(QItemSelectionModel *selectionModel)
1128 {
1129 Q_D(QTableView);
1130 Q_ASSERT(selectionModel);
1131 d->verticalHeader->setSelectionModel(selectionModel);
1132 d->horizontalHeader->setSelectionModel(selectionModel);
1133 QAbstractItemView::setSelectionModel(selectionModel);
1134 }
1135
1136 /*!
1137 Returns the table view's horizontal header.
1138
1139 \sa setHorizontalHeader(), verticalHeader(), QAbstractItemModel::headerData()
1140 */
horizontalHeader() const1141 QHeaderView *QTableView::horizontalHeader() const
1142 {
1143 Q_D(const QTableView);
1144 return d->horizontalHeader;
1145 }
1146
1147 /*!
1148 Returns the table view's vertical header.
1149
1150 \sa setVerticalHeader(), horizontalHeader(), QAbstractItemModel::headerData()
1151 */
verticalHeader() const1152 QHeaderView *QTableView::verticalHeader() const
1153 {
1154 Q_D(const QTableView);
1155 return d->verticalHeader;
1156 }
1157
1158 /*!
1159 Sets the widget to use for the horizontal header to \a header.
1160
1161 \sa horizontalHeader() setVerticalHeader()
1162 */
setHorizontalHeader(QHeaderView * header)1163 void QTableView::setHorizontalHeader(QHeaderView *header)
1164 {
1165 Q_D(QTableView);
1166
1167 if (!header || header == d->horizontalHeader)
1168 return;
1169 if (d->horizontalHeader && d->horizontalHeader->parent() == this)
1170 delete d->horizontalHeader;
1171 d->horizontalHeader = header;
1172 d->horizontalHeader->setParent(this);
1173 if (!d->horizontalHeader->model()) {
1174 d->horizontalHeader->setModel(d->model);
1175 if (d->selectionModel)
1176 d->horizontalHeader->setSelectionModel(d->selectionModel);
1177 }
1178
1179 connect(d->horizontalHeader,SIGNAL(sectionResized(int,int,int)),
1180 this, SLOT(columnResized(int,int,int)));
1181 connect(d->horizontalHeader, SIGNAL(sectionMoved(int,int,int)),
1182 this, SLOT(columnMoved(int,int,int)));
1183 connect(d->horizontalHeader, SIGNAL(sectionCountChanged(int,int)),
1184 this, SLOT(columnCountChanged(int,int)));
1185 connect(d->horizontalHeader, SIGNAL(sectionPressed(int)), this, SLOT(selectColumn(int)));
1186 connect(d->horizontalHeader, SIGNAL(sectionEntered(int)), this, SLOT(_q_selectColumn(int)));
1187 connect(d->horizontalHeader, SIGNAL(sectionHandleDoubleClicked(int)),
1188 this, SLOT(resizeColumnToContents(int)));
1189 connect(d->horizontalHeader, SIGNAL(geometriesChanged()), this, SLOT(updateGeometries()));
1190
1191 //update the sorting enabled states on the new header
1192 setSortingEnabled(d->sortingEnabled);
1193 }
1194
1195 /*!
1196 Sets the widget to use for the vertical header to \a header.
1197
1198 \sa verticalHeader() setHorizontalHeader()
1199 */
setVerticalHeader(QHeaderView * header)1200 void QTableView::setVerticalHeader(QHeaderView *header)
1201 {
1202 Q_D(QTableView);
1203
1204 if (!header || header == d->verticalHeader)
1205 return;
1206 if (d->verticalHeader && d->verticalHeader->parent() == this)
1207 delete d->verticalHeader;
1208 d->verticalHeader = header;
1209 d->verticalHeader->setParent(this);
1210 if (!d->verticalHeader->model()) {
1211 d->verticalHeader->setModel(d->model);
1212 if (d->selectionModel)
1213 d->verticalHeader->setSelectionModel(d->selectionModel);
1214 }
1215
1216 connect(d->verticalHeader, SIGNAL(sectionResized(int,int,int)),
1217 this, SLOT(rowResized(int,int,int)));
1218 connect(d->verticalHeader, SIGNAL(sectionMoved(int,int,int)),
1219 this, SLOT(rowMoved(int,int,int)));
1220 connect(d->verticalHeader, SIGNAL(sectionCountChanged(int,int)),
1221 this, SLOT(rowCountChanged(int,int)));
1222 connect(d->verticalHeader, SIGNAL(sectionPressed(int)), this, SLOT(selectRow(int)));
1223 connect(d->verticalHeader, SIGNAL(sectionEntered(int)), this, SLOT(_q_selectRow(int)));
1224 connect(d->verticalHeader, SIGNAL(sectionHandleDoubleClicked(int)),
1225 this, SLOT(resizeRowToContents(int)));
1226 connect(d->verticalHeader, SIGNAL(geometriesChanged()), this, SLOT(updateGeometries()));
1227 }
1228
1229 /*!
1230 \internal
1231
1232 Scroll the contents of the table view by (\a dx, \a dy).
1233 */
scrollContentsBy(int dx,int dy)1234 void QTableView::scrollContentsBy(int dx, int dy)
1235 {
1236 Q_D(QTableView);
1237
1238 d->delayedAutoScroll.stop(); // auto scroll was canceled by the user scrolling
1239
1240 dx = isRightToLeft() ? -dx : dx;
1241 if (dx) {
1242 if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) {
1243 int oldOffset = d->horizontalHeader->offset();
1244 if (horizontalScrollBar()->value() == horizontalScrollBar()->maximum())
1245 d->horizontalHeader->setOffsetToLastSection();
1246 else
1247 d->horizontalHeader->setOffsetToSectionPosition(horizontalScrollBar()->value());
1248 int newOffset = d->horizontalHeader->offset();
1249 dx = isRightToLeft() ? newOffset - oldOffset : oldOffset - newOffset;
1250 } else {
1251 d->horizontalHeader->setOffset(horizontalScrollBar()->value());
1252 }
1253 }
1254 if (dy) {
1255 if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
1256 int oldOffset = d->verticalHeader->offset();
1257 if (verticalScrollBar()->value() == verticalScrollBar()->maximum())
1258 d->verticalHeader->setOffsetToLastSection();
1259 else
1260 d->verticalHeader->setOffsetToSectionPosition(verticalScrollBar()->value());
1261 int newOffset = d->verticalHeader->offset();
1262 dy = oldOffset - newOffset;
1263 } else {
1264 d->verticalHeader->setOffset(verticalScrollBar()->value());
1265 }
1266 }
1267 d->scrollContentsBy(dx, dy);
1268
1269 if (d->showGrid) {
1270 //we need to update the first line of the previous top item in the view
1271 //because it has the grid drawn if the header is invisible.
1272 //It is strictly related to what's done at then end of the paintEvent
1273 if (dy > 0 && d->horizontalHeader->isHidden() && d->verticalScrollMode == ScrollPerItem) {
1274 d->viewport->update(0, dy, d->viewport->width(), dy);
1275 }
1276 if (dx > 0 && d->verticalHeader->isHidden() && d->horizontalScrollMode == ScrollPerItem) {
1277 d->viewport->update(dx, 0, dx, d->viewport->height());
1278 }
1279 }
1280 }
1281
1282 /*!
1283 \reimp
1284 */
viewOptions() const1285 QStyleOptionViewItem QTableView::viewOptions() const
1286 {
1287 QStyleOptionViewItem option = QAbstractItemView::viewOptions();
1288 option.showDecorationSelected = true;
1289 return option;
1290 }
1291
1292 /*!
1293 Paints the table on receipt of the given paint event \a event.
1294 */
paintEvent(QPaintEvent * event)1295 void QTableView::paintEvent(QPaintEvent *event)
1296 {
1297 Q_D(QTableView);
1298 // setup temp variables for the painting
1299 QStyleOptionViewItemV4 option = d->viewOptionsV4();
1300 const QPoint offset = d->scrollDelayOffset;
1301 const bool showGrid = d->showGrid;
1302 const int gridSize = showGrid ? 1 : 0;
1303 const int gridHint = style()->styleHint(QStyle::SH_Table_GridLineColor, &option, this);
1304 const QColor gridColor = static_cast<QRgb>(gridHint);
1305 const QPen gridPen = QPen(gridColor, 0, d->gridStyle);
1306 const QHeaderView *verticalHeader = d->verticalHeader;
1307 const QHeaderView *horizontalHeader = d->horizontalHeader;
1308 const bool alternate = d->alternatingColors;
1309 const bool rightToLeft = isRightToLeft();
1310
1311 QPainter painter(d->viewport);
1312
1313 // if there's nothing to do, clear the area and return
1314 if (horizontalHeader->count() == 0 || verticalHeader->count() == 0 || !d->itemDelegate)
1315 return;
1316
1317 uint x = horizontalHeader->length() - horizontalHeader->offset() - (rightToLeft ? 0 : 1);
1318 uint y = verticalHeader->length() - verticalHeader->offset() - 1;
1319
1320 const QRegion region = event->region().translated(offset);
1321 const QVector<QRect> rects = region.rects();
1322
1323 //firstVisualRow is the visual index of the first visible row. lastVisualRow is the visual index of the last visible Row.
1324 //same goes for ...VisualColumn
1325 int firstVisualRow = qMax(verticalHeader->visualIndexAt(0),0);
1326 int lastVisualRow = verticalHeader->visualIndexAt(verticalHeader->viewport()->height());
1327 if (lastVisualRow == -1)
1328 lastVisualRow = d->model->rowCount(d->root) - 1;
1329
1330 int firstVisualColumn = horizontalHeader->visualIndexAt(0);
1331 int lastVisualColumn = horizontalHeader->visualIndexAt(horizontalHeader->viewport()->width());
1332 if (rightToLeft)
1333 qSwap(firstVisualColumn, lastVisualColumn);
1334 if (firstVisualColumn == -1)
1335 firstVisualColumn = 0;
1336 if (lastVisualColumn == -1)
1337 lastVisualColumn = horizontalHeader->count() - 1;
1338
1339 QBitArray drawn((lastVisualRow - firstVisualRow + 1) * (lastVisualColumn - firstVisualColumn + 1));
1340
1341 if (d->hasSpans()) {
1342 d->drawAndClipSpans(region, &painter, option, &drawn,
1343 firstVisualRow, lastVisualRow, firstVisualColumn, lastVisualColumn);
1344 }
1345
1346 for (int i = 0; i < rects.size(); ++i) {
1347 QRect dirtyArea = rects.at(i);
1348 dirtyArea.setBottom(qMin(dirtyArea.bottom(), int(y)));
1349 if (rightToLeft) {
1350 dirtyArea.setLeft(qMax(dirtyArea.left(), d->viewport->width() - int(x)));
1351 } else {
1352 dirtyArea.setRight(qMin(dirtyArea.right(), int(x)));
1353 }
1354
1355 // get the horizontal start and end visual sections
1356 int left = horizontalHeader->visualIndexAt(dirtyArea.left());
1357 int right = horizontalHeader->visualIndexAt(dirtyArea.right());
1358 if (rightToLeft)
1359 qSwap(left, right);
1360 if (left == -1) left = 0;
1361 if (right == -1) right = horizontalHeader->count() - 1;
1362
1363 // get the vertical start and end visual sections and if alternate color
1364 int bottom = verticalHeader->visualIndexAt(dirtyArea.bottom());
1365 if (bottom == -1) bottom = verticalHeader->count() - 1;
1366 int top = 0;
1367 bool alternateBase = false;
1368 if (alternate && verticalHeader->sectionsHidden()) {
1369 uint verticalOffset = verticalHeader->offset();
1370 int row = verticalHeader->logicalIndex(top);
1371 for (int y = 0;
1372 ((uint)(y += verticalHeader->sectionSize(top)) <= verticalOffset) && (top < bottom);
1373 ++top) {
1374 row = verticalHeader->logicalIndex(top);
1375 if (alternate && !verticalHeader->isSectionHidden(row))
1376 alternateBase = !alternateBase;
1377 }
1378 } else {
1379 top = verticalHeader->visualIndexAt(dirtyArea.top());
1380 alternateBase = (top & 1) && alternate;
1381 }
1382 if (top == -1 || top > bottom)
1383 continue;
1384
1385 // Paint each row item
1386 for (int visualRowIndex = top; visualRowIndex <= bottom; ++visualRowIndex) {
1387 int row = verticalHeader->logicalIndex(visualRowIndex);
1388 if (verticalHeader->isSectionHidden(row))
1389 continue;
1390 int rowY = rowViewportPosition(row);
1391 rowY += offset.y();
1392 int rowh = rowHeight(row) - gridSize;
1393
1394 // Paint each column item
1395 for (int visualColumnIndex = left; visualColumnIndex <= right; ++visualColumnIndex) {
1396 int currentBit = (visualRowIndex - firstVisualRow) * (lastVisualColumn - firstVisualColumn + 1)
1397 + visualColumnIndex - firstVisualColumn;
1398
1399 if (currentBit < 0 || currentBit >= drawn.size() || drawn.testBit(currentBit))
1400 continue;
1401 drawn.setBit(currentBit);
1402
1403 int col = horizontalHeader->logicalIndex(visualColumnIndex);
1404 if (horizontalHeader->isSectionHidden(col))
1405 continue;
1406 int colp = columnViewportPosition(col);
1407 colp += offset.x();
1408 int colw = columnWidth(col) - gridSize;
1409
1410 const QModelIndex index = d->model->index(row, col, d->root);
1411 if (index.isValid()) {
1412 option.rect = QRect(colp + (showGrid && rightToLeft ? 1 : 0), rowY, colw, rowh);
1413 if (alternate) {
1414 if (alternateBase)
1415 option.features |= QStyleOptionViewItemV2::Alternate;
1416 else
1417 option.features &= ~QStyleOptionViewItemV2::Alternate;
1418 }
1419 d->drawCell(&painter, option, index);
1420 }
1421 }
1422 alternateBase = !alternateBase && alternate;
1423 }
1424
1425 if (showGrid) {
1426 // Find the bottom right (the last rows/columns might be hidden)
1427 while (verticalHeader->isSectionHidden(verticalHeader->logicalIndex(bottom))) --bottom;
1428 QPen old = painter.pen();
1429 painter.setPen(gridPen);
1430 // Paint each row
1431 for (int visualIndex = top; visualIndex <= bottom; ++visualIndex) {
1432 int row = verticalHeader->logicalIndex(visualIndex);
1433 if (verticalHeader->isSectionHidden(row))
1434 continue;
1435 int rowY = rowViewportPosition(row);
1436 rowY += offset.y();
1437 int rowh = rowHeight(row) - gridSize;
1438 painter.drawLine(dirtyArea.left(), rowY + rowh, dirtyArea.right(), rowY + rowh);
1439 }
1440
1441 // Paint each column
1442 for (int h = left; h <= right; ++h) {
1443 int col = horizontalHeader->logicalIndex(h);
1444 if (horizontalHeader->isSectionHidden(col))
1445 continue;
1446 int colp = columnViewportPosition(col);
1447 colp += offset.x();
1448 if (!rightToLeft)
1449 colp += columnWidth(col) - gridSize;
1450 painter.drawLine(colp, dirtyArea.top(), colp, dirtyArea.bottom());
1451 }
1452
1453 //draw the top & left grid lines if the headers are not visible.
1454 //We do update this line when subsequent scroll happen (see scrollContentsBy)
1455 if (horizontalHeader->isHidden() && verticalScrollMode() == ScrollPerItem)
1456 painter.drawLine(dirtyArea.left(), 0, dirtyArea.right(), 0);
1457 if (verticalHeader->isHidden() && horizontalScrollMode() == ScrollPerItem)
1458 painter.drawLine(0, dirtyArea.top(), 0, dirtyArea.bottom());
1459 painter.setPen(old);
1460 }
1461 }
1462
1463 #ifndef QT_NO_DRAGANDDROP
1464 // Paint the dropIndicator
1465 d->paintDropIndicator(&painter);
1466 #endif
1467 }
1468
1469 /*!
1470 Returns the index position of the model item corresponding to the
1471 table item at position \a pos in contents coordinates.
1472 */
indexAt(const QPoint & pos) const1473 QModelIndex QTableView::indexAt(const QPoint &pos) const
1474 {
1475 Q_D(const QTableView);
1476 d->executePostedLayout();
1477 int r = rowAt(pos.y());
1478 int c = columnAt(pos.x());
1479 if (r >= 0 && c >= 0) {
1480 if (d->hasSpans()) {
1481 QSpanCollection::Span span = d->span(r, c);
1482 r = span.top();
1483 c = span.left();
1484 }
1485 return d->model->index(r, c, d->root);
1486 }
1487 return QModelIndex();
1488 }
1489
1490 /*!
1491 Returns the horizontal offset of the items in the table view.
1492
1493 Note that the table view uses the horizontal header section
1494 positions to determine the positions of columns in the view.
1495
1496 \sa verticalOffset()
1497 */
horizontalOffset() const1498 int QTableView::horizontalOffset() const
1499 {
1500 Q_D(const QTableView);
1501 return d->horizontalHeader->offset();
1502 }
1503
1504 /*!
1505 Returns the vertical offset of the items in the table view.
1506
1507 Note that the table view uses the vertical header section
1508 positions to determine the positions of rows in the view.
1509
1510 \sa horizontalOffset()
1511 */
verticalOffset() const1512 int QTableView::verticalOffset() const
1513 {
1514 Q_D(const QTableView);
1515 return d->verticalHeader->offset();
1516 }
1517
1518 /*!
1519 \fn QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
1520
1521 Moves the cursor in accordance with the given \a cursorAction, using the
1522 information provided by the \a modifiers.
1523
1524 \sa QAbstractItemView::CursorAction
1525 */
moveCursor(CursorAction cursorAction,Qt::KeyboardModifiers modifiers)1526 QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
1527 {
1528 Q_D(QTableView);
1529 Q_UNUSED(modifiers);
1530
1531 int bottom = d->model->rowCount(d->root) - 1;
1532 // make sure that bottom is the bottommost *visible* row
1533 while (bottom >= 0 && isRowHidden(d->logicalRow(bottom)))
1534 --bottom;
1535
1536 int right = d->model->columnCount(d->root) - 1;
1537
1538 while (right >= 0 && isColumnHidden(d->logicalColumn(right)))
1539 --right;
1540
1541 if (bottom == -1 || right == -1)
1542 return QModelIndex(); // model is empty
1543
1544 QModelIndex current = currentIndex();
1545
1546 if (!current.isValid()) {
1547 int row = 0;
1548 int column = 0;
1549 while (column < right && isColumnHidden(d->logicalColumn(column)))
1550 ++column;
1551 while (isRowHidden(d->logicalRow(row)) && row < bottom)
1552 ++row;
1553 d->visualCursor = QPoint(column, row);
1554 return d->model->index(d->logicalRow(row), d->logicalColumn(column), d->root);
1555 }
1556
1557 // Update visual cursor if current index has changed.
1558 QPoint visualCurrent(d->visualColumn(current.column()), d->visualRow(current.row()));
1559 if (visualCurrent != d->visualCursor) {
1560 if (d->hasSpans()) {
1561 QSpanCollection::Span span = d->span(current.row(), current.column());
1562 if (span.top() > d->visualCursor.y() || d->visualCursor.y() > span.bottom()
1563 || span.left() > d->visualCursor.x() || d->visualCursor.x() > span.right())
1564 d->visualCursor = visualCurrent;
1565 } else {
1566 d->visualCursor = visualCurrent;
1567 }
1568 }
1569
1570 int visualRow = d->visualCursor.y();
1571 if (visualRow > bottom)
1572 visualRow = bottom;
1573 Q_ASSERT(visualRow != -1);
1574 int visualColumn = d->visualCursor.x();
1575 if (visualColumn > right)
1576 visualColumn = right;
1577 Q_ASSERT(visualColumn != -1);
1578
1579 if (isRightToLeft()) {
1580 if (cursorAction == MoveLeft)
1581 cursorAction = MoveRight;
1582 else if (cursorAction == MoveRight)
1583 cursorAction = MoveLeft;
1584 }
1585
1586 switch (cursorAction) {
1587 case MoveUp: {
1588 int originalRow = visualRow;
1589 #ifdef QT_KEYPAD_NAVIGATION
1590 if (QApplication::keypadNavigationEnabled() && visualRow == 0)
1591 visualRow = d->visualRow(model()->rowCount() - 1) + 1;
1592 // FIXME? visualRow = bottom + 1;
1593 #endif
1594 int r = d->logicalRow(visualRow);
1595 int c = d->logicalColumn(visualColumn);
1596 if (r != -1 && d->hasSpans()) {
1597 QSpanCollection::Span span = d->span(r, c);
1598 if (span.width() > 1 || span.height() > 1)
1599 visualRow = d->visualRow(span.top());
1600 }
1601 while (visualRow >= 0) {
1602 --visualRow;
1603 r = d->logicalRow(visualRow);
1604 c = d->logicalColumn(visualColumn);
1605 if (r == -1 || (!isRowHidden(r) && d->isCellEnabled(r, c)))
1606 break;
1607 }
1608 if (visualRow < 0)
1609 visualRow = originalRow;
1610 break;
1611 }
1612 case MoveDown: {
1613 int originalRow = visualRow;
1614 if (d->hasSpans()) {
1615 QSpanCollection::Span span = d->span(current.row(), current.column());
1616 visualRow = d->visualRow(d->rowSpanEndLogical(span.top(), span.height()));
1617 }
1618 #ifdef QT_KEYPAD_NAVIGATION
1619 if (QApplication::keypadNavigationEnabled() && visualRow >= bottom)
1620 visualRow = -1;
1621 #endif
1622 int r = d->logicalRow(visualRow);
1623 int c = d->logicalColumn(visualColumn);
1624 if (r != -1 && d->hasSpans()) {
1625 QSpanCollection::Span span = d->span(r, c);
1626 if (span.width() > 1 || span.height() > 1)
1627 visualRow = d->visualRow(d->rowSpanEndLogical(span.top(), span.height()));
1628 }
1629 while (visualRow <= bottom) {
1630 ++visualRow;
1631 r = d->logicalRow(visualRow);
1632 c = d->logicalColumn(visualColumn);
1633 if (r == -1 || (!isRowHidden(r) && d->isCellEnabled(r, c)))
1634 break;
1635 }
1636 if (visualRow > bottom)
1637 visualRow = originalRow;
1638 break;
1639 }
1640 case MovePrevious:
1641 case MoveLeft: {
1642 int originalRow = visualRow;
1643 int originalColumn = visualColumn;
1644 bool firstTime = true;
1645 bool looped = false;
1646 bool wrapped = false;
1647 do {
1648 int r = d->logicalRow(visualRow);
1649 int c = d->logicalColumn(visualColumn);
1650 if (firstTime && c != -1 && d->hasSpans()) {
1651 firstTime = false;
1652 QSpanCollection::Span span = d->span(r, c);
1653 if (span.width() > 1 || span.height() > 1)
1654 visualColumn = d->visualColumn(span.left());
1655 }
1656 while (visualColumn >= 0) {
1657 --visualColumn;
1658 r = d->logicalRow(visualRow);
1659 c = d->logicalColumn(visualColumn);
1660 if (r == -1 || c == -1 || (!isRowHidden(r) && !isColumnHidden(c) && d->isCellEnabled(r, c)))
1661 break;
1662 if (wrapped && (originalRow < visualRow || (originalRow == visualRow && originalColumn <= visualColumn))) {
1663 looped = true;
1664 break;
1665 }
1666 }
1667 if (cursorAction == MoveLeft || visualColumn >= 0)
1668 break;
1669 visualColumn = right + 1;
1670 if (visualRow == 0) {
1671 wrapped = true;
1672 visualRow = bottom;
1673 } else {
1674 --visualRow;
1675 }
1676 } while (!looped);
1677 if (visualColumn < 0)
1678 visualColumn = originalColumn;
1679 break;
1680 }
1681 case MoveNext:
1682 case MoveRight: {
1683 int originalRow = visualRow;
1684 int originalColumn = visualColumn;
1685 bool firstTime = true;
1686 bool looped = false;
1687 bool wrapped = false;
1688 do {
1689 int r = d->logicalRow(visualRow);
1690 int c = d->logicalColumn(visualColumn);
1691 if (firstTime && c != -1 && d->hasSpans()) {
1692 firstTime = false;
1693 QSpanCollection::Span span = d->span(r, c);
1694 if (span.width() > 1 || span.height() > 1)
1695 visualColumn = d->visualColumn(d->columnSpanEndLogical(span.left(), span.width()));
1696 }
1697 while (visualColumn <= right) {
1698 ++visualColumn;
1699 r = d->logicalRow(visualRow);
1700 c = d->logicalColumn(visualColumn);
1701 if (r == -1 || c == -1 || (!isRowHidden(r) && !isColumnHidden(c) && d->isCellEnabled(r, c)))
1702 break;
1703 if (wrapped && (originalRow > visualRow || (originalRow == visualRow && originalColumn >= visualColumn))) {
1704 looped = true;
1705 break;
1706 }
1707 }
1708 if (cursorAction == MoveRight || visualColumn <= right)
1709 break;
1710 visualColumn = -1;
1711 if (visualRow == bottom) {
1712 wrapped = true;
1713 visualRow = 0;
1714 } else {
1715 ++visualRow;
1716 }
1717 } while (!looped);
1718 if (visualColumn > right)
1719 visualColumn = originalColumn;
1720 break;
1721 }
1722 case MoveHome:
1723 visualColumn = 0;
1724 while (visualColumn < right && d->isVisualColumnHiddenOrDisabled(visualRow, visualColumn))
1725 ++visualColumn;
1726 if (modifiers & Qt::ControlModifier) {
1727 visualRow = 0;
1728 while (visualRow < bottom && d->isVisualRowHiddenOrDisabled(visualRow, visualColumn))
1729 ++visualRow;
1730 }
1731 break;
1732 case MoveEnd:
1733 visualColumn = right;
1734 if (modifiers & Qt::ControlModifier)
1735 visualRow = bottom;
1736 break;
1737 case MovePageUp: {
1738 int newRow = rowAt(visualRect(current).top() - d->viewport->height());
1739 if (newRow == -1)
1740 newRow = d->logicalRow(0);
1741 return d->model->index(newRow, current.column(), d->root);
1742 }
1743 case MovePageDown: {
1744 int newRow = rowAt(visualRect(current).bottom() + d->viewport->height());
1745 if (newRow == -1)
1746 newRow = d->logicalRow(bottom);
1747 return d->model->index(newRow, current.column(), d->root);
1748 }}
1749
1750 d->visualCursor = QPoint(visualColumn, visualRow);
1751 int logicalRow = d->logicalRow(visualRow);
1752 int logicalColumn = d->logicalColumn(visualColumn);
1753 if (!d->model->hasIndex(logicalRow, logicalColumn, d->root))
1754 return QModelIndex();
1755
1756 QModelIndex result = d->model->index(logicalRow, logicalColumn, d->root);
1757 if (!d->isRowHidden(logicalRow) && !d->isColumnHidden(logicalColumn) && d->isIndexEnabled(result))
1758 return result;
1759
1760 return QModelIndex();
1761 }
1762
1763 /*!
1764 \fn void QTableView::setSelection(const QRect &rect,
1765 QItemSelectionModel::SelectionFlags flags)
1766
1767 Selects the items within the given \a rect and in accordance with
1768 the specified selection \a flags.
1769 */
setSelection(const QRect & rect,QItemSelectionModel::SelectionFlags command)1770 void QTableView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
1771 {
1772 Q_D(QTableView);
1773 QModelIndex tl = indexAt(QPoint(isRightToLeft() ? qMax(rect.left(), rect.right())
1774 : qMin(rect.left(), rect.right()), qMin(rect.top(), rect.bottom())));
1775 QModelIndex br = indexAt(QPoint(isRightToLeft() ? qMin(rect.left(), rect.right()) :
1776 qMax(rect.left(), rect.right()), qMax(rect.top(), rect.bottom())));
1777 if (!d->selectionModel || !tl.isValid() || !br.isValid() || !d->isIndexEnabled(tl) || !d->isIndexEnabled(br))
1778 return;
1779
1780 bool verticalMoved = verticalHeader()->sectionsMoved();
1781 bool horizontalMoved = horizontalHeader()->sectionsMoved();
1782
1783 QItemSelection selection;
1784
1785 if (d->hasSpans()) {
1786 bool expanded;
1787 int top = qMin(d->visualRow(tl.row()), d->visualRow(br.row()));
1788 int left = qMin(d->visualColumn(tl.column()), d->visualColumn(br.column()));
1789 int bottom = qMax(d->visualRow(tl.row()), d->visualRow(br.row()));
1790 int right = qMax(d->visualColumn(tl.column()), d->visualColumn(br.column()));
1791 do {
1792 expanded = false;
1793 foreach (QSpanCollection::Span *it, d->spans.spans) {
1794 const QSpanCollection::Span &span = *it;
1795 int t = d->visualRow(span.top());
1796 int l = d->visualColumn(span.left());
1797 int b = d->visualRow(d->rowSpanEndLogical(span.top(), span.height()));
1798 int r = d->visualColumn(d->columnSpanEndLogical(span.left(), span.width()));
1799 if ((t > bottom) || (l > right) || (top > b) || (left > r))
1800 continue; // no intersect
1801 if (t < top) {
1802 top = t;
1803 expanded = true;
1804 }
1805 if (l < left) {
1806 left = l;
1807 expanded = true;
1808 }
1809 if (b > bottom) {
1810 bottom = b;
1811 expanded = true;
1812 }
1813 if (r > right) {
1814 right = r;
1815 expanded = true;
1816 }
1817 if (expanded)
1818 break;
1819 }
1820 } while (expanded);
1821 for (int horizontal = left; horizontal <= right; ++horizontal) {
1822 int column = d->logicalColumn(horizontal);
1823 for (int vertical = top; vertical <= bottom; ++vertical) {
1824 int row = d->logicalRow(vertical);
1825 QModelIndex index = d->model->index(row, column, d->root);
1826 selection.append(QItemSelectionRange(index));
1827 }
1828 }
1829 } else if (verticalMoved && horizontalMoved) {
1830 int top = d->visualRow(tl.row());
1831 int left = d->visualColumn(tl.column());
1832 int bottom = d->visualRow(br.row());
1833 int right = d->visualColumn(br.column());
1834 for (int horizontal = left; horizontal <= right; ++horizontal) {
1835 int column = d->logicalColumn(horizontal);
1836 for (int vertical = top; vertical <= bottom; ++vertical) {
1837 int row = d->logicalRow(vertical);
1838 QModelIndex index = d->model->index(row, column, d->root);
1839 selection.append(QItemSelectionRange(index));
1840 }
1841 }
1842 } else if (horizontalMoved) {
1843 int left = d->visualColumn(tl.column());
1844 int right = d->visualColumn(br.column());
1845 for (int visual = left; visual <= right; ++visual) {
1846 int column = d->logicalColumn(visual);
1847 QModelIndex topLeft = d->model->index(tl.row(), column, d->root);
1848 QModelIndex bottomRight = d->model->index(br.row(), column, d->root);
1849 selection.append(QItemSelectionRange(topLeft, bottomRight));
1850 }
1851 } else if (verticalMoved) {
1852 int top = d->visualRow(tl.row());
1853 int bottom = d->visualRow(br.row());
1854 for (int visual = top; visual <= bottom; ++visual) {
1855 int row = d->logicalRow(visual);
1856 QModelIndex topLeft = d->model->index(row, tl.column(), d->root);
1857 QModelIndex bottomRight = d->model->index(row, br.column(), d->root);
1858 selection.append(QItemSelectionRange(topLeft, bottomRight));
1859 }
1860 } else { // nothing moved
1861 QItemSelectionRange range(tl, br);
1862 if (!range.isEmpty())
1863 selection.append(range);
1864 }
1865
1866 d->selectionModel->select(selection, command);
1867 }
1868
1869 /*!
1870 \internal
1871
1872 Returns the rectangle from the viewport of the items in the given
1873 \a selection.
1874
1875 Since 4.7, the returned region only contains rectangles intersecting
1876 (or included in) the viewport.
1877 */
visualRegionForSelection(const QItemSelection & selection) const1878 QRegion QTableView::visualRegionForSelection(const QItemSelection &selection) const
1879 {
1880 Q_D(const QTableView);
1881
1882 if (selection.isEmpty())
1883 return QRegion();
1884
1885 QRegion selectionRegion;
1886 const QRect &viewportRect = d->viewport->rect();
1887 bool verticalMoved = verticalHeader()->sectionsMoved();
1888 bool horizontalMoved = horizontalHeader()->sectionsMoved();
1889
1890 if ((verticalMoved && horizontalMoved) || (d->hasSpans() && (verticalMoved || horizontalMoved))) {
1891 for (int i = 0; i < selection.count(); ++i) {
1892 QItemSelectionRange range = selection.at(i);
1893 if (range.parent() != d->root || !range.isValid())
1894 continue;
1895 for (int r = range.top(); r <= range.bottom(); ++r)
1896 for (int c = range.left(); c <= range.right(); ++c) {
1897 const QRect &rangeRect = visualRect(d->model->index(r, c, d->root));
1898 if (viewportRect.intersects(rangeRect))
1899 selectionRegion += rangeRect;
1900 }
1901 }
1902 } else if (horizontalMoved) {
1903 for (int i = 0; i < selection.count(); ++i) {
1904 QItemSelectionRange range = selection.at(i);
1905 if (range.parent() != d->root || !range.isValid())
1906 continue;
1907 int top = rowViewportPosition(range.top());
1908 int bottom = rowViewportPosition(range.bottom()) + rowHeight(range.bottom());
1909 if (top > bottom)
1910 qSwap<int>(top, bottom);
1911 int height = bottom - top;
1912 for (int c = range.left(); c <= range.right(); ++c) {
1913 const QRect rangeRect(columnViewportPosition(c), top, columnWidth(c), height);
1914 if (viewportRect.intersects(rangeRect))
1915 selectionRegion += rangeRect;
1916 }
1917 }
1918 } else if (verticalMoved) {
1919 for (int i = 0; i < selection.count(); ++i) {
1920 QItemSelectionRange range = selection.at(i);
1921 if (range.parent() != d->root || !range.isValid())
1922 continue;
1923 int left = columnViewportPosition(range.left());
1924 int right = columnViewportPosition(range.right()) + columnWidth(range.right());
1925 if (left > right)
1926 qSwap<int>(left, right);
1927 int width = right - left;
1928 for (int r = range.top(); r <= range.bottom(); ++r) {
1929 const QRect rangeRect(left, rowViewportPosition(r), width, rowHeight(r));
1930 if (viewportRect.intersects(rangeRect))
1931 selectionRegion += rangeRect;
1932 }
1933 }
1934 } else { // nothing moved
1935 const int gridAdjust = showGrid() ? 1 : 0;
1936 for (int i = 0; i < selection.count(); ++i) {
1937 QItemSelectionRange range = selection.at(i);
1938 if (range.parent() != d->root || !range.isValid())
1939 continue;
1940 d->trimHiddenSelections(&range);
1941
1942 const int rtop = rowViewportPosition(range.top());
1943 const int rbottom = rowViewportPosition(range.bottom()) + rowHeight(range.bottom());
1944 int rleft;
1945 int rright;
1946 if (isLeftToRight()) {
1947 rleft = columnViewportPosition(range.left());
1948 rright = columnViewportPosition(range.right()) + columnWidth(range.right());
1949 } else {
1950 rleft = columnViewportPosition(range.right());
1951 rright = columnViewportPosition(range.left()) + columnWidth(range.left());
1952 }
1953 const QRect rangeRect(QPoint(rleft, rtop), QPoint(rright - 1 - gridAdjust, rbottom - 1 - gridAdjust));
1954 if (viewportRect.intersects(rangeRect))
1955 selectionRegion += rangeRect;
1956 if (d->hasSpans()) {
1957 foreach (QSpanCollection::Span *s,
1958 d->spans.spansInRect(range.left(), range.top(), range.width(), range.height())) {
1959 if (range.contains(s->top(), s->left(), range.parent())) {
1960 const QRect &visualSpanRect = d->visualSpanRect(*s);
1961 if (viewportRect.intersects(visualSpanRect))
1962 selectionRegion += visualSpanRect;
1963 }
1964 }
1965 }
1966 }
1967 }
1968
1969 return selectionRegion;
1970 }
1971
1972
1973 /*!
1974 \reimp
1975 */
selectedIndexes() const1976 QModelIndexList QTableView::selectedIndexes() const
1977 {
1978 Q_D(const QTableView);
1979 QModelIndexList viewSelected;
1980 QModelIndexList modelSelected;
1981 if (d->selectionModel)
1982 modelSelected = d->selectionModel->selectedIndexes();
1983 for (int i = 0; i < modelSelected.count(); ++i) {
1984 QModelIndex index = modelSelected.at(i);
1985 if (!isIndexHidden(index) && index.parent() == d->root)
1986 viewSelected.append(index);
1987 }
1988 return viewSelected;
1989 }
1990
1991
1992 /*!
1993 This slot is called whenever rows are added or deleted. The
1994 previous number of rows is specified by \a oldCount, and the new
1995 number of rows is specified by \a newCount.
1996 */
rowCountChanged(int oldCount,int newCount)1997 void QTableView::rowCountChanged(int oldCount, int newCount )
1998 {
1999 Q_D(QTableView);
2000 //when removing rows, we need to disable updates for the header until the geometries have been
2001 //updated and the offset has been adjusted, or we risk calling paintSection for all the sections
2002 if (newCount < oldCount)
2003 d->verticalHeader->setUpdatesEnabled(false);
2004 d->doDelayedItemsLayout();
2005 }
2006
2007 /*!
2008 This slot is called whenever columns are added or deleted. The
2009 previous number of columns is specified by \a oldCount, and the new
2010 number of columns is specified by \a newCount.
2011 */
columnCountChanged(int,int)2012 void QTableView::columnCountChanged(int, int)
2013 {
2014 Q_D(QTableView);
2015 updateGeometries();
2016 if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem)
2017 d->horizontalHeader->setOffsetToSectionPosition(horizontalScrollBar()->value());
2018 else
2019 d->horizontalHeader->setOffset(horizontalScrollBar()->value());
2020 d->viewport->update();
2021 }
2022
2023 /*!
2024 \reimp
2025 */
updateGeometries()2026 void QTableView::updateGeometries()
2027 {
2028 Q_D(QTableView);
2029 if (d->geometryRecursionBlock)
2030 return;
2031 d->geometryRecursionBlock = true;
2032
2033 int width = 0;
2034 if (!d->verticalHeader->isHidden()) {
2035 width = qMax(d->verticalHeader->minimumWidth(), d->verticalHeader->sizeHint().width());
2036 width = qMin(width, d->verticalHeader->maximumWidth());
2037 }
2038 int height = 0;
2039 if (!d->horizontalHeader->isHidden()) {
2040 height = qMax(d->horizontalHeader->minimumHeight(), d->horizontalHeader->sizeHint().height());
2041 height = qMin(height, d->horizontalHeader->maximumHeight());
2042 }
2043 bool reverse = isRightToLeft();
2044 if (reverse)
2045 setViewportMargins(0, height, width, 0);
2046 else
2047 setViewportMargins(width, height, 0, 0);
2048
2049 // update headers
2050
2051 QRect vg = d->viewport->geometry();
2052
2053 int verticalLeft = reverse ? vg.right() + 1 : (vg.left() - width);
2054 d->verticalHeader->setGeometry(verticalLeft, vg.top(), width, vg.height());
2055 if (d->verticalHeader->isHidden())
2056 QMetaObject::invokeMethod(d->verticalHeader, "updateGeometries");
2057
2058 int horizontalTop = vg.top() - height;
2059 d->horizontalHeader->setGeometry(vg.left(), horizontalTop, vg.width(), height);
2060 if (d->horizontalHeader->isHidden())
2061 QMetaObject::invokeMethod(d->horizontalHeader, "updateGeometries");
2062
2063 // update cornerWidget
2064 if (d->horizontalHeader->isHidden() || d->verticalHeader->isHidden()) {
2065 d->cornerWidget->setHidden(true);
2066 } else {
2067 d->cornerWidget->setHidden(false);
2068 d->cornerWidget->setGeometry(verticalLeft, horizontalTop, width, height);
2069 }
2070
2071 // update scroll bars
2072
2073 // ### move this block into the if
2074 QSize vsize = d->viewport->size();
2075 QSize max = maximumViewportSize();
2076 uint horizontalLength = d->horizontalHeader->length();
2077 uint verticalLength = d->verticalHeader->length();
2078 if ((uint)max.width() >= horizontalLength && (uint)max.height() >= verticalLength)
2079 vsize = max;
2080
2081 // horizontal scroll bar
2082 const int columnCount = d->horizontalHeader->count();
2083 const int viewportWidth = vsize.width();
2084 int columnsInViewport = 0;
2085 for (int width = 0, column = columnCount - 1; column >= 0; --column) {
2086 int logical = d->horizontalHeader->logicalIndex(column);
2087 if (!d->horizontalHeader->isSectionHidden(logical)) {
2088 width += d->horizontalHeader->sectionSize(logical);
2089 if (width > viewportWidth)
2090 break;
2091 ++columnsInViewport;
2092 }
2093 }
2094 columnsInViewport = qMax(columnsInViewport, 1); //there must be always at least 1 column
2095
2096 if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) {
2097 const int visibleColumns = columnCount - d->horizontalHeader->hiddenSectionCount();
2098 horizontalScrollBar()->setRange(0, visibleColumns - columnsInViewport);
2099 horizontalScrollBar()->setPageStep(columnsInViewport);
2100 if (columnsInViewport >= visibleColumns)
2101 d->horizontalHeader->setOffset(0);
2102 horizontalScrollBar()->setSingleStep(1);
2103 } else { // ScrollPerPixel
2104 horizontalScrollBar()->setPageStep(vsize.width());
2105 horizontalScrollBar()->setRange(0, horizontalLength - vsize.width());
2106 horizontalScrollBar()->setSingleStep(qMax(vsize.width() / (columnsInViewport + 1), 2));
2107 }
2108
2109 // vertical scroll bar
2110 const int rowCount = d->verticalHeader->count();
2111 const int viewportHeight = vsize.height();
2112 int rowsInViewport = 0;
2113 for (int height = 0, row = rowCount - 1; row >= 0; --row) {
2114 int logical = d->verticalHeader->logicalIndex(row);
2115 if (!d->verticalHeader->isSectionHidden(logical)) {
2116 height += d->verticalHeader->sectionSize(logical);
2117 if (height > viewportHeight)
2118 break;
2119 ++rowsInViewport;
2120 }
2121 }
2122 rowsInViewport = qMax(rowsInViewport, 1); //there must be always at least 1 row
2123
2124 if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
2125 const int visibleRows = rowCount - d->verticalHeader->hiddenSectionCount();
2126 verticalScrollBar()->setRange(0, visibleRows - rowsInViewport);
2127 verticalScrollBar()->setPageStep(rowsInViewport);
2128 if (rowsInViewport >= visibleRows)
2129 d->verticalHeader->setOffset(0);
2130 verticalScrollBar()->setSingleStep(1);
2131 } else { // ScrollPerPixel
2132 verticalScrollBar()->setPageStep(vsize.height());
2133 verticalScrollBar()->setRange(0, verticalLength - vsize.height());
2134 verticalScrollBar()->setSingleStep(qMax(vsize.height() / (rowsInViewport + 1), 2));
2135 }
2136
2137 d->geometryRecursionBlock = false;
2138 QAbstractItemView::updateGeometries();
2139 }
2140
2141 /*!
2142 Returns the size hint for the given \a row's height or -1 if there
2143 is no model.
2144
2145 If you need to set the height of a given row to a fixed value, call
2146 QHeaderView::resizeSection() on the table's vertical header.
2147
2148 If you reimplement this function in a subclass, note that the value you
2149 return is only used when resizeRowToContents() is called. In that case,
2150 if a larger row height is required by either the vertical header or
2151 the item delegate, that width will be used instead.
2152
2153 \sa QWidget::sizeHint, verticalHeader()
2154 */
sizeHintForRow(int row) const2155 int QTableView::sizeHintForRow(int row) const
2156 {
2157 Q_D(const QTableView);
2158
2159 if (!model())
2160 return -1;
2161
2162 ensurePolished();
2163
2164 int left = qMax(0, d->horizontalHeader->visualIndexAt(0));
2165 int right = d->horizontalHeader->visualIndexAt(d->viewport->width());
2166 if (right == -1) // the table don't have enough columns to fill the viewport
2167 right = d->model->columnCount(d->root) - 1;
2168
2169 QStyleOptionViewItemV4 option = d->viewOptionsV4();
2170
2171 int hint = 0;
2172 QModelIndex index;
2173 for (int column = left; column <= right; ++column) {
2174 int logicalColumn = d->horizontalHeader->logicalIndex(column);
2175 if (d->horizontalHeader->isSectionHidden(logicalColumn))
2176 continue;
2177 index = d->model->index(row, logicalColumn, d->root);
2178 if (d->wrapItemText) {// for wrapping boundaries
2179 option.rect.setY(rowViewportPosition(index.row()));
2180 option.rect.setHeight(rowHeight(index.row()));
2181 option.rect.setX(columnViewportPosition(index.column()));
2182 option.rect.setWidth(columnWidth(index.column()));
2183 }
2184
2185 QWidget *editor = d->editorForIndex(index).widget.data();
2186 if (editor && d->persistent.contains(editor)) {
2187 hint = qMax(hint, editor->sizeHint().height());
2188 int min = editor->minimumSize().height();
2189 int max = editor->maximumSize().height();
2190 hint = qBound(min, hint, max);
2191 }
2192
2193 hint = qMax(hint, itemDelegate(index)->sizeHint(option, index).height());
2194 }
2195
2196 return d->showGrid ? hint + 1 : hint;
2197 }
2198
2199 /*!
2200 Returns the size hint for the given \a column's width or -1 if
2201 there is no model.
2202
2203 If you need to set the width of a given column to a fixed value, call
2204 QHeaderView::resizeSection() on the table's horizontal header.
2205
2206 If you reimplement this function in a subclass, note that the value you
2207 return will be used when resizeColumnToContents() or
2208 QHeaderView::resizeSections() is called. If a larger column width is
2209 required by either the horizontal header or the item delegate, the larger
2210 width will be used instead.
2211
2212 \sa QWidget::sizeHint, horizontalHeader()
2213 */
sizeHintForColumn(int column) const2214 int QTableView::sizeHintForColumn(int column) const
2215 {
2216 Q_D(const QTableView);
2217
2218 if (!model())
2219 return -1;
2220
2221 ensurePolished();
2222
2223 int top = qMax(0, d->verticalHeader->visualIndexAt(0));
2224 int bottom = d->verticalHeader->visualIndexAt(d->viewport->height());
2225 if (!isVisible() || bottom == -1) // the table don't have enough rows to fill the viewport
2226 bottom = d->model->rowCount(d->root) - 1;
2227
2228 QStyleOptionViewItemV4 option = d->viewOptionsV4();
2229
2230 int hint = 0;
2231 QModelIndex index;
2232 for (int row = top; row <= bottom; ++row) {
2233 int logicalRow = d->verticalHeader->logicalIndex(row);
2234 if (d->verticalHeader->isSectionHidden(logicalRow))
2235 continue;
2236 index = d->model->index(logicalRow, column, d->root);
2237
2238 QWidget *editor = d->editorForIndex(index).widget.data();
2239 if (editor && d->persistent.contains(editor)) {
2240 hint = qMax(hint, editor->sizeHint().width());
2241 int min = editor->minimumSize().width();
2242 int max = editor->maximumSize().width();
2243 hint = qBound(min, hint, max);
2244 }
2245
2246 hint = qMax(hint, itemDelegate(index)->sizeHint(option, index).width());
2247 }
2248
2249 return d->showGrid ? hint + 1 : hint;
2250 }
2251
2252 /*!
2253 Returns the y-coordinate in contents coordinates of the given \a
2254 row.
2255 */
rowViewportPosition(int row) const2256 int QTableView::rowViewportPosition(int row) const
2257 {
2258 Q_D(const QTableView);
2259 return d->verticalHeader->sectionViewportPosition(row);
2260 }
2261
2262 /*!
2263 Returns the row in which the given y-coordinate, \a y, in contents
2264 coordinates is located.
2265
2266 \note This function returns -1 if the given coordinate is not valid
2267 (has no row).
2268
2269 \sa columnAt()
2270 */
rowAt(int y) const2271 int QTableView::rowAt(int y) const
2272 {
2273 Q_D(const QTableView);
2274 return d->verticalHeader->logicalIndexAt(y);
2275 }
2276
2277 /*!
2278 \since 4.1
2279
2280 Sets the height of the given \a row to be \a height.
2281 */
setRowHeight(int row,int height)2282 void QTableView::setRowHeight(int row, int height)
2283 {
2284 Q_D(const QTableView);
2285 d->verticalHeader->resizeSection(row, height);
2286 }
2287
2288 /*!
2289 Returns the height of the given \a row.
2290
2291 \sa resizeRowToContents(), columnWidth()
2292 */
rowHeight(int row) const2293 int QTableView::rowHeight(int row) const
2294 {
2295 Q_D(const QTableView);
2296 return d->verticalHeader->sectionSize(row);
2297 }
2298
2299 /*!
2300 Returns the x-coordinate in contents coordinates of the given \a
2301 column.
2302 */
columnViewportPosition(int column) const2303 int QTableView::columnViewportPosition(int column) const
2304 {
2305 Q_D(const QTableView);
2306 return d->horizontalHeader->sectionViewportPosition(column);
2307 }
2308
2309 /*!
2310 Returns the column in which the given x-coordinate, \a x, in contents
2311 coordinates is located.
2312
2313 \note This function returns -1 if the given coordinate is not valid
2314 (has no column).
2315
2316 \sa rowAt()
2317 */
columnAt(int x) const2318 int QTableView::columnAt(int x) const
2319 {
2320 Q_D(const QTableView);
2321 return d->horizontalHeader->logicalIndexAt(x);
2322 }
2323
2324 /*!
2325 \since 4.1
2326
2327 Sets the width of the given \a column to be \a width.
2328 */
setColumnWidth(int column,int width)2329 void QTableView::setColumnWidth(int column, int width)
2330 {
2331 Q_D(const QTableView);
2332 d->horizontalHeader->resizeSection(column, width);
2333 }
2334
2335 /*!
2336 Returns the width of the given \a column.
2337
2338 \sa resizeColumnToContents(), rowHeight()
2339 */
columnWidth(int column) const2340 int QTableView::columnWidth(int column) const
2341 {
2342 Q_D(const QTableView);
2343 return d->horizontalHeader->sectionSize(column);
2344 }
2345
2346 /*!
2347 Returns true if the given \a row is hidden; otherwise returns false.
2348
2349 \sa isColumnHidden()
2350 */
isRowHidden(int row) const2351 bool QTableView::isRowHidden(int row) const
2352 {
2353 Q_D(const QTableView);
2354 return d->verticalHeader->isSectionHidden(row);
2355 }
2356
2357 /*!
2358 If \a hide is true \a row will be hidden, otherwise it will be shown.
2359
2360 \sa setColumnHidden()
2361 */
setRowHidden(int row,bool hide)2362 void QTableView::setRowHidden(int row, bool hide)
2363 {
2364 Q_D(QTableView);
2365 if (row < 0 || row >= d->verticalHeader->count())
2366 return;
2367 d->verticalHeader->setSectionHidden(row, hide);
2368 }
2369
2370 /*!
2371 Returns true if the given \a column is hidden; otherwise returns false.
2372
2373 \sa isRowHidden()
2374 */
isColumnHidden(int column) const2375 bool QTableView::isColumnHidden(int column) const
2376 {
2377 Q_D(const QTableView);
2378 return d->horizontalHeader->isSectionHidden(column);
2379 }
2380
2381 /*!
2382 If \a hide is true the given \a column will be hidden; otherwise it
2383 will be shown.
2384
2385 \sa setRowHidden()
2386 */
setColumnHidden(int column,bool hide)2387 void QTableView::setColumnHidden(int column, bool hide)
2388 {
2389 Q_D(QTableView);
2390 if (column < 0 || column >= d->horizontalHeader->count())
2391 return;
2392 d->horizontalHeader->setSectionHidden(column, hide);
2393 }
2394
2395 /*!
2396 \since 4.2
2397 \property QTableView::sortingEnabled
2398 \brief whether sorting is enabled
2399
2400 If this property is true, sorting is enabled for the table. If
2401 this property is false, sorting is not enabled. The default value
2402 is false.
2403
2404 \note. Setting the property to true with setSortingEnabled()
2405 immediately triggers a call to sortByColumn() with the current
2406 sort section and order.
2407
2408 \sa sortByColumn()
2409 */
2410
2411 /*!
2412 If \a enabled true enables sorting for the table and immediately
2413 trigger a call to sortByColumn() with the current sort section and
2414 order
2415 */
setSortingEnabled(bool enable)2416 void QTableView::setSortingEnabled(bool enable)
2417 {
2418 Q_D(QTableView);
2419 d->sortingEnabled = enable;
2420 horizontalHeader()->setSortIndicatorShown(enable);
2421 if (enable) {
2422 disconnect(d->horizontalHeader, SIGNAL(sectionEntered(int)),
2423 this, SLOT(_q_selectColumn(int)));
2424 disconnect(horizontalHeader(), SIGNAL(sectionPressed(int)),
2425 this, SLOT(selectColumn(int)));
2426 connect(horizontalHeader(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)),
2427 this, SLOT(sortByColumn(int)), Qt::UniqueConnection);
2428 sortByColumn(horizontalHeader()->sortIndicatorSection(),
2429 horizontalHeader()->sortIndicatorOrder());
2430 } else {
2431 connect(d->horizontalHeader, SIGNAL(sectionEntered(int)),
2432 this, SLOT(_q_selectColumn(int)), Qt::UniqueConnection);
2433 connect(horizontalHeader(), SIGNAL(sectionPressed(int)),
2434 this, SLOT(selectColumn(int)), Qt::UniqueConnection);
2435 disconnect(horizontalHeader(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)),
2436 this, SLOT(sortByColumn(int)));
2437 }
2438 }
2439
isSortingEnabled() const2440 bool QTableView::isSortingEnabled() const
2441 {
2442 Q_D(const QTableView);
2443 return d->sortingEnabled;
2444 }
2445
2446 /*!
2447 \property QTableView::showGrid
2448 \brief whether the grid is shown
2449
2450 If this property is true a grid is drawn for the table; if the
2451 property is false, no grid is drawn. The default value is true.
2452 */
showGrid() const2453 bool QTableView::showGrid() const
2454 {
2455 Q_D(const QTableView);
2456 return d->showGrid;
2457 }
2458
setShowGrid(bool show)2459 void QTableView::setShowGrid(bool show)
2460 {
2461 Q_D(QTableView);
2462 if (d->showGrid != show) {
2463 d->showGrid = show;
2464 d->viewport->update();
2465 }
2466 }
2467
2468 /*!
2469 \property QTableView::gridStyle
2470 \brief the pen style used to draw the grid.
2471
2472 This property holds the style used when drawing the grid (see \l{showGrid}).
2473 */
gridStyle() const2474 Qt::PenStyle QTableView::gridStyle() const
2475 {
2476 Q_D(const QTableView);
2477 return d->gridStyle;
2478 }
2479
setGridStyle(Qt::PenStyle style)2480 void QTableView::setGridStyle(Qt::PenStyle style)
2481 {
2482 Q_D(QTableView);
2483 if (d->gridStyle != style) {
2484 d->gridStyle = style;
2485 d->viewport->update();
2486 }
2487 }
2488
2489 /*!
2490 \property QTableView::wordWrap
2491 \brief the item text word-wrapping policy
2492 \since 4.3
2493
2494 If this property is true then the item text is wrapped where
2495 necessary at word-breaks; otherwise it is not wrapped at all.
2496 This property is true by default.
2497
2498 Note that even of wrapping is enabled, the cell will not be
2499 expanded to fit all text. Ellipsis will be inserted according to
2500 the current \l{QAbstractItemView::}{textElideMode}.
2501
2502 */
setWordWrap(bool on)2503 void QTableView::setWordWrap(bool on)
2504 {
2505 Q_D(QTableView);
2506 if (d->wrapItemText == on)
2507 return;
2508 d->wrapItemText = on;
2509 QMetaObject::invokeMethod(d->verticalHeader, "resizeSections");
2510 QMetaObject::invokeMethod(d->horizontalHeader, "resizeSections");
2511 }
2512
wordWrap() const2513 bool QTableView::wordWrap() const
2514 {
2515 Q_D(const QTableView);
2516 return d->wrapItemText;
2517 }
2518
2519 /*!
2520 \property QTableView::cornerButtonEnabled
2521 \brief whether the button in the top-left corner is enabled
2522 \since 4.3
2523
2524 If this property is true then button in the top-left corner
2525 of the table view is enabled. Clicking on this button will
2526 select all the cells in the table view.
2527
2528 This property is true by default.
2529 */
setCornerButtonEnabled(bool enable)2530 void QTableView::setCornerButtonEnabled(bool enable)
2531 {
2532 Q_D(QTableView);
2533 d->cornerWidget->setEnabled(enable);
2534 }
2535
isCornerButtonEnabled() const2536 bool QTableView::isCornerButtonEnabled() const
2537 {
2538 Q_D(const QTableView);
2539 return d->cornerWidget->isEnabled();
2540 }
2541
2542 /*!
2543 \internal
2544
2545 Returns the rectangle on the viewport occupied by the given \a
2546 index.
2547 If the index is hidden in the view it will return a null QRect.
2548 */
visualRect(const QModelIndex & index) const2549 QRect QTableView::visualRect(const QModelIndex &index) const
2550 {
2551 Q_D(const QTableView);
2552 if (!d->isIndexValid(index) || index.parent() != d->root
2553 || (!d->hasSpans() && isIndexHidden(index)))
2554 return QRect();
2555
2556 d->executePostedLayout();
2557
2558 if (d->hasSpans()) {
2559 QSpanCollection::Span span = d->span(index.row(), index.column());
2560 return d->visualSpanRect(span);
2561 }
2562
2563 int rowp = rowViewportPosition(index.row());
2564 int rowh = rowHeight(index.row());
2565 int colp = columnViewportPosition(index.column());
2566 int colw = columnWidth(index.column());
2567
2568 const int i = showGrid() ? 1 : 0;
2569 return QRect(colp, rowp, colw - i, rowh - i);
2570 }
2571
2572 /*!
2573 \internal
2574
2575 Makes sure that the given \a item is visible in the table view,
2576 scrolling if necessary.
2577 */
scrollTo(const QModelIndex & index,ScrollHint hint)2578 void QTableView::scrollTo(const QModelIndex &index, ScrollHint hint)
2579 {
2580 Q_D(QTableView);
2581
2582 // check if we really need to do anything
2583 if (!d->isIndexValid(index)
2584 || (d->model->parent(index) != d->root)
2585 || isRowHidden(index.row()) || isColumnHidden(index.column()))
2586 return;
2587
2588 QSpanCollection::Span span;
2589 if (d->hasSpans())
2590 span = d->span(index.row(), index.column());
2591
2592 // Adjust horizontal position
2593
2594 int viewportWidth = d->viewport->width();
2595 int horizontalOffset = d->horizontalHeader->offset();
2596 int horizontalPosition = d->horizontalHeader->sectionPosition(index.column());
2597 int horizontalIndex = d->horizontalHeader->visualIndex(index.column());
2598 int cellWidth = d->hasSpans()
2599 ? d->columnSpanWidth(index.column(), span.width())
2600 : d->horizontalHeader->sectionSize(index.column());
2601
2602 if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) {
2603
2604 bool positionAtLeft = (horizontalPosition - horizontalOffset < 0);
2605 bool positionAtRight = (horizontalPosition - horizontalOffset + cellWidth > viewportWidth);
2606
2607 if (hint == PositionAtCenter || positionAtRight) {
2608 int w = (hint == PositionAtCenter ? viewportWidth / 2 : viewportWidth);
2609 int x = cellWidth;
2610 while (horizontalIndex > 0) {
2611 x += columnWidth(d->horizontalHeader->logicalIndex(horizontalIndex-1));
2612 if (x > w)
2613 break;
2614 --horizontalIndex;
2615 }
2616 }
2617
2618 if (positionAtRight || hint == PositionAtCenter || positionAtLeft) {
2619 int hiddenSections = 0;
2620 if (d->horizontalHeader->sectionsHidden()) {
2621 for (int s = horizontalIndex - 1; s >= 0; --s) {
2622 int column = d->horizontalHeader->logicalIndex(s);
2623 if (d->horizontalHeader->isSectionHidden(column))
2624 ++hiddenSections;
2625 }
2626 }
2627 horizontalScrollBar()->setValue(horizontalIndex - hiddenSections);
2628 }
2629
2630 } else { // ScrollPerPixel
2631 if (hint == PositionAtCenter) {
2632 horizontalScrollBar()->setValue(horizontalPosition - ((viewportWidth - cellWidth) / 2));
2633 } else {
2634 if (horizontalPosition - horizontalOffset < 0 || cellWidth > viewportWidth)
2635 horizontalScrollBar()->setValue(horizontalPosition);
2636 else if (horizontalPosition - horizontalOffset + cellWidth > viewportWidth)
2637 horizontalScrollBar()->setValue(horizontalPosition - viewportWidth + cellWidth);
2638 }
2639 }
2640
2641 // Adjust vertical position
2642
2643 int viewportHeight = d->viewport->height();
2644 int verticalOffset = d->verticalHeader->offset();
2645 int verticalPosition = d->verticalHeader->sectionPosition(index.row());
2646 int verticalIndex = d->verticalHeader->visualIndex(index.row());
2647 int cellHeight = d->hasSpans()
2648 ? d->rowSpanHeight(index.row(), span.height())
2649 : d->verticalHeader->sectionSize(index.row());
2650
2651 if (verticalPosition - verticalOffset < 0 || cellHeight > viewportHeight) {
2652 if (hint == EnsureVisible)
2653 hint = PositionAtTop;
2654 } else if (verticalPosition - verticalOffset + cellHeight > viewportHeight) {
2655 if (hint == EnsureVisible)
2656 hint = PositionAtBottom;
2657 }
2658
2659 if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
2660
2661 if (hint == PositionAtBottom || hint == PositionAtCenter) {
2662 int h = (hint == PositionAtCenter ? viewportHeight / 2 : viewportHeight);
2663 int y = cellHeight;
2664 while (verticalIndex > 0) {
2665 int row = d->verticalHeader->logicalIndex(verticalIndex - 1);
2666 y += d->verticalHeader->sectionSize(row);
2667 if (y > h)
2668 break;
2669 --verticalIndex;
2670 }
2671 }
2672
2673 if (hint == PositionAtBottom || hint == PositionAtCenter || hint == PositionAtTop) {
2674 int hiddenSections = 0;
2675 if (d->verticalHeader->sectionsHidden()) {
2676 for (int s = verticalIndex - 1; s >= 0; --s) {
2677 int row = d->verticalHeader->logicalIndex(s);
2678 if (d->verticalHeader->isSectionHidden(row))
2679 ++hiddenSections;
2680 }
2681 }
2682 verticalScrollBar()->setValue(verticalIndex - hiddenSections);
2683 }
2684
2685 } else { // ScrollPerPixel
2686 if (hint == PositionAtTop) {
2687 verticalScrollBar()->setValue(verticalPosition);
2688 } else if (hint == PositionAtBottom) {
2689 verticalScrollBar()->setValue(verticalPosition - viewportHeight + cellHeight);
2690 } else if (hint == PositionAtCenter) {
2691 verticalScrollBar()->setValue(verticalPosition - ((viewportHeight - cellHeight) / 2));
2692 }
2693 }
2694
2695 update(index);
2696 }
2697
2698 /*!
2699 This slot is called to change the height of the given \a row. The
2700 old height is specified by \a oldHeight, and the new height by \a
2701 newHeight.
2702
2703 \sa columnResized()
2704 */
rowResized(int row,int,int)2705 void QTableView::rowResized(int row, int, int)
2706 {
2707 Q_D(QTableView);
2708 d->rowsToUpdate.append(row);
2709 if (d->rowResizeTimerID == 0)
2710 d->rowResizeTimerID = startTimer(0);
2711 }
2712
2713 /*!
2714 This slot is called to change the width of the given \a column.
2715 The old width is specified by \a oldWidth, and the new width by \a
2716 newWidth.
2717
2718 \sa rowResized()
2719 */
columnResized(int column,int,int)2720 void QTableView::columnResized(int column, int, int)
2721 {
2722 Q_D(QTableView);
2723 d->columnsToUpdate.append(column);
2724 if (d->columnResizeTimerID == 0)
2725 d->columnResizeTimerID = startTimer(0);
2726 }
2727
2728 /*!
2729 \reimp
2730 */
timerEvent(QTimerEvent * event)2731 void QTableView::timerEvent(QTimerEvent *event)
2732 {
2733 Q_D(QTableView);
2734
2735 if (event->timerId() == d->columnResizeTimerID) {
2736 updateGeometries();
2737 killTimer(d->columnResizeTimerID);
2738 d->columnResizeTimerID = 0;
2739
2740 QRect rect;
2741 int viewportHeight = d->viewport->height();
2742 int viewportWidth = d->viewport->width();
2743 if (d->hasSpans()) {
2744 rect = QRect(0, 0, viewportWidth, viewportHeight);
2745 } else {
2746 for (int i = d->columnsToUpdate.size()-1; i >= 0; --i) {
2747 int column = d->columnsToUpdate.at(i);
2748 int x = columnViewportPosition(column);
2749 if (isRightToLeft())
2750 rect |= QRect(0, 0, x + columnWidth(column), viewportHeight);
2751 else
2752 rect |= QRect(x, 0, viewportWidth - x, viewportHeight);
2753 }
2754 }
2755
2756 d->viewport->update(rect.normalized());
2757 d->columnsToUpdate.clear();
2758 }
2759
2760 if (event->timerId() == d->rowResizeTimerID) {
2761 updateGeometries();
2762 killTimer(d->rowResizeTimerID);
2763 d->rowResizeTimerID = 0;
2764
2765 int viewportHeight = d->viewport->height();
2766 int viewportWidth = d->viewport->width();
2767 int top;
2768 if (d->hasSpans()) {
2769 top = 0;
2770 } else {
2771 top = viewportHeight;
2772 for (int i = d->rowsToUpdate.size()-1; i >= 0; --i) {
2773 int y = rowViewportPosition(d->rowsToUpdate.at(i));
2774 top = qMin(top, y);
2775 }
2776 }
2777
2778 d->viewport->update(QRect(0, top, viewportWidth, viewportHeight - top));
2779 d->rowsToUpdate.clear();
2780 }
2781
2782 QAbstractItemView::timerEvent(event);
2783 }
2784
2785 /*!
2786 This slot is called to change the index of the given \a row in the
2787 table view. The old index is specified by \a oldIndex, and the new
2788 index by \a newIndex.
2789
2790 \sa columnMoved()
2791 */
rowMoved(int,int oldIndex,int newIndex)2792 void QTableView::rowMoved(int, int oldIndex, int newIndex)
2793 {
2794 Q_D(QTableView);
2795
2796 updateGeometries();
2797 int logicalOldIndex = d->verticalHeader->logicalIndex(oldIndex);
2798 int logicalNewIndex = d->verticalHeader->logicalIndex(newIndex);
2799 if (d->hasSpans()) {
2800 d->viewport->update();
2801 } else {
2802 int oldTop = rowViewportPosition(logicalOldIndex);
2803 int newTop = rowViewportPosition(logicalNewIndex);
2804 int oldBottom = oldTop + rowHeight(logicalOldIndex);
2805 int newBottom = newTop + rowHeight(logicalNewIndex);
2806 int top = qMin(oldTop, newTop);
2807 int bottom = qMax(oldBottom, newBottom);
2808 int height = bottom - top;
2809 d->viewport->update(0, top, d->viewport->width(), height);
2810 }
2811 }
2812
2813 /*!
2814 This slot is called to change the index of the given \a column in
2815 the table view. The old index is specified by \a oldIndex, and
2816 the new index by \a newIndex.
2817
2818 \sa rowMoved()
2819 */
columnMoved(int,int oldIndex,int newIndex)2820 void QTableView::columnMoved(int, int oldIndex, int newIndex)
2821 {
2822 Q_D(QTableView);
2823
2824 updateGeometries();
2825 int logicalOldIndex = d->horizontalHeader->logicalIndex(oldIndex);
2826 int logicalNewIndex = d->horizontalHeader->logicalIndex(newIndex);
2827 if (d->hasSpans()) {
2828 d->viewport->update();
2829 } else {
2830 int oldLeft = columnViewportPosition(logicalOldIndex);
2831 int newLeft = columnViewportPosition(logicalNewIndex);
2832 int oldRight = oldLeft + columnWidth(logicalOldIndex);
2833 int newRight = newLeft + columnWidth(logicalNewIndex);
2834 int left = qMin(oldLeft, newLeft);
2835 int right = qMax(oldRight, newRight);
2836 int width = right - left;
2837 d->viewport->update(left, 0, width, d->viewport->height());
2838 }
2839 }
2840
2841 /*!
2842 Selects the given \a row in the table view if the current
2843 SelectionMode and SelectionBehavior allows rows to be selected.
2844
2845 \sa selectColumn()
2846 */
selectRow(int row)2847 void QTableView::selectRow(int row)
2848 {
2849 Q_D(QTableView);
2850 d->selectRow(row, true);
2851 }
2852
2853 /*!
2854 Selects the given \a column in the table view if the current
2855 SelectionMode and SelectionBehavior allows columns to be selected.
2856
2857 \sa selectRow()
2858 */
selectColumn(int column)2859 void QTableView::selectColumn(int column)
2860 {
2861 Q_D(QTableView);
2862 d->selectColumn(column, true);
2863 }
2864
2865 /*!
2866 Hide the given \a row.
2867
2868 \sa showRow() hideColumn()
2869 */
hideRow(int row)2870 void QTableView::hideRow(int row)
2871 {
2872 Q_D(QTableView);
2873 d->verticalHeader->hideSection(row);
2874 }
2875
2876 /*!
2877 Hide the given \a column.
2878
2879 \sa showColumn() hideRow()
2880 */
hideColumn(int column)2881 void QTableView::hideColumn(int column)
2882 {
2883 Q_D(QTableView);
2884 d->horizontalHeader->hideSection(column);
2885 }
2886
2887 /*!
2888 Show the given \a row.
2889
2890 \sa hideRow() showColumn()
2891 */
showRow(int row)2892 void QTableView::showRow(int row)
2893 {
2894 Q_D(QTableView);
2895 d->verticalHeader->showSection(row);
2896 }
2897
2898 /*!
2899 Show the given \a column.
2900
2901 \sa hideColumn() showRow()
2902 */
showColumn(int column)2903 void QTableView::showColumn(int column)
2904 {
2905 Q_D(QTableView);
2906 d->horizontalHeader->showSection(column);
2907 }
2908
2909 /*!
2910 Resizes the given \a row based on the size hints of the delegate
2911 used to render each item in the row.
2912 */
resizeRowToContents(int row)2913 void QTableView::resizeRowToContents(int row)
2914 {
2915 Q_D(QTableView);
2916 int content = sizeHintForRow(row);
2917 int header = d->verticalHeader->sectionSizeHint(row);
2918 d->verticalHeader->resizeSection(row, qMax(content, header));
2919 }
2920
2921 /*!
2922 Resizes all rows based on the size hints of the delegate
2923 used to render each item in the rows.
2924 */
resizeRowsToContents()2925 void QTableView::resizeRowsToContents()
2926 {
2927 Q_D(QTableView);
2928 d->verticalHeader->resizeSections(QHeaderView::ResizeToContents);
2929 }
2930
2931 /*!
2932 Resizes the given \a column based on the size hints of the delegate
2933 used to render each item in the column.
2934
2935 \note Only visible columns will be resized. Reimplement sizeHintForColumn()
2936 to resize hidden columns as well.
2937 */
resizeColumnToContents(int column)2938 void QTableView::resizeColumnToContents(int column)
2939 {
2940 Q_D(QTableView);
2941 int content = sizeHintForColumn(column);
2942 int header = d->horizontalHeader->sectionSizeHint(column);
2943 d->horizontalHeader->resizeSection(column, qMax(content, header));
2944 }
2945
2946 /*!
2947 Resizes all columns based on the size hints of the delegate
2948 used to render each item in the columns.
2949 */
resizeColumnsToContents()2950 void QTableView::resizeColumnsToContents()
2951 {
2952 Q_D(QTableView);
2953 d->horizontalHeader->resizeSections(QHeaderView::ResizeToContents);
2954 }
2955
2956 /*!
2957 \obsolete
2958 \overload
2959
2960 Sorts the model by the values in the given \a column.
2961 */
sortByColumn(int column)2962 void QTableView::sortByColumn(int column)
2963 {
2964 Q_D(QTableView);
2965 if (column == -1)
2966 return;
2967 d->model->sort(column, d->horizontalHeader->sortIndicatorOrder());
2968 }
2969
2970 /*!
2971 \since 4.2
2972
2973 Sorts the model by the values in the given \a column in the given \a order.
2974
2975 \sa sortingEnabled
2976 */
sortByColumn(int column,Qt::SortOrder order)2977 void QTableView::sortByColumn(int column, Qt::SortOrder order)
2978 {
2979 Q_D(QTableView);
2980 d->horizontalHeader->setSortIndicator(column, order);
2981 sortByColumn(column);
2982 }
2983
2984 /*!
2985 \internal
2986 */
verticalScrollbarAction(int action)2987 void QTableView::verticalScrollbarAction(int action)
2988 {
2989 QAbstractItemView::verticalScrollbarAction(action);
2990 }
2991
2992 /*!
2993 \internal
2994 */
horizontalScrollbarAction(int action)2995 void QTableView::horizontalScrollbarAction(int action)
2996 {
2997 QAbstractItemView::horizontalScrollbarAction(action);
2998 }
2999
3000 /*!
3001 \reimp
3002 */
isIndexHidden(const QModelIndex & index) const3003 bool QTableView::isIndexHidden(const QModelIndex &index) const
3004 {
3005 Q_D(const QTableView);
3006 Q_ASSERT(d->isIndexValid(index));
3007 if (isRowHidden(index.row()) || isColumnHidden(index.column()))
3008 return true;
3009 if (d->hasSpans()) {
3010 QSpanCollection::Span span = d->span(index.row(), index.column());
3011 return !((span.top() == index.row()) && (span.left() == index.column()));
3012 }
3013 return false;
3014 }
3015
3016 /*!
3017 \fn void QTableView::setSpan(int row, int column, int rowSpanCount, int columnSpanCount)
3018 \since 4.2
3019
3020 Sets the span of the table element at (\a row, \a column) to the number of
3021 rows and columns specified by (\a rowSpanCount, \a columnSpanCount).
3022
3023 \sa rowSpan(), columnSpan()
3024 */
setSpan(int row,int column,int rowSpan,int columnSpan)3025 void QTableView::setSpan(int row, int column, int rowSpan, int columnSpan)
3026 {
3027 Q_D(QTableView);
3028 if (row < 0 || column < 0 || rowSpan < 0 || columnSpan < 0)
3029 return;
3030 d->setSpan(row, column, rowSpan, columnSpan);
3031 d->viewport->update();
3032 }
3033
3034 /*!
3035 \since 4.2
3036
3037 Returns the row span of the table element at (\a row, \a column).
3038 The default is 1.
3039
3040 \sa setSpan(), columnSpan()
3041 */
rowSpan(int row,int column) const3042 int QTableView::rowSpan(int row, int column) const
3043 {
3044 Q_D(const QTableView);
3045 return d->rowSpan(row, column);
3046 }
3047
3048 /*!
3049 \since 4.2
3050
3051 Returns the column span of the table element at (\a row, \a
3052 column). The default is 1.
3053
3054 \sa setSpan(), rowSpan()
3055 */
columnSpan(int row,int column) const3056 int QTableView::columnSpan(int row, int column) const
3057 {
3058 Q_D(const QTableView);
3059 return d->columnSpan(row, column);
3060 }
3061
3062 /*!
3063 \since 4.4
3064
3065 Removes all row and column spans in the table view.
3066
3067 \sa setSpan()
3068 */
3069
clearSpans()3070 void QTableView::clearSpans()
3071 {
3072 Q_D(QTableView);
3073 d->spans.clear();
3074 d->viewport->update();
3075 }
3076
_q_selectRow(int row)3077 void QTableViewPrivate::_q_selectRow(int row)
3078 {
3079 selectRow(row, false);
3080 }
3081
_q_selectColumn(int column)3082 void QTableViewPrivate::_q_selectColumn(int column)
3083 {
3084 selectColumn(column, false);
3085 }
3086
selectRow(int row,bool anchor)3087 void QTableViewPrivate::selectRow(int row, bool anchor)
3088 {
3089 Q_Q(QTableView);
3090
3091 if (q->selectionBehavior() == QTableView::SelectColumns
3092 || (q->selectionMode() == QTableView::SingleSelection
3093 && q->selectionBehavior() == QTableView::SelectItems))
3094 return;
3095
3096 if (row >= 0 && row < model->rowCount(root)) {
3097 int column = horizontalHeader->logicalIndexAt(q->isRightToLeft() ? viewport->width() : 0);
3098 QModelIndex index = model->index(row, column, root);
3099 QItemSelectionModel::SelectionFlags command = q->selectionCommand(index);
3100 selectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
3101 if ((anchor && !(command & QItemSelectionModel::Current))
3102 || (q->selectionMode() == QTableView::SingleSelection))
3103 rowSectionAnchor = row;
3104
3105 if (q->selectionMode() != QTableView::SingleSelection
3106 && command.testFlag(QItemSelectionModel::Toggle)) {
3107 if (anchor)
3108 ctrlDragSelectionFlag = verticalHeader->selectionModel()->selectedRows().contains(index)
3109 ? QItemSelectionModel::Deselect : QItemSelectionModel::Select;
3110 command &= ~QItemSelectionModel::Toggle;
3111 command |= ctrlDragSelectionFlag;
3112 if (!anchor)
3113 command |= QItemSelectionModel::Current;
3114 }
3115
3116 QModelIndex tl = model->index(qMin(rowSectionAnchor, row), 0, root);
3117 QModelIndex br = model->index(qMax(rowSectionAnchor, row), model->columnCount(root) - 1, root);
3118 if (verticalHeader->sectionsMoved() && tl.row() != br.row())
3119 q->setSelection(q->visualRect(tl)|q->visualRect(br), command);
3120 else
3121 selectionModel->select(QItemSelection(tl, br), command);
3122 }
3123 }
3124
selectColumn(int column,bool anchor)3125 void QTableViewPrivate::selectColumn(int column, bool anchor)
3126 {
3127 Q_Q(QTableView);
3128
3129 if (q->selectionBehavior() == QTableView::SelectRows
3130 || (q->selectionMode() == QTableView::SingleSelection
3131 && q->selectionBehavior() == QTableView::SelectItems))
3132 return;
3133
3134 if (column >= 0 && column < model->columnCount(root)) {
3135 int row = verticalHeader->logicalIndexAt(0);
3136 QModelIndex index = model->index(row, column, root);
3137 QItemSelectionModel::SelectionFlags command = q->selectionCommand(index);
3138 selectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
3139 if ((anchor && !(command & QItemSelectionModel::Current))
3140 || (q->selectionMode() == QTableView::SingleSelection))
3141 columnSectionAnchor = column;
3142
3143 if (q->selectionMode() != QTableView::SingleSelection
3144 && command.testFlag(QItemSelectionModel::Toggle)) {
3145 if (anchor)
3146 ctrlDragSelectionFlag = horizontalHeader->selectionModel()->selectedColumns().contains(index)
3147 ? QItemSelectionModel::Deselect : QItemSelectionModel::Select;
3148 command &= ~QItemSelectionModel::Toggle;
3149 command |= ctrlDragSelectionFlag;
3150 if (!anchor)
3151 command |= QItemSelectionModel::Current;
3152 }
3153
3154 QModelIndex tl = model->index(0, qMin(columnSectionAnchor, column), root);
3155 QModelIndex br = model->index(model->rowCount(root) - 1,
3156 qMax(columnSectionAnchor, column), root);
3157 if (horizontalHeader->sectionsMoved() && tl.column() != br.column())
3158 q->setSelection(q->visualRect(tl)|q->visualRect(br), command);
3159 else
3160 selectionModel->select(QItemSelection(tl, br), command);
3161 }
3162 }
3163
3164 /*!
3165 \reimp
3166 */
currentChanged(const QModelIndex & current,const QModelIndex & previous)3167 void QTableView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
3168 {
3169 #ifndef QT_NO_ACCESSIBILITY
3170 if (QAccessible::isActive()) {
3171 if (current.isValid()) {
3172 #ifdef Q_WS_X11
3173 Q_D(QTableView);
3174 int entry = d->accessibleTable2Index(current);
3175 QAccessible::updateAccessibility(this, entry, QAccessible::Focus);
3176 #else
3177 int entry = visualIndex(current) + 1;
3178 if (horizontalHeader())
3179 ++entry;
3180 QAccessible::updateAccessibility(viewport(), entry, QAccessible::Focus);
3181 #endif
3182 }
3183 }
3184 #endif
3185 QAbstractItemView::currentChanged(current, previous);
3186 }
3187
3188 /*!
3189 \reimp
3190 */
selectionChanged(const QItemSelection & selected,const QItemSelection & deselected)3191 void QTableView::selectionChanged(const QItemSelection &selected,
3192 const QItemSelection &deselected)
3193 {
3194 #ifndef QT_NO_ACCESSIBILITY
3195 Q_D(QTableView);
3196 Q_UNUSED(d)
3197 if (QAccessible::isActive()) {
3198 // ### does not work properly for selection ranges.
3199 QModelIndex sel = selected.indexes().value(0);
3200 if (sel.isValid()) {
3201 #ifdef Q_WS_X11
3202 int entry = d->accessibleTable2Index(sel);
3203 QAccessible::updateAccessibility(this, entry, QAccessible::Selection);
3204 #else
3205 int entry = visualIndex(sel);
3206 if (horizontalHeader())
3207 ++entry;
3208 QAccessible::updateAccessibility(viewport(), entry, QAccessible::Selection);
3209 #endif
3210 }
3211 QModelIndex desel = deselected.indexes().value(0);
3212 if (desel.isValid()) {
3213 #ifdef Q_WS_X11
3214 int entry = d->accessibleTable2Index(desel);
3215 QAccessible::updateAccessibility(this, entry, QAccessible::SelectionRemove);
3216 #else
3217 int entry = visualIndex(sel);
3218 if (horizontalHeader())
3219 ++entry;
3220 QAccessible::updateAccessibility(viewport(), entry, QAccessible::SelectionRemove);
3221 #endif
3222 }
3223 }
3224 #endif
3225 QAbstractItemView::selectionChanged(selected, deselected);
3226 }
3227
visualIndex(const QModelIndex & index) const3228 int QTableView::visualIndex(const QModelIndex &index) const
3229 {
3230 return index.row();
3231 }
3232
3233 QT_END_NAMESPACE
3234
3235 #include "qtableview.moc"
3236
3237 #include "moc_qtableview.cpp"
3238
3239 #endif // QT_NO_TABLEVIEW
3240