source: trunk/src/gui/itemviews/qcolumnview.cpp@ 376

Last change on this file since 376 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 33.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <qglobal.h>
43
44#ifndef QT_NO_COLUMNVIEW
45
46#include "qcolumnview.h"
47#include "qcolumnview_p.h"
48#include "qcolumnviewgrip_p.h"
49
50#include <qlistview.h>
51#include <qabstractitemdelegate.h>
52#include <qscrollbar.h>
53#include <qpainter.h>
54#include <qdebug.h>
55#include <qpainterpath.h>
56
57QT_BEGIN_NAMESPACE
58
59#define ANIMATION_DURATION_MSEC 150
60
61/*!
62 \since 4.3
63 \class QColumnView
64 \brief The QColumnView class provides a model/view implementation of a column view.
65 \ingroup model-view
66 \ingroup advanced
67 \mainclass
68
69 QColumnView displays a model in a number of QListViews, one for each
70 hierarchy in the tree. This is sometimes referred to as a cascading list.
71
72 The QColumnView class is one of the \l{Model/View Classes}
73 and is part of Qt's \l{Model/View Programming}{model/view framework}.
74
75 QColumnView implements the interfaces defined by the
76 QAbstractItemView class to allow it to display data provided by
77 models derived from the QAbstractItemModel class.
78
79 \image qcolumnview.png
80
81 \sa \link model-view-programming.html Model/View Programming\endlink
82*/
83
84/*!
85 Constructs a column view with a \a parent to represent a model's
86 data. Use setModel() to set the model.
87
88 \sa QAbstractItemModel
89*/
90QColumnView::QColumnView(QWidget * parent)
91: QAbstractItemView(*new QColumnViewPrivate, parent)
92{
93 Q_D(QColumnView);
94 d->initialize();
95}
96
97/*!
98 \internal
99*/
100QColumnView::QColumnView(QColumnViewPrivate & dd, QWidget * parent)
101: QAbstractItemView(dd, parent)
102{
103 Q_D(QColumnView);
104 d->initialize();
105}
106
107void QColumnViewPrivate::initialize()
108{
109 Q_Q(QColumnView);
110 q->setTextElideMode(Qt::ElideMiddle);
111 q->connect(&currentAnimation, SIGNAL(frameChanged(int)),
112 q->horizontalScrollBar(), SLOT(setValue(int)));
113 q->connect(&currentAnimation, SIGNAL(finished()), q, SLOT(_q_changeCurrentColumn()));
114 delete itemDelegate;
115 q->setItemDelegate(new QColumnViewDelegate(q));
116}
117
118/*!
119 Destroys the column view.
120*/
121QColumnView::~QColumnView()
122{
123}
124
125/*!
126 \property QColumnView::resizeGripsVisible
127 \brief the way to specify if the list views gets resize grips or not
128
129 By default, \c visible is set to true
130
131 \sa setRootIndex()
132*/
133void QColumnView::setResizeGripsVisible(bool visible)
134{
135 Q_D(QColumnView);
136 if (d->showResizeGrips == visible)
137 return;
138 d->showResizeGrips = visible;
139 for (int i = 0; i < d->columns.count(); ++i) {
140 QAbstractItemView *view = d->columns[i];
141 if (visible) {
142 QColumnViewGrip *grip = new QColumnViewGrip(view);
143 view->setCornerWidget(grip);
144 connect(grip, SIGNAL(gripMoved(int)), this, SLOT(_q_gripMoved(int)));
145 } else {
146 QWidget *widget = view->cornerWidget();
147 view->setCornerWidget(0);
148 widget->deleteLater();
149 }
150 }
151}
152
153bool QColumnView::resizeGripsVisible() const
154{
155 Q_D(const QColumnView);
156 return d->showResizeGrips;
157}
158
159/*!
160 \reimp
161*/
162void QColumnView::setModel(QAbstractItemModel *model)
163{
164 Q_D(QColumnView);
165 if (model == d->model)
166 return;
167 d->closeColumns();
168 QAbstractItemView::setModel(model);
169}
170
171/*!
172 \reimp
173*/
174void QColumnView::setRootIndex(const QModelIndex &index)
175{
176 Q_D(QColumnView);
177 if (!model())
178 return;
179
180 d->closeColumns();
181 Q_ASSERT(d->columns.count() == 0);
182
183 QAbstractItemView *view = d->createColumn(index, true);
184 if (view->selectionModel())
185 view->selectionModel()->deleteLater();
186 if (view->model())
187 view->setSelectionModel(selectionModel());
188
189 QAbstractItemView::setRootIndex(index);
190 d->updateScrollbars();
191}
192
193/*!
194 \reimp
195*/
196bool QColumnView::isIndexHidden(const QModelIndex &index) const
197{
198 Q_UNUSED(index);
199 return false;
200}
201
202/*!
203 \reimp
204*/
205QModelIndex QColumnView::indexAt(const QPoint &point) const
206{
207 Q_D(const QColumnView);
208 for (int i = 0; i < d->columns.size(); ++i) {
209 QPoint topLeft = d->columns.at(i)->frameGeometry().topLeft();
210 QPoint adjustedPoint(point.x() - topLeft.x(), point.y() - topLeft.y());
211 QModelIndex index = d->columns.at(i)->indexAt(adjustedPoint);
212 if (index.isValid())
213 return index;
214 }
215 return QModelIndex();
216}
217
218/*!
219 \reimp
220*/
221QRect QColumnView::visualRect(const QModelIndex &index) const
222{
223 if (!index.isValid())
224 return QRect();
225
226 Q_D(const QColumnView);
227 for (int i = 0; i < d->columns.size(); ++i) {
228 QRect rect = d->columns.at(i)->visualRect(index);
229 if (!rect.isNull()) {
230 rect.translate(d->columns.at(i)->frameGeometry().topLeft());
231 return rect;
232 }
233 }
234 return QRect();
235}
236
237/*!
238 \reimp
239 */
240void QColumnView::scrollContentsBy(int dx, int dy)
241{
242 Q_D(QColumnView);
243 if (d->columns.isEmpty() || dx == 0)
244 return;
245
246 dx = isRightToLeft() ? -dx : dx;
247 for (int i = 0; i < d->columns.count(); ++i)
248 d->columns.at(i)->move(d->columns.at(i)->x() + dx, 0);
249 d->offset += dx;
250 QAbstractItemView::scrollContentsBy(dx, dy);
251}
252
253/*!
254 \reimp
255*/
256void QColumnView::scrollTo(const QModelIndex &index, ScrollHint hint)
257{
258 Q_D(QColumnView);
259 Q_UNUSED(hint);
260 if (!index.isValid() || d->columns.isEmpty())
261 return;
262
263 if (d->currentAnimation.state() == QTimeLine::Running)
264 return;
265
266 d->currentAnimation.stop();
267
268 // Fill up what is needed to get to index
269 d->closeColumns(index, true);
270
271 QModelIndex indexParent = index.parent();
272 // Find the left edge of the column that contains index
273 int currentColumn = 0;
274 int leftEdge = 0;
275 while (currentColumn < d->columns.size()) {
276 if (indexParent == d->columns.at(currentColumn)->rootIndex())
277 break;
278 leftEdge += d->columns.at(currentColumn)->width();
279 ++currentColumn;
280 }
281
282 // Don't let us scroll above the root index
283 if (currentColumn == d->columns.size())
284 return;
285
286 int indexColumn = currentColumn;
287 // Find the width of what we want to show (i.e. the right edge)
288 int visibleWidth = d->columns.at(currentColumn)->width();
289 // We want to always try to show two columns
290 if (currentColumn + 1 < d->columns.size()) {
291 ++currentColumn;
292 visibleWidth += d->columns.at(currentColumn)->width();
293 }
294
295 int rightEdge = leftEdge + visibleWidth;
296 if (isRightToLeft()) {
297 leftEdge = viewport()->width() - leftEdge;
298 rightEdge = leftEdge - visibleWidth;
299 qSwap(rightEdge, leftEdge);
300 }
301
302 // If it is already visible don't animate
303 if (leftEdge > -horizontalOffset()
304 && rightEdge <= ( -horizontalOffset() + viewport()->size().width())) {
305 d->columns.at(indexColumn)->scrollTo(index);
306 d->_q_changeCurrentColumn();
307 return;
308 }
309
310 int newScrollbarValue = 0;
311 if (isRightToLeft()) {
312 if (leftEdge < 0) {
313 // scroll to the right
314 newScrollbarValue = viewport()->size().width() - leftEdge;
315 } else {
316 // scroll to the left
317 newScrollbarValue = rightEdge + horizontalOffset();
318 }
319 } else {
320 if (leftEdge > -horizontalOffset()) {
321 // scroll to the right
322 newScrollbarValue = rightEdge - viewport()->size().width();
323 } else {
324 // scroll to the left
325 newScrollbarValue = leftEdge;
326 }
327 }
328
329 //horizontalScrollBar()->setValue(newScrollbarValue);
330 //d->_q_changeCurrentColumn();
331 //return;
332 // or do the following currentAnimation
333
334 int oldValue = horizontalScrollBar()->value();
335
336 if (oldValue < newScrollbarValue) {
337 d->currentAnimation.setFrameRange(oldValue, newScrollbarValue);
338 d->currentAnimation.setDirection(QTimeLine::Forward);
339 d->currentAnimation.setCurrentTime(0);
340 } else {
341 d->currentAnimation.setFrameRange(newScrollbarValue, oldValue);
342 d->currentAnimation.setDirection(QTimeLine::Backward);
343 }
344 d->currentAnimation.start();
345}
346
347/*!
348 \reimp
349 Move left should go to the parent index
350 Move right should go to the child index or down if there is no child
351*/
352QModelIndex QColumnView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
353{
354 // the child views which have focus get to deal with this first and if
355 // they don't accept it then it comes up this view and we only grip left/right
356 Q_UNUSED(modifiers);
357 if (!model())
358 return QModelIndex();
359
360 QModelIndex current = currentIndex();
361 if (isRightToLeft()) {
362 if (cursorAction == MoveLeft)
363 cursorAction = MoveRight;
364 else if (cursorAction == MoveRight)
365 cursorAction = MoveLeft;
366 }
367 switch (cursorAction) {
368 case MoveLeft:
369 if (current.parent().isValid() && current.parent() != rootIndex())
370 return (current.parent());
371 else
372 return current;
373 break;
374
375 case MoveRight:
376 if (model()->hasChildren(current))
377 return model()->index(0, 0, current);
378 else
379 return current.sibling(current.row() + 1, current.column());
380 break;
381
382 default:
383 break;
384 }
385
386 return QModelIndex();
387}
388
389/*!
390 \reimp
391*/
392void QColumnView::resizeEvent(QResizeEvent *event)
393{
394 Q_D(QColumnView);
395 d->doLayout();
396 d->updateScrollbars();
397 if (!isRightToLeft()) {
398 int diff = event->oldSize().width() - event->size().width();
399 if (diff < 0 && horizontalScrollBar()->isVisible()
400 && horizontalScrollBar()->value() == horizontalScrollBar()->maximum()) {
401 horizontalScrollBar()->setMaximum(horizontalScrollBar()->maximum() + diff);
402 }
403 }
404 QAbstractItemView::resizeEvent(event);
405}
406
407/*!
408 \internal
409*/
410void QColumnViewPrivate::updateScrollbars()
411{
412 Q_Q(QColumnView);
413 if (currentAnimation.state() == QTimeLine::Running)
414 return;
415
416 // find the total horizontal length of the laid out columns
417 int horizontalLength = 0;
418 if (!columns.isEmpty()) {
419 horizontalLength = (columns.last()->x() + columns.last()->width()) - columns.first()->x();
420 if (horizontalLength <= 0) // reverse mode
421 horizontalLength = (columns.first()->x() + columns.first()->width()) - columns.last()->x();
422 }
423
424 QSize viewportSize = q->viewport()->size();
425 if (horizontalLength < viewportSize.width() && q->horizontalScrollBar()->value() == 0) {
426 q->horizontalScrollBar()->setRange(0, 0);
427 } else {
428 int visibleLength = qMin(horizontalLength + q->horizontalOffset(), viewportSize.width());
429 int hiddenLength = horizontalLength - visibleLength;
430 if (hiddenLength != q->horizontalScrollBar()->maximum())
431 q->horizontalScrollBar()->setRange(0, hiddenLength);
432 }
433 if (!columns.isEmpty()) {
434 int pageStepSize = columns.at(0)->width();
435 if (pageStepSize != q->horizontalScrollBar()->pageStep())
436 q->horizontalScrollBar()->setPageStep(pageStepSize);
437 }
438 bool visible = (q->horizontalScrollBar()->maximum() > 0);
439 if (visible != q->horizontalScrollBar()->isVisible())
440 q->horizontalScrollBar()->setVisible(visible);
441}
442
443/*!
444 \reimp
445*/
446int QColumnView::horizontalOffset() const
447{
448 Q_D(const QColumnView);
449 return d->offset;
450}
451
452/*!
453 \reimp
454*/
455int QColumnView::verticalOffset() const
456{
457 return 0;
458}
459
460/*!
461 \reimp
462*/
463QRegion QColumnView::visualRegionForSelection(const QItemSelection &selection) const
464{
465 int ranges = selection.count();
466
467 if (ranges == 0)
468 return QRect();
469
470 // Note that we use the top and bottom functions of the selection range
471 // since the data is stored in rows.
472 int firstRow = selection.at(0).top();
473 int lastRow = selection.at(0).top();
474 for (int i = 0; i < ranges; ++i) {
475 firstRow = qMin(firstRow, selection.at(i).top());
476 lastRow = qMax(lastRow, selection.at(i).bottom());
477 }
478
479 QModelIndex firstIdx = model()->index(qMin(firstRow, lastRow), 0, rootIndex());
480 QModelIndex lastIdx = model()->index(qMax(firstRow, lastRow), 0, rootIndex());
481
482 if (firstIdx == lastIdx)
483 return visualRect(firstIdx);
484
485 QRegion firstRegion = visualRect(firstIdx);
486 QRegion lastRegion = visualRect(lastIdx);
487 return firstRegion.unite(lastRegion);
488}
489
490/*!
491 \reimp
492*/
493void QColumnView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
494{
495 Q_UNUSED(rect);
496 Q_UNUSED(command);
497}
498
499/*!
500 \reimp
501*/
502void QColumnView::setSelectionModel(QItemSelectionModel *newSelectionModel)
503{
504 Q_D(const QColumnView);
505 for (int i = 0; i < d->columns.size(); ++i) {
506 if (d->columns.at(i)->selectionModel() == selectionModel()) {
507 d->columns.at(i)->setSelectionModel(newSelectionModel);
508 break;
509 }
510 }
511 QAbstractItemView::setSelectionModel(newSelectionModel);
512}
513
514/*!
515 \reimp
516*/
517QSize QColumnView::sizeHint() const
518{
519 Q_D(const QColumnView);
520 QSize sizeHint;
521 for (int i = 0; i < d->columns.size(); ++i) {
522 sizeHint += d->columns.at(i)->sizeHint();
523 }
524 return sizeHint.expandedTo(QAbstractItemView::sizeHint());
525}
526
527/*!
528 \internal
529 Move all widgets from the corner grip and to the right
530 */
531void QColumnViewPrivate::_q_gripMoved(int offset)
532{
533 Q_Q(QColumnView);
534
535 QObject *grip = q->sender();
536 Q_ASSERT(grip);
537
538 if (q->isRightToLeft())
539 offset = -1 * offset;
540
541 bool found = false;
542 for (int i = 0; i < columns.size(); ++i) {
543 if (!found && columns.at(i)->cornerWidget() == grip) {
544 found = true;
545 columnSizes[i] = columns.at(i)->width();
546 if (q->isRightToLeft())
547 columns.at(i)->move(columns.at(i)->x() + offset, 0);
548 continue;
549 }
550 if (!found)
551 continue;
552
553 int currentX = columns.at(i)->x();
554 columns.at(i)->move(currentX + offset, 0);
555 }
556
557 updateScrollbars();
558}
559
560/*!
561 \internal
562
563 Find where the current columns intersect parent's columns
564
565 Delete any extra columns and insert any needed columns.
566 */
567void QColumnViewPrivate::closeColumns(const QModelIndex &parent, bool build)
568{
569 if (columns.isEmpty())
570 return;
571
572 bool clearAll = !parent.isValid();
573 bool passThroughRoot = false;
574
575 QList<QModelIndex> dirsToAppend;
576
577 // Find the last column that matches the parent's tree
578 int currentColumn = -1;
579 QModelIndex parentIndex = parent;
580 while (currentColumn == -1 && parentIndex.isValid()) {
581 if (columns.isEmpty())
582 break;
583 parentIndex = parentIndex.parent();
584 if (root == parentIndex)
585 passThroughRoot = true;
586 if (!parentIndex.isValid())
587 break;
588 for (int i = columns.size() - 1; i >= 0; --i) {
589 if (columns.at(i)->rootIndex() == parentIndex) {
590 currentColumn = i;
591 break;
592 }
593 }
594 if (currentColumn == -1)
595 dirsToAppend.append(parentIndex);
596 }
597
598 // Someone wants to go to an index that can be reached without changing
599 // the root index, don't allow them
600 if (!clearAll && !passThroughRoot && currentColumn == -1)
601 return;
602
603 if (currentColumn == -1 && parent.isValid())
604 currentColumn = 0;
605
606 // Optimization so we don't go deleting and then creating the same thing
607 bool alreadyExists = false;
608 if (build && columns.size() > currentColumn + 1) {
609 bool viewingParent = (columns.at(currentColumn + 1)->rootIndex() == parent);
610 bool viewingChild = (!model->hasChildren(parent)
611 && !columns.at(currentColumn + 1)->rootIndex().isValid());
612 if (viewingParent || viewingChild) {
613 currentColumn++;
614 alreadyExists = true;
615 }
616 }
617
618 // Delete columns that don't match our path
619 for (int i = columns.size() - 1; i > currentColumn; --i) {
620 QAbstractItemView* notShownAnymore = columns.at(i);
621 columns.removeAt(i);
622 notShownAnymore->setVisible(false);
623 if (notShownAnymore != previewColumn)
624 notShownAnymore->deleteLater();
625 }
626
627 if (columns.isEmpty()) {
628 offset = 0;
629 updateScrollbars();
630 }
631
632 // Now fill in missing columns
633 while (!dirsToAppend.isEmpty()) {
634 QAbstractItemView *newView = createColumn(dirsToAppend.takeLast(), true);
635 if (!dirsToAppend.isEmpty())
636 newView->setCurrentIndex(dirsToAppend.last());
637 }
638
639 if (build && !alreadyExists)
640 createColumn(parent, false);
641}
642
643void QColumnViewPrivate::_q_clicked(const QModelIndex &index)
644{
645 Q_Q(QColumnView);
646 QModelIndex parent = index.parent();
647 QAbstractItemView *columnClicked = 0;
648 for (int column = 0; column < columns.count(); ++column) {
649 if (columns.at(column)->rootIndex() == parent) {
650 columnClicked = columns[column];
651 break;
652 }
653 }
654 if (q->selectionModel() && columnClicked) {
655 QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::Current;
656 if (columnClicked->selectionModel()->isSelected(index))
657 flags |= QItemSelectionModel::Select;
658 q->selectionModel()->setCurrentIndex(index, flags);
659 }
660}
661
662/*!
663 \internal
664 Create a new column for \a index. A grip is attached if requested and it is shown
665 if requested.
666
667 Return the new view
668
669 \sa createColumn() setPreviewWidget()
670 \sa doLayout()
671*/
672QAbstractItemView *QColumnViewPrivate::createColumn(const QModelIndex &index, bool show)
673{
674 Q_Q(QColumnView);
675 QAbstractItemView *view = 0;
676 if (model->hasChildren(index)) {
677 view = q->createColumn(index);
678 q->connect(view, SIGNAL(clicked(const QModelIndex &)),
679 q, SLOT(_q_clicked(const QModelIndex &)));
680 } else {
681 if (!previewColumn)
682 setPreviewWidget(new QWidget(q));
683 view = previewColumn;
684 view->setMinimumWidth(qMax(view->minimumWidth(), previewWidget->minimumWidth()));
685 }
686
687 q->connect(view, SIGNAL(activated(const QModelIndex &)),
688 q, SIGNAL(activated(const QModelIndex &)));
689 q->connect(view, SIGNAL(clicked(const QModelIndex &)),
690 q, SIGNAL(clicked(const QModelIndex &)));
691 q->connect(view, SIGNAL(doubleClicked(const QModelIndex &)),
692 q, SIGNAL(doubleClicked(const QModelIndex &)));
693 q->connect(view, SIGNAL(entered(const QModelIndex &)),
694 q, SIGNAL(entered(const QModelIndex &)));
695 q->connect(view, SIGNAL(pressed(const QModelIndex &)),
696 q, SIGNAL(pressed(const QModelIndex &)));
697
698 view->setFocusPolicy(Qt::NoFocus);
699 view->setParent(q->viewport());
700 Q_ASSERT(view);
701
702 // Setup corner grip
703 if (showResizeGrips) {
704 QColumnViewGrip *grip = new QColumnViewGrip(view);
705 view->setCornerWidget(grip);
706 q->connect(grip, SIGNAL(gripMoved(int)), q, SLOT(_q_gripMoved(int)));
707 }
708
709 if (columnSizes.count() > columns.count()) {
710 view->setGeometry(0, 0, columnSizes.at(columns.count()), q->viewport()->height());
711 } else {
712 int initialWidth = view->sizeHint().width();
713 if (q->isRightToLeft())
714 view->setGeometry(q->viewport()->width() - initialWidth, 0, initialWidth, q->viewport()->height());
715 else
716 view->setGeometry(0, 0, initialWidth, q->viewport()->height());
717 columnSizes.resize(qMax(columnSizes.count(), columns.count() + 1));
718 columnSizes[columns.count()] = initialWidth;
719 }
720 if (!columns.isEmpty() && columns.last()->isHidden())
721 columns.last()->setVisible(true);
722
723 columns.append(view);
724 doLayout();
725 updateScrollbars();
726 if (show && view->isHidden())
727 view->setVisible(true);
728 return view;
729}
730
731/*!
732 \fn void QColumnView::updatePreviewWidget(const QModelIndex &index)
733
734 This signal is emitted when the preview widget should be updated to
735 provide rich information about \a index
736
737 \sa previewWidget()
738 */
739
740/*!
741 To use a custom widget for the final column when you select
742 an item overload this function and return a widget.
743 \a index is the root index that will be assigned to the view.
744
745 Return the new view. QColumnView will automatically take ownership of the widget.
746
747 \sa setPreviewWidget()
748 */
749QAbstractItemView *QColumnView::createColumn(const QModelIndex &index)
750{
751 QListView *view = new QListView(viewport());
752
753 initializeColumn(view);
754
755 view->setRootIndex(index);
756 if (model()->canFetchMore(index))
757 model()->fetchMore(index);
758
759 return view;
760}
761
762/*!
763 Copies the behavior and options of the column view and applies them to
764 the \a column such as the iconSize(), textElideMode() and
765 alternatingRowColors(). This can be useful when reimplementing
766 createColumn().
767
768 \since 4.4
769 \sa createColumn()
770 */
771void QColumnView::initializeColumn(QAbstractItemView *column) const
772{
773 Q_D(const QColumnView);
774
775 column->setFrameShape(QFrame::NoFrame);
776 column->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
777 column->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
778 column->setMinimumWidth(100);
779 column->setAttribute(Qt::WA_MacShowFocusRect, false);
780
781#ifndef QT_NO_DRAGANDDROP
782 column->setDragDropMode(dragDropMode());
783 column->setDragDropOverwriteMode(dragDropOverwriteMode());
784 column->setDropIndicatorShown(showDropIndicator());
785#endif
786 column->setAlternatingRowColors(alternatingRowColors());
787 column->setAutoScroll(hasAutoScroll());
788 column->setEditTriggers(editTriggers());
789 column->setHorizontalScrollMode(horizontalScrollMode());
790 column->setIconSize(iconSize());
791 column->setSelectionBehavior(selectionBehavior());
792 column->setSelectionMode(selectionMode());
793 column->setTabKeyNavigation(tabKeyNavigation());
794 column->setTextElideMode(textElideMode());
795 column->setVerticalScrollMode(verticalScrollMode());
796
797 column->setModel(model());
798
799 // Copy the custom delegate per row
800 QMapIterator<int, QPointer<QAbstractItemDelegate> > i(d->rowDelegates);
801 while (i.hasNext()) {
802 i.next();
803 column->setItemDelegateForRow(i.key(), i.value());
804 }
805
806 // set the delegate to be the columnview delegate
807 QAbstractItemDelegate *delegate = column->itemDelegate();
808 column->setItemDelegate(d->itemDelegate);
809 delete delegate;
810}
811
812/*!
813 Returns the preview widget, or 0 if there is none.
814
815 \sa setPreviewWidget(), updatePreviewWidget()
816*/
817QWidget *QColumnView::previewWidget() const
818{
819 Q_D(const QColumnView);
820 return d->previewWidget;
821}
822
823/*!
824 Sets the preview \a widget.
825
826 The \a widget becomes a child of the column view, and will be
827 destroyed when the column area is deleted or when a new widget is
828 set.
829
830 \sa previewWidget(), updatePreviewWidget()
831*/
832void QColumnView::setPreviewWidget(QWidget *widget)
833{
834 Q_D(QColumnView);
835 d->setPreviewWidget(widget);
836}
837
838/*!
839 \internal
840*/
841void QColumnViewPrivate::setPreviewWidget(QWidget *widget)
842{
843 Q_Q(QColumnView);
844 if (previewColumn) {
845 if (!columns.isEmpty() && columns.last() == previewColumn)
846 columns.removeLast();
847 previewColumn->deleteLater();
848 }
849 QColumnViewPreviewColumn *column = new QColumnViewPreviewColumn(q);
850 column->setPreviewWidget(widget);
851 previewColumn = column;
852 previewColumn->hide();
853 previewColumn->setFrameShape(QFrame::NoFrame);
854 previewColumn->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
855 previewColumn->setSelectionMode(QAbstractItemView::NoSelection);
856 previewColumn->setMinimumWidth(qMax(previewColumn->verticalScrollBar()->width(),
857 previewColumn->minimumWidth()));
858 previewWidget = widget;
859 previewWidget->setParent(previewColumn->viewport());
860}
861
862/*!
863 Sets the column widths to the values given in the \a list. Extra values in the list are
864 kept and used when the columns are created.
865
866 If list contains too few values, only width of the rest of the columns will not be modified.
867
868 \sa columnWidths(), createColumn()
869*/
870void QColumnView::setColumnWidths(const QList<int> &list)
871{
872 Q_D(QColumnView);
873 int i = 0;
874 for (; (i < list.count() && i < d->columns.count()); ++i) {
875 d->columns.at(i)->resize(list.at(i), d->columns.at(i)->height());
876 d->columnSizes[i] = list.at(i);
877 }
878 for (; i < list.count(); ++i)
879 d->columnSizes.append(list.at(i));
880}
881
882/*!
883 Returns a list of the width of all the columns in this view.
884
885 \sa setColumnWidths()
886*/
887QList<int> QColumnView::columnWidths() const
888{
889 Q_D(const QColumnView);
890 QList<int> list;
891 for (int i = 0; i < d->columns.count(); ++i)
892 list.append(d->columnSizes.at(i));
893 return list;
894}
895
896/*!
897 \reimp
898*/
899void QColumnView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
900{
901 Q_D(QColumnView);
902 if (!current.isValid()) {
903 QAbstractItemView::currentChanged(current, previous);
904 return;
905 }
906
907 QModelIndex currentParent = current.parent();
908 // optimize for just moving up/down in a list where the child view doesn't change
909 if (currentParent == previous.parent()
910 && model()->hasChildren(current) && model()->hasChildren(previous)) {
911 for (int i = 0; i < d->columns.size(); ++i) {
912 if (currentParent == d->columns.at(i)->rootIndex()) {
913 if (d->columns.size() > i + 1) {
914 QAbstractItemView::currentChanged(current, previous);
915 return;
916 }
917 break;
918 }
919 }
920 }
921
922 // Scrolling to the right we need to have an empty spot
923 bool found = false;
924 if (currentParent == previous) {
925 for (int i = 0; i < d->columns.size(); ++i) {
926 if (currentParent == d->columns.at(i)->rootIndex()) {
927 found = true;
928 if (d->columns.size() < i + 2) {
929 d->createColumn(current, false);
930 }
931 break;
932 }
933 }
934 }
935 if (!found)
936 d->closeColumns(current, true);
937
938 if (!model()->hasChildren(current))
939 emit updatePreviewWidget(current);
940
941 QAbstractItemView::currentChanged(current, previous);
942}
943
944/*
945 We have change the current column and need to update focus and selection models
946 on the new current column.
947*/
948void QColumnViewPrivate::_q_changeCurrentColumn()
949{
950 Q_Q(QColumnView);
951 if (columns.isEmpty())
952 return;
953
954 QModelIndex current = q->currentIndex();
955 if (!current.isValid())
956 return;
957
958 // We might have scrolled far to the left so we need to close all of the children
959 closeColumns(current, true);
960
961 // Set up the "current" column with focus
962 int currentColumn = qMax(0, columns.size() - 2);
963 QAbstractItemView *parentColumn = columns.at(currentColumn);
964 if (q->hasFocus())
965 parentColumn->setFocus(Qt::OtherFocusReason);
966 q->setFocusProxy(parentColumn);
967
968 // find the column that is our current selection model and give it a new one.
969 for (int i = 0; i < columns.size(); ++i) {
970 if (columns.at(i)->selectionModel() == q->selectionModel()) {
971 QItemSelectionModel *replacementSelectionModel =
972 new QItemSelectionModel(parentColumn->model());
973 replacementSelectionModel->setCurrentIndex(
974 q->selectionModel()->currentIndex(), QItemSelectionModel::Current);
975 replacementSelectionModel->select(
976 q->selectionModel()->selection(), QItemSelectionModel::Select);
977 QAbstractItemView *view = columns.at(i);
978 view->setSelectionModel(replacementSelectionModel);
979 view->setFocusPolicy(Qt::NoFocus);
980 if (columns.size() > i + 1)
981 view->setCurrentIndex(columns.at(i+1)->rootIndex());
982 break;
983 }
984 }
985 parentColumn->selectionModel()->deleteLater();
986 parentColumn->setFocusPolicy(Qt::StrongFocus);
987 parentColumn->setSelectionModel(q->selectionModel());
988 // We want the parent selection to stay highlighted (but dimmed depending upon the color theme)
989 if (currentColumn > 0) {
990 parentColumn = columns.at(currentColumn - 1);
991 if (parentColumn->currentIndex() != current.parent())
992 parentColumn->setCurrentIndex(current.parent());
993 }
994
995 if (columns.last()->isHidden()) {
996 columns.last()->setVisible(true);
997 }
998 if (columns.last()->selectionModel())
999 columns.last()->selectionModel()->clear();
1000 updateScrollbars();
1001}
1002
1003/*!
1004 \reimp
1005*/
1006void QColumnView::selectAll()
1007{
1008 if (!model() || !selectionModel())
1009 return;
1010
1011 QModelIndexList indexList = selectionModel()->selectedIndexes();
1012 QModelIndex parent = rootIndex();
1013 QItemSelection selection;
1014 if (indexList.count() >= 1)
1015 parent = indexList.at(0).parent();
1016 if (indexList.count() == 1) {
1017 parent = indexList.at(0);
1018 if (!model()->hasChildren(parent))
1019 parent = parent.parent();
1020 else
1021 selection.append(QItemSelectionRange(parent, parent));
1022 }
1023
1024 QModelIndex tl = model()->index(0, 0, parent);
1025 QModelIndex br = model()->index(model()->rowCount(parent) - 1,
1026 model()->columnCount(parent) - 1,
1027 parent);
1028 selection.append(QItemSelectionRange(tl, br));
1029 selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect);
1030}
1031
1032/*
1033 * private object implementation
1034 */
1035QColumnViewPrivate::QColumnViewPrivate()
1036: QAbstractItemViewPrivate()
1037,showResizeGrips(true)
1038,offset(0)
1039,currentAnimation(ANIMATION_DURATION_MSEC)
1040,previewWidget(0)
1041,previewColumn(0)
1042{
1043}
1044
1045QColumnViewPrivate::~QColumnViewPrivate()
1046{
1047}
1048
1049/*!
1050 \internal
1051 Place all of the columns where they belong inside of the viewport, resize as necessary.
1052*/
1053void QColumnViewPrivate::doLayout()
1054{
1055 Q_Q(QColumnView);
1056 if (!model || columns.isEmpty())
1057 return;
1058
1059 int viewportHeight = q->viewport()->height();
1060 int x = columns.at(0)->x();
1061
1062 if (q->isRightToLeft()) {
1063 x = q->viewport()->width() + q->horizontalOffset();
1064 for (int i = 0; i < columns.size(); ++i) {
1065 QAbstractItemView *view = columns.at(i);
1066 x -= view->width();
1067 if (x != view->x() || viewportHeight != view->height())
1068 view->setGeometry(x, 0, view->width(), viewportHeight);
1069 }
1070 } else {
1071 for (int i = 0; i < columns.size(); ++i) {
1072 QAbstractItemView *view = columns.at(i);
1073 int currentColumnWidth = view->width();
1074 if (x != view->x() || viewportHeight != view->height())
1075 view->setGeometry(x, 0, currentColumnWidth, viewportHeight);
1076 x += currentColumnWidth;
1077 }
1078 }
1079}
1080
1081/*!
1082 \internal
1083
1084 Draws a delegate with a > if an object has children.
1085
1086 \sa {Model/View Programming}, QItemDelegate
1087*/
1088void QColumnViewDelegate::paint(QPainter *painter,
1089 const QStyleOptionViewItem &option,
1090 const QModelIndex &index) const
1091{
1092 drawBackground(painter, option, index );
1093
1094 bool reverse = (option.direction == Qt::RightToLeft);
1095 int width = ((option.rect.height() * 2) / 3);
1096 // Modify the options to give us room to add an arrow
1097 QStyleOptionViewItemV4 opt = option;
1098 if (reverse)
1099 opt.rect.adjust(width,0,0,0);
1100 else
1101 opt.rect.adjust(0,0,-width,0);
1102
1103 if (!(index.model()->flags(index) & Qt::ItemIsEnabled)) {
1104 opt.showDecorationSelected = true;
1105 opt.state |= QStyle::State_Selected;
1106 }
1107
1108 QItemDelegate::paint(painter, opt, index);
1109
1110 if (reverse)
1111 opt.rect = QRect(option.rect.x(), option.rect.y(), width, option.rect.height());
1112 else
1113 opt.rect = QRect(option.rect.x() + option.rect.width() - width, option.rect.y(),
1114 width, option.rect.height());
1115
1116 // Draw >
1117 if (index.model()->hasChildren(index)) {
1118 const QWidget *view = opt.widget;
1119 QStyle *style = view ? view->style() : qApp->style();
1120 style->drawPrimitive(QStyle::PE_IndicatorColumnViewArrow, &opt, painter, view);
1121 }
1122}
1123
1124QT_END_NAMESPACE
1125
1126#include "moc_qcolumnview.cpp"
1127
1128#endif // QT_NO_COLUMNVIEW
Note: See TracBrowser for help on using the repository browser.