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