|
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 "qabstractitemview.h" |
|
43 |
|
44 #ifndef QT_NO_ITEMVIEWS |
|
45 #include <qpointer.h> |
|
46 #include <qapplication.h> |
|
47 #include <qclipboard.h> |
|
48 #include <qpainter.h> |
|
49 #include <qstyle.h> |
|
50 #include <qdrag.h> |
|
51 #include <qevent.h> |
|
52 #include <qscrollbar.h> |
|
53 #include <qwhatsthis.h> |
|
54 #include <qtooltip.h> |
|
55 #include <qdatetime.h> |
|
56 #include <qlineedit.h> |
|
57 #include <qspinbox.h> |
|
58 #include <qstyleditemdelegate.h> |
|
59 #include <private/qabstractitemview_p.h> |
|
60 #include <private/qabstractitemmodel_p.h> |
|
61 #ifndef QT_NO_ACCESSIBILITY |
|
62 #include <qaccessible.h> |
|
63 #endif |
|
64 #include <private/qsoftkeymanager_p.h> |
|
65 |
|
66 QT_BEGIN_NAMESPACE |
|
67 |
|
68 QAbstractItemViewPrivate::QAbstractItemViewPrivate() |
|
69 : model(QAbstractItemModelPrivate::staticEmptyModel()), |
|
70 itemDelegate(0), |
|
71 selectionModel(0), |
|
72 ctrlDragSelectionFlag(QItemSelectionModel::NoUpdate), |
|
73 selectionMode(QAbstractItemView::ExtendedSelection), |
|
74 selectionBehavior(QAbstractItemView::SelectItems), |
|
75 currentlyCommittingEditor(0), |
|
76 pressedModifiers(Qt::NoModifier), |
|
77 pressedPosition(QPoint(-1, -1)), |
|
78 pressedAlreadySelected(false), |
|
79 viewportEnteredNeeded(false), |
|
80 state(QAbstractItemView::NoState), |
|
81 editTriggers(QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed), |
|
82 lastTrigger(QAbstractItemView::NoEditTriggers), |
|
83 tabKeyNavigation(false), |
|
84 #ifndef QT_NO_DRAGANDDROP |
|
85 showDropIndicator(true), |
|
86 dragEnabled(false), |
|
87 dragDropMode(QAbstractItemView::NoDragDrop), |
|
88 overwrite(false), |
|
89 dropIndicatorPosition(QAbstractItemView::OnItem), |
|
90 defaultDropAction(Qt::IgnoreAction), |
|
91 #endif |
|
92 #ifdef QT_SOFTKEYS_ENABLED |
|
93 doneSoftKey(0), |
|
94 #endif |
|
95 autoScroll(true), |
|
96 autoScrollMargin(16), |
|
97 autoScrollCount(0), |
|
98 shouldScrollToCurrentOnShow(false), |
|
99 alternatingColors(false), |
|
100 textElideMode(Qt::ElideRight), |
|
101 verticalScrollMode(QAbstractItemView::ScrollPerItem), |
|
102 horizontalScrollMode(QAbstractItemView::ScrollPerItem), |
|
103 currentIndexSet(false), |
|
104 wrapItemText(false), |
|
105 delayedPendingLayout(false) |
|
106 { |
|
107 } |
|
108 |
|
109 QAbstractItemViewPrivate::~QAbstractItemViewPrivate() |
|
110 { |
|
111 } |
|
112 |
|
113 void QAbstractItemViewPrivate::init() |
|
114 { |
|
115 Q_Q(QAbstractItemView); |
|
116 q->setItemDelegate(new QStyledItemDelegate(q)); |
|
117 |
|
118 vbar->setRange(0, 0); |
|
119 hbar->setRange(0, 0); |
|
120 |
|
121 QObject::connect(vbar, SIGNAL(actionTriggered(int)), |
|
122 q, SLOT(verticalScrollbarAction(int))); |
|
123 QObject::connect(hbar, SIGNAL(actionTriggered(int)), |
|
124 q, SLOT(horizontalScrollbarAction(int))); |
|
125 QObject::connect(vbar, SIGNAL(valueChanged(int)), |
|
126 q, SLOT(verticalScrollbarValueChanged(int))); |
|
127 QObject::connect(hbar, SIGNAL(valueChanged(int)), |
|
128 q, SLOT(horizontalScrollbarValueChanged(int))); |
|
129 |
|
130 viewport->setBackgroundRole(QPalette::Base); |
|
131 |
|
132 doDelayedItemsLayout(); |
|
133 |
|
134 q->setAttribute(Qt::WA_InputMethodEnabled); |
|
135 |
|
136 #ifdef QT_SOFTKEYS_ENABLED |
|
137 doneSoftKey = QSoftKeyManager::createKeyedAction(QSoftKeyManager::DoneSoftKey, Qt::Key_Back, q); |
|
138 #endif |
|
139 } |
|
140 |
|
141 void QAbstractItemViewPrivate::checkMouseMove(const QPersistentModelIndex &index) |
|
142 { |
|
143 //we take a persistent model index because the model might change by emitting signals |
|
144 Q_Q(QAbstractItemView); |
|
145 if (viewportEnteredNeeded || enteredIndex != index) { |
|
146 viewportEnteredNeeded = false; |
|
147 |
|
148 if (index.isValid()) { |
|
149 emit q->entered(index); |
|
150 #ifndef QT_NO_STATUSTIP |
|
151 QString statustip = model->data(index, Qt::StatusTipRole).toString(); |
|
152 if (parent && !statustip.isEmpty()) { |
|
153 QStatusTipEvent tip(statustip); |
|
154 QApplication::sendEvent(parent, &tip); |
|
155 } |
|
156 #endif |
|
157 } else { |
|
158 #ifndef QT_NO_STATUSTIP |
|
159 if (parent) { |
|
160 QString emptyString; |
|
161 QStatusTipEvent tip( emptyString ); |
|
162 QApplication::sendEvent(parent, &tip); |
|
163 } |
|
164 #endif |
|
165 emit q->viewportEntered(); |
|
166 } |
|
167 enteredIndex = index; |
|
168 } |
|
169 } |
|
170 |
|
171 |
|
172 /*! |
|
173 \class QAbstractItemView |
|
174 |
|
175 \brief The QAbstractItemView class provides the basic functionality for |
|
176 item view classes. |
|
177 |
|
178 \ingroup model-view |
|
179 |
|
180 |
|
181 QAbstractItemView class is the base class for every standard view |
|
182 that uses a QAbstractItemModel. QAbstractItemView is an abstract |
|
183 class and cannot itself be instantiated. It provides a standard |
|
184 interface for interoperating with models through the signals and |
|
185 slots mechanism, enabling subclasses to be kept up-to-date with |
|
186 changes to their models. This class provides standard support for |
|
187 keyboard and mouse navigation, viewport scrolling, item editing, |
|
188 and selections. The keyboard navigation implements this |
|
189 functionality: |
|
190 |
|
191 \table |
|
192 \header |
|
193 \o Keys |
|
194 \o Functionality |
|
195 \row |
|
196 \o Arrow keys |
|
197 \o Changes the current item and selects it. |
|
198 \row |
|
199 \o Ctrl+Arrow keys |
|
200 \o Changes the current item but does not select it. |
|
201 \row |
|
202 \o Shift+Arrow keys |
|
203 \o Changes the current item and selects it. The previously |
|
204 selected item(s) is not deselected. |
|
205 \row |
|
206 \o Ctr+Space |
|
207 \o Toggles selection of the current item. |
|
208 \row |
|
209 \o Tab/Backtab |
|
210 \o Changes the current item to the next/previous item. |
|
211 \row |
|
212 \o Home/End |
|
213 \o Selects the first/last item in the model. |
|
214 \row |
|
215 \o Page up/Page down |
|
216 \o Scrolls the rows shown up/down by the number of |
|
217 visible rows in the view. |
|
218 \row |
|
219 \o Ctrl+A |
|
220 \o Selects all items in the model. |
|
221 \endtable |
|
222 |
|
223 Note that the above table assumes that the |
|
224 \l{selectionMode}{selection mode} allows the operations. For |
|
225 instance, you cannot select items if the selection mode is |
|
226 QAbstractItemView::NoSelection. |
|
227 |
|
228 The QAbstractItemView class is one of the \l{Model/View Classes} |
|
229 and is part of Qt's \l{Model/View Programming}{model/view framework}. |
|
230 |
|
231 The view classes that inherit QAbstractItemView only need |
|
232 to implement their own view-specific functionality, such as |
|
233 drawing items, returning the geometry of items, finding items, |
|
234 etc. |
|
235 |
|
236 QAbstractItemView provides common slots such as edit() and |
|
237 setCurrentIndex(). Many protected slots are also provided, including |
|
238 dataChanged(), rowsInserted(), rowsAboutToBeRemoved(), selectionChanged(), |
|
239 and currentChanged(). |
|
240 |
|
241 The root item is returned by rootIndex(), and the current item by |
|
242 currentIndex(). To make sure that an item is visible use |
|
243 scrollTo(). |
|
244 |
|
245 Some of QAbstractItemView's functions are concerned with |
|
246 scrolling, for example setHorizontalScrollMode() and |
|
247 setVerticalScrollMode(). To set the range of the scroll bars, you |
|
248 can, for example, reimplement the view's resizeEvent() function: |
|
249 |
|
250 \snippet doc/src/snippets/code/src_gui_itemviews_qabstractitemview.cpp 0 |
|
251 |
|
252 Note that the range is not updated until the widget is shown. |
|
253 |
|
254 Several other functions are concerned with selection control; for |
|
255 example setSelectionMode(), and setSelectionBehavior(). This class |
|
256 provides a default selection model to work with |
|
257 (selectionModel()), but this can be replaced by using |
|
258 setSelectionModel() with an instance of QItemSelectionModel. |
|
259 |
|
260 For complete control over the display and editing of items you can |
|
261 specify a delegate with setItemDelegate(). |
|
262 |
|
263 QAbstractItemView provides a lot of protected functions. Some are |
|
264 concerned with editing, for example, edit(), and commitData(), |
|
265 whilst others are keyboard and mouse event handlers. |
|
266 |
|
267 \note If you inherit QAbstractItemView and intend to update the contents |
|
268 of the viewport, you should use viewport->update() instead of |
|
269 \l{QWidget::update()}{update()} as all painting operations take place on the |
|
270 viewport. |
|
271 |
|
272 \sa {View Classes}, {Model/View Programming}, QAbstractItemModel, {Chart Example} |
|
273 */ |
|
274 |
|
275 /*! |
|
276 \enum QAbstractItemView::SelectionMode |
|
277 |
|
278 This enum indicates how the view responds to user selections: |
|
279 |
|
280 \value SingleSelection When the user selects an item, any already-selected |
|
281 item becomes unselected, and the user cannot unselect the selected item by |
|
282 clicking on it. |
|
283 |
|
284 \value ContiguousSelection When the user selects an item in the usual way, |
|
285 the selection is cleared and the new item selected. However, if the user |
|
286 presses the Shift key while clicking on an item, all items between the |
|
287 current item and the clicked item are selected or unselected, depending on |
|
288 the state of the clicked item. |
|
289 |
|
290 \value ExtendedSelection When the user selects an item in the usual way, |
|
291 the selection is cleared and the new item selected. However, if the user |
|
292 presses the Ctrl key when clicking on an item, the clicked item gets |
|
293 toggled and all other items are left untouched. If the user presses the |
|
294 Shift key while clicking on an item, all items between the current item |
|
295 and the clicked item are selected or unselected, depending on the state of |
|
296 the clicked item. Multiple items can be selected by dragging the mouse over |
|
297 them. |
|
298 |
|
299 \value MultiSelection When the user selects an item in the usual way, the |
|
300 selection status of that item is toggled and the other items are left |
|
301 alone. Multiple items can be toggled by dragging the mouse over them. |
|
302 |
|
303 \value NoSelection Items cannot be selected. |
|
304 |
|
305 The most commonly used modes are SingleSelection and ExtendedSelection. |
|
306 */ |
|
307 |
|
308 /*! |
|
309 \enum QAbstractItemView::SelectionBehavior |
|
310 |
|
311 \value SelectItems Selecting single items. |
|
312 \value SelectRows Selecting only rows. |
|
313 \value SelectColumns Selecting only columns. |
|
314 */ |
|
315 |
|
316 /*! |
|
317 \enum QAbstractItemView::ScrollHint |
|
318 |
|
319 \value EnsureVisible Scroll to ensure that the item is visible. |
|
320 \value PositionAtTop Scroll to position the item at the top of the |
|
321 viewport. |
|
322 \value PositionAtBottom Scroll to position the item at the bottom of the |
|
323 viewport. |
|
324 \value PositionAtCenter Scroll to position the item at the center of the |
|
325 viewport. |
|
326 */ |
|
327 |
|
328 |
|
329 /*! |
|
330 \enum QAbstractItemView::EditTrigger |
|
331 |
|
332 This enum describes actions which will initiate item editing. |
|
333 |
|
334 \value NoEditTriggers No editing possible. |
|
335 \value CurrentChanged Editing start whenever current item changes. |
|
336 \value DoubleClicked Editing starts when an item is double clicked. |
|
337 \value SelectedClicked Editing starts when clicking on an already selected |
|
338 item. |
|
339 \value EditKeyPressed Editing starts when the platform edit key has been |
|
340 pressed over an item. |
|
341 \value AnyKeyPressed Editing starts when any key is pressed over an item. |
|
342 \value AllEditTriggers Editing starts for all above actions. |
|
343 */ |
|
344 |
|
345 /*! |
|
346 \enum QAbstractItemView::CursorAction |
|
347 |
|
348 This enum describes the different ways to navigate between items, |
|
349 \sa moveCursor() |
|
350 |
|
351 \value MoveUp Move to the item above the current item. |
|
352 \value MoveDown Move to the item below the current item. |
|
353 \value MoveLeft Move to the item left of the current item. |
|
354 \value MoveRight Move to the item right of the current item. |
|
355 \value MoveHome Move to the top-left corner item. |
|
356 \value MoveEnd Move to the bottom-right corner item. |
|
357 \value MovePageUp Move one page up above the current item. |
|
358 \value MovePageDown Move one page down below the current item. |
|
359 \value MoveNext Move to the item after the current item. |
|
360 \value MovePrevious Move to the item before the current item. |
|
361 */ |
|
362 |
|
363 /*! |
|
364 \enum QAbstractItemView::State |
|
365 |
|
366 Describes the different states the view can be in. This is usually |
|
367 only interesting when reimplementing your own view. |
|
368 |
|
369 \value NoState The is the default state. |
|
370 \value DraggingState The user is dragging items. |
|
371 \value DragSelectingState The user is selecting items. |
|
372 \value EditingState The user is editing an item in a widget editor. |
|
373 \value ExpandingState The user is opening a branch of items. |
|
374 \value CollapsingState The user is closing a branch of items. |
|
375 \value AnimatingState The item view is performing an animation. |
|
376 */ |
|
377 |
|
378 /*! |
|
379 \since 4.2 |
|
380 \enum QAbstractItemView::ScrollMode |
|
381 |
|
382 \value ScrollPerItem The view will scroll the contents one item at a time. |
|
383 \value ScrollPerPixel The view will scroll the contents one pixel at a time. |
|
384 */ |
|
385 |
|
386 /*! |
|
387 \fn QRect QAbstractItemView::visualRect(const QModelIndex &index) const = 0 |
|
388 Returns the rectangle on the viewport occupied by the item at \a index. |
|
389 |
|
390 If your item is displayed in several areas then visualRect should return |
|
391 the primary area that contains index and not the complete area that index |
|
392 might encompasses, touch or cause drawing. |
|
393 |
|
394 In the base class this is a pure virtual function. |
|
395 |
|
396 \sa indexAt(), visualRegionForSelection() |
|
397 */ |
|
398 |
|
399 /*! |
|
400 \fn void QAbstractItemView::scrollTo(const QModelIndex &index, ScrollHint hint) = 0 |
|
401 |
|
402 Scrolls the view if necessary to ensure that the item at \a index |
|
403 is visible. The view will try to position the item according to the given \a hint. |
|
404 |
|
405 In the base class this is a pure virtual function. |
|
406 */ |
|
407 |
|
408 /*! |
|
409 \fn QModelIndex QAbstractItemView::indexAt(const QPoint &point) const = 0 |
|
410 |
|
411 Returns the model index of the item at the viewport coordinates \a point. |
|
412 |
|
413 In the base class this is a pure virtual function. |
|
414 |
|
415 \sa visualRect() |
|
416 */ |
|
417 |
|
418 /*! |
|
419 \fn void QAbstractItemView::activated(const QModelIndex &index) |
|
420 |
|
421 This signal is emitted when the item specified by \a index is |
|
422 activated by the user. How to activate items depends on the |
|
423 platform; e.g., by single- or double-clicking the item, or by |
|
424 pressing the Return or Enter key when the item is current. |
|
425 |
|
426 \sa clicked(), doubleClicked(), entered(), pressed() |
|
427 */ |
|
428 |
|
429 /*! |
|
430 \fn void QAbstractItemView::entered(const QModelIndex &index) |
|
431 |
|
432 This signal is emitted when the mouse cursor enters the item |
|
433 specified by \a index. |
|
434 Mouse tracking needs to be enabled for this feature to work. |
|
435 |
|
436 \sa viewportEntered(), activated(), clicked(), doubleClicked(), pressed() |
|
437 */ |
|
438 |
|
439 /*! |
|
440 \fn void QAbstractItemView::viewportEntered() |
|
441 |
|
442 This signal is emitted when the mouse cursor enters the viewport. |
|
443 Mouse tracking needs to be enabled for this feature to work. |
|
444 |
|
445 \sa entered() |
|
446 */ |
|
447 |
|
448 /*! |
|
449 \fn void QAbstractItemView::pressed(const QModelIndex &index) |
|
450 |
|
451 This signal is emitted when a mouse button is pressed. The item |
|
452 the mouse was pressed on is specified by \a index. The signal is |
|
453 only emitted when the index is valid. |
|
454 |
|
455 Use the QApplication::mouseButtons() function to get the state |
|
456 of the mouse buttons. |
|
457 |
|
458 \sa activated(), clicked(), doubleClicked(), entered() |
|
459 */ |
|
460 |
|
461 /*! |
|
462 \fn void QAbstractItemView::clicked(const QModelIndex &index) |
|
463 |
|
464 This signal is emitted when a mouse button is clicked. The item |
|
465 the mouse was clicked on is specified by \a index. The signal is |
|
466 only emitted when the index is valid. |
|
467 |
|
468 \sa activated(), doubleClicked(), entered(), pressed() |
|
469 */ |
|
470 |
|
471 /*! |
|
472 \fn void QAbstractItemView::doubleClicked(const QModelIndex &index) |
|
473 |
|
474 This signal is emitted when a mouse button is double-clicked. The |
|
475 item the mouse was double-clicked on is specified by \a index. |
|
476 The signal is only emitted when the index is valid. |
|
477 |
|
478 \sa clicked(), activated() |
|
479 */ |
|
480 |
|
481 /*! |
|
482 \fn QModelIndex QAbstractItemView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) = 0 |
|
483 |
|
484 Returns a QModelIndex object pointing to the next object in the view, |
|
485 based on the given \a cursorAction and keyboard modifiers specified |
|
486 by \a modifiers. |
|
487 |
|
488 In the base class this is a pure virtual function. |
|
489 */ |
|
490 |
|
491 /*! |
|
492 \fn int QAbstractItemView::horizontalOffset() const = 0 |
|
493 |
|
494 Returns the horizontal offset of the view. |
|
495 |
|
496 In the base class this is a pure virtual function. |
|
497 |
|
498 \sa verticalOffset() |
|
499 */ |
|
500 |
|
501 /*! |
|
502 \fn int QAbstractItemView::verticalOffset() const = 0 |
|
503 |
|
504 Returns the vertical offset of the view. |
|
505 |
|
506 In the base class this is a pure virtual function. |
|
507 |
|
508 \sa horizontalOffset() |
|
509 */ |
|
510 |
|
511 /*! |
|
512 \fn bool QAbstractItemView::isIndexHidden(const QModelIndex &index) const |
|
513 |
|
514 Returns true if the item referred to by the given \a index is hidden in the view, |
|
515 otherwise returns false. |
|
516 |
|
517 Hiding is a view specific feature. For example in TableView a column can be marked |
|
518 as hidden or a row in the TreeView. |
|
519 |
|
520 In the base class this is a pure virtual function. |
|
521 */ |
|
522 |
|
523 /*! |
|
524 \fn void QAbstractItemView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags flags) |
|
525 |
|
526 Applies the selection \a flags to the items in or touched by the |
|
527 rectangle, \a rect. |
|
528 |
|
529 When implementing your own itemview setSelection should call |
|
530 selectionModel()->select(selection, flags) where selection |
|
531 is either an empty QModelIndex or a QItemSelection that contains |
|
532 all items that are contained in \a rect. |
|
533 |
|
534 \sa selectionCommand(), selectedIndexes() |
|
535 */ |
|
536 |
|
537 /*! |
|
538 \fn QRegion QAbstractItemView::visualRegionForSelection(const QItemSelection &selection) const = 0 |
|
539 |
|
540 Returns the region from the viewport of the items in the given |
|
541 \a selection. |
|
542 |
|
543 In the base class this is a pure virtual function. |
|
544 |
|
545 \sa visualRect(), selectedIndexes() |
|
546 */ |
|
547 |
|
548 /*! |
|
549 \fn void QAbstractItemView::update() |
|
550 \internal |
|
551 */ |
|
552 |
|
553 /*! |
|
554 Constructs an abstract item view with the given \a parent. |
|
555 */ |
|
556 QAbstractItemView::QAbstractItemView(QWidget *parent) |
|
557 : QAbstractScrollArea(*(new QAbstractItemViewPrivate), parent) |
|
558 { |
|
559 d_func()->init(); |
|
560 } |
|
561 |
|
562 /*! |
|
563 \internal |
|
564 */ |
|
565 QAbstractItemView::QAbstractItemView(QAbstractItemViewPrivate &dd, QWidget *parent) |
|
566 : QAbstractScrollArea(dd, parent) |
|
567 { |
|
568 d_func()->init(); |
|
569 } |
|
570 |
|
571 /*! |
|
572 Destroys the view. |
|
573 */ |
|
574 QAbstractItemView::~QAbstractItemView() |
|
575 { |
|
576 } |
|
577 |
|
578 /*! |
|
579 Sets the \a model for the view to present. |
|
580 |
|
581 This function will create and set a new selection model, replacing any |
|
582 model that was previously set with setSelectionModel(). However, the old |
|
583 selection model will not be deleted as it may be shared between several |
|
584 views. We recommend that you delete the old selection model if it is no |
|
585 longer required. This is done with the following code: |
|
586 |
|
587 \snippet doc/src/snippets/code/src_gui_itemviews_qabstractitemview.cpp 2 |
|
588 |
|
589 If both the old model and the old selection model do not have parents, or |
|
590 if their parents are long-lived objects, it may be preferable to call their |
|
591 deleteLater() functions to explicitly delete them. |
|
592 |
|
593 The view \e{does not} take ownership of the model unless it is the model's |
|
594 parent object because the view may be shared between many different views. |
|
595 |
|
596 \sa selectionModel(), setSelectionModel() |
|
597 */ |
|
598 void QAbstractItemView::setModel(QAbstractItemModel *model) |
|
599 { |
|
600 Q_D(QAbstractItemView); |
|
601 if (model == d->model) |
|
602 return; |
|
603 if (d->model && d->model != QAbstractItemModelPrivate::staticEmptyModel()) { |
|
604 disconnect(d->model, SIGNAL(destroyed()), |
|
605 this, SLOT(_q_modelDestroyed())); |
|
606 disconnect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), |
|
607 this, SLOT(dataChanged(QModelIndex,QModelIndex))); |
|
608 disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)), |
|
609 this, SLOT(rowsInserted(QModelIndex,int,int))); |
|
610 disconnect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), |
|
611 this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int))); |
|
612 disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)), |
|
613 this, SLOT(_q_rowsRemoved(QModelIndex,int,int))); |
|
614 disconnect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), |
|
615 this, SLOT(_q_columnsAboutToBeRemoved(QModelIndex,int,int))); |
|
616 disconnect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)), |
|
617 this, SLOT(_q_columnsRemoved(QModelIndex,int,int))); |
|
618 disconnect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)), |
|
619 this, SLOT(_q_columnsInserted(QModelIndex,int,int))); |
|
620 |
|
621 disconnect(d->model, SIGNAL(modelReset()), this, SLOT(reset())); |
|
622 disconnect(d->model, SIGNAL(layoutChanged()), this, SLOT(_q_layoutChanged())); |
|
623 } |
|
624 d->model = (model ? model : QAbstractItemModelPrivate::staticEmptyModel()); |
|
625 |
|
626 // These asserts do basic sanity checking of the model |
|
627 Q_ASSERT_X(d->model->index(0,0) == d->model->index(0,0), |
|
628 "QAbstractItemView::setModel", |
|
629 "A model should return the exact same index " |
|
630 "(including its internal id/pointer) when asked for it twice in a row."); |
|
631 Q_ASSERT_X(d->model->index(0,0).parent() == QModelIndex(), |
|
632 "QAbstractItemView::setModel", |
|
633 "The parent of a top level index should be invalid"); |
|
634 |
|
635 if (d->model != QAbstractItemModelPrivate::staticEmptyModel()) { |
|
636 connect(d->model, SIGNAL(destroyed()), |
|
637 this, SLOT(_q_modelDestroyed())); |
|
638 connect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), |
|
639 this, SLOT(dataChanged(QModelIndex,QModelIndex))); |
|
640 connect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)), |
|
641 this, SLOT(rowsInserted(QModelIndex,int,int))); |
|
642 connect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), |
|
643 this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int))); |
|
644 connect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)), |
|
645 this, SLOT(_q_rowsRemoved(QModelIndex,int,int))); |
|
646 connect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), |
|
647 this, SLOT(_q_columnsAboutToBeRemoved(QModelIndex,int,int))); |
|
648 connect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)), |
|
649 this, SLOT(_q_columnsRemoved(QModelIndex,int,int))); |
|
650 connect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)), |
|
651 this, SLOT(_q_columnsInserted(QModelIndex,int,int))); |
|
652 |
|
653 connect(d->model, SIGNAL(modelReset()), this, SLOT(reset())); |
|
654 connect(d->model, SIGNAL(layoutChanged()), this, SLOT(_q_layoutChanged())); |
|
655 } |
|
656 setSelectionModel(new QItemSelectionModel(d->model, this)); |
|
657 reset(); // kill editors, set new root and do layout |
|
658 } |
|
659 |
|
660 /*! |
|
661 Returns the model that this view is presenting. |
|
662 */ |
|
663 QAbstractItemModel *QAbstractItemView::model() const |
|
664 { |
|
665 Q_D(const QAbstractItemView); |
|
666 return (d->model == QAbstractItemModelPrivate::staticEmptyModel() ? 0 : d->model); |
|
667 } |
|
668 |
|
669 /*! |
|
670 Sets the current selection model to the given \a selectionModel. |
|
671 |
|
672 Note that, if you call setModel() after this function, the given \a selectionModel |
|
673 will be replaced by one created by the view. |
|
674 |
|
675 \note It is up to the application to delete the old selection model if it is no |
|
676 longer needed; i.e., if it is not being used by other views. This will happen |
|
677 automatically when its parent object is deleted. However, if it does not have a |
|
678 parent, or if the parent is a long-lived object, it may be preferable to call its |
|
679 deleteLater() function to explicitly delete it. |
|
680 |
|
681 \sa selectionModel(), setModel(), clearSelection() |
|
682 */ |
|
683 void QAbstractItemView::setSelectionModel(QItemSelectionModel *selectionModel) |
|
684 { |
|
685 // ### if the given model is null, we should use the original selection model |
|
686 Q_ASSERT(selectionModel); |
|
687 Q_D(QAbstractItemView); |
|
688 |
|
689 if (selectionModel->model() != d->model) { |
|
690 qWarning("QAbstractItemView::setSelectionModel() failed: " |
|
691 "Trying to set a selection model, which works on " |
|
692 "a different model than the view."); |
|
693 return; |
|
694 } |
|
695 |
|
696 if (d->selectionModel) { |
|
697 disconnect(d->selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), |
|
698 this, SLOT(selectionChanged(QItemSelection,QItemSelection))); |
|
699 disconnect(d->selectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)), |
|
700 this, SLOT(currentChanged(QModelIndex,QModelIndex))); |
|
701 } |
|
702 |
|
703 d->selectionModel = selectionModel; |
|
704 |
|
705 if (d->selectionModel) { |
|
706 connect(d->selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), |
|
707 this, SLOT(selectionChanged(QItemSelection,QItemSelection))); |
|
708 connect(d->selectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)), |
|
709 this, SLOT(currentChanged(QModelIndex,QModelIndex))); |
|
710 } |
|
711 } |
|
712 |
|
713 /*! |
|
714 Returns the current selection model. |
|
715 |
|
716 \sa setSelectionModel(), selectedIndexes() |
|
717 */ |
|
718 QItemSelectionModel* QAbstractItemView::selectionModel() const |
|
719 { |
|
720 Q_D(const QAbstractItemView); |
|
721 return d->selectionModel; |
|
722 } |
|
723 |
|
724 /*! |
|
725 Sets the item delegate for this view and its model to \a delegate. |
|
726 This is useful if you want complete control over the editing and |
|
727 display of items. |
|
728 |
|
729 Any existing delegate will be removed, but not deleted. QAbstractItemView |
|
730 does not take ownership of \a delegate. |
|
731 |
|
732 \warning You should not share the same instance of a delegate between views. |
|
733 Doing so can cause incorrect or unintuitive editing behavior since each |
|
734 view connected to a given delegate may receive the \l{QAbstractItemDelegate::}{closeEditor()} |
|
735 signal, and attempt to access, modify or close an editor that has already been closed. |
|
736 |
|
737 \sa itemDelegate() |
|
738 */ |
|
739 void QAbstractItemView::setItemDelegate(QAbstractItemDelegate *delegate) |
|
740 { |
|
741 Q_D(QAbstractItemView); |
|
742 if (delegate == d->itemDelegate) |
|
743 return; |
|
744 |
|
745 if (d->itemDelegate) { |
|
746 if (d->delegateRefCount(d->itemDelegate) == 1) { |
|
747 disconnect(d->itemDelegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)), |
|
748 this, SLOT(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint))); |
|
749 disconnect(d->itemDelegate, SIGNAL(commitData(QWidget*)), this, SLOT(commitData(QWidget*))); |
|
750 disconnect(d->itemDelegate, SIGNAL(sizeHintChanged(QModelIndex)), this, SLOT(doItemsLayout())); |
|
751 } |
|
752 } |
|
753 |
|
754 if (delegate) { |
|
755 if (d->delegateRefCount(delegate) == 0) { |
|
756 connect(delegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)), |
|
757 this, SLOT(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint))); |
|
758 connect(delegate, SIGNAL(commitData(QWidget*)), this, SLOT(commitData(QWidget*))); |
|
759 qRegisterMetaType<QModelIndex>("QModelIndex"); |
|
760 connect(delegate, SIGNAL(sizeHintChanged(QModelIndex)), this, SLOT(doItemsLayout()), Qt::QueuedConnection); |
|
761 } |
|
762 } |
|
763 d->itemDelegate = delegate; |
|
764 viewport()->update(); |
|
765 } |
|
766 |
|
767 /*! |
|
768 Returns the item delegate used by this view and model. This is |
|
769 either one set with setItemDelegate(), or the default one. |
|
770 |
|
771 \sa setItemDelegate() |
|
772 */ |
|
773 QAbstractItemDelegate *QAbstractItemView::itemDelegate() const |
|
774 { |
|
775 return d_func()->itemDelegate; |
|
776 } |
|
777 |
|
778 /*! |
|
779 \reimp |
|
780 */ |
|
781 QVariant QAbstractItemView::inputMethodQuery(Qt::InputMethodQuery query) const |
|
782 { |
|
783 const QModelIndex current = currentIndex(); |
|
784 if (!current.isValid() || query != Qt::ImMicroFocus) |
|
785 return QAbstractScrollArea::inputMethodQuery(query); |
|
786 return visualRect(current); |
|
787 } |
|
788 |
|
789 /*! |
|
790 \since 4.2 |
|
791 |
|
792 Sets the given item \a delegate used by this view and model for the given |
|
793 \a row. All items on \a row will be drawn and managed by \a delegate |
|
794 instead of using the default delegate (i.e., itemDelegate()). |
|
795 |
|
796 Any existing row delegate for \a row will be removed, but not |
|
797 deleted. QAbstractItemView does not take ownership of \a delegate. |
|
798 |
|
799 \note If a delegate has been assigned to both a row and a column, the row |
|
800 delegate (i.e., this delegate) will take presedence and manage the |
|
801 intersecting cell index. |
|
802 |
|
803 \warning You should not share the same instance of a delegate between views. |
|
804 Doing so can cause incorrect or unintuitive editing behavior since each |
|
805 view connected to a given delegate may receive the \l{QAbstractItemDelegate::}{closeEditor()} |
|
806 signal, and attempt to access, modify or close an editor that has already been closed. |
|
807 |
|
808 \sa itemDelegateForRow(), setItemDelegateForColumn(), itemDelegate() |
|
809 */ |
|
810 void QAbstractItemView::setItemDelegateForRow(int row, QAbstractItemDelegate *delegate) |
|
811 { |
|
812 Q_D(QAbstractItemView); |
|
813 if (QAbstractItemDelegate *rowDelegate = d->rowDelegates.value(row, 0)) { |
|
814 if (d->delegateRefCount(rowDelegate) == 1) { |
|
815 disconnect(rowDelegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)), |
|
816 this, SLOT(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint))); |
|
817 disconnect(rowDelegate, SIGNAL(commitData(QWidget*)), this, SLOT(commitData(QWidget*))); |
|
818 } |
|
819 d->rowDelegates.remove(row); |
|
820 } |
|
821 if (delegate) { |
|
822 if (d->delegateRefCount(delegate) == 0) { |
|
823 connect(delegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)), |
|
824 this, SLOT(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint))); |
|
825 connect(delegate, SIGNAL(commitData(QWidget*)), this, SLOT(commitData(QWidget*))); |
|
826 } |
|
827 d->rowDelegates.insert(row, delegate); |
|
828 } |
|
829 viewport()->update(); |
|
830 } |
|
831 |
|
832 /*! |
|
833 \since 4.2 |
|
834 |
|
835 Returns the item delegate used by this view and model for the given \a row, |
|
836 or 0 if no delegate has been assigned. You can call itemDelegate() to get a |
|
837 pointer to the current delegate for a given index. |
|
838 |
|
839 \sa setItemDelegateForRow(), itemDelegateForColumn(), setItemDelegate() |
|
840 */ |
|
841 QAbstractItemDelegate *QAbstractItemView::itemDelegateForRow(int row) const |
|
842 { |
|
843 Q_D(const QAbstractItemView); |
|
844 return d->rowDelegates.value(row, 0); |
|
845 } |
|
846 |
|
847 /*! |
|
848 \since 4.2 |
|
849 |
|
850 Sets the given item \a delegate used by this view and model for the given |
|
851 \a column. All items on \a column will be drawn and managed by \a delegate |
|
852 instead of using the default delegate (i.e., itemDelegate()). |
|
853 |
|
854 Any existing column delegate for \a column will be removed, but not |
|
855 deleted. QAbstractItemView does not take ownership of \a delegate. |
|
856 |
|
857 \note If a delegate has been assigned to both a row and a column, the row |
|
858 delegate will take presedence and manage the intersecting cell index. |
|
859 |
|
860 \warning You should not share the same instance of a delegate between views. |
|
861 Doing so can cause incorrect or unintuitive editing behavior since each |
|
862 view connected to a given delegate may receive the \l{QAbstractItemDelegate::}{closeEditor()} |
|
863 signal, and attempt to access, modify or close an editor that has already been closed. |
|
864 |
|
865 \sa itemDelegateForColumn(), setItemDelegateForRow(), itemDelegate() |
|
866 */ |
|
867 void QAbstractItemView::setItemDelegateForColumn(int column, QAbstractItemDelegate *delegate) |
|
868 { |
|
869 Q_D(QAbstractItemView); |
|
870 if (QAbstractItemDelegate *columnDelegate = d->columnDelegates.value(column, 0)) { |
|
871 if (d->delegateRefCount(columnDelegate) == 1) { |
|
872 disconnect(columnDelegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)), |
|
873 this, SLOT(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint))); |
|
874 disconnect(columnDelegate, SIGNAL(commitData(QWidget*)), this, SLOT(commitData(QWidget*))); |
|
875 } |
|
876 d->columnDelegates.remove(column); |
|
877 } |
|
878 if (delegate) { |
|
879 if (d->delegateRefCount(delegate) == 0) { |
|
880 connect(delegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)), |
|
881 this, SLOT(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint))); |
|
882 connect(delegate, SIGNAL(commitData(QWidget*)), this, SLOT(commitData(QWidget*))); |
|
883 } |
|
884 d->columnDelegates.insert(column, delegate); |
|
885 } |
|
886 viewport()->update(); |
|
887 } |
|
888 |
|
889 /*! |
|
890 \since 4.2 |
|
891 |
|
892 Returns the item delegate used by this view and model for the given \a |
|
893 column. You can call itemDelegate() to get a pointer to the current delegate |
|
894 for a given index. |
|
895 |
|
896 \sa setItemDelegateForColumn(), itemDelegateForRow(), itemDelegate() |
|
897 */ |
|
898 QAbstractItemDelegate *QAbstractItemView::itemDelegateForColumn(int column) const |
|
899 { |
|
900 Q_D(const QAbstractItemView); |
|
901 return d->columnDelegates.value(column, 0); |
|
902 } |
|
903 |
|
904 /*! |
|
905 Returns the item delegate used by this view and model for |
|
906 the given \a index. |
|
907 */ |
|
908 QAbstractItemDelegate *QAbstractItemView::itemDelegate(const QModelIndex &index) const |
|
909 { |
|
910 Q_D(const QAbstractItemView); |
|
911 return d->delegateForIndex(index); |
|
912 } |
|
913 |
|
914 /*! |
|
915 \property QAbstractItemView::selectionMode |
|
916 \brief which selection mode the view operates in |
|
917 |
|
918 This property controls whether the user can select one or many items |
|
919 and, in many-item selections, whether the selection must be a |
|
920 continuous range of items. |
|
921 |
|
922 \sa SelectionMode SelectionBehavior |
|
923 */ |
|
924 void QAbstractItemView::setSelectionMode(SelectionMode mode) |
|
925 { |
|
926 Q_D(QAbstractItemView); |
|
927 d->selectionMode = mode; |
|
928 } |
|
929 |
|
930 QAbstractItemView::SelectionMode QAbstractItemView::selectionMode() const |
|
931 { |
|
932 Q_D(const QAbstractItemView); |
|
933 return d->selectionMode; |
|
934 } |
|
935 |
|
936 /*! |
|
937 \property QAbstractItemView::selectionBehavior |
|
938 \brief which selection behavior the view uses |
|
939 |
|
940 This property holds whether selections are done |
|
941 in terms of single items, rows or columns. |
|
942 |
|
943 \sa SelectionMode SelectionBehavior |
|
944 */ |
|
945 |
|
946 void QAbstractItemView::setSelectionBehavior(QAbstractItemView::SelectionBehavior behavior) |
|
947 { |
|
948 Q_D(QAbstractItemView); |
|
949 d->selectionBehavior = behavior; |
|
950 } |
|
951 |
|
952 QAbstractItemView::SelectionBehavior QAbstractItemView::selectionBehavior() const |
|
953 { |
|
954 Q_D(const QAbstractItemView); |
|
955 return d->selectionBehavior; |
|
956 } |
|
957 |
|
958 /*! |
|
959 Sets the current item to be the item at \a index. |
|
960 |
|
961 Unless the current selection mode is |
|
962 \l{QAbstractItemView::}{NoSelection}, the item is also be selected. |
|
963 Note that this function also updates the starting position for any |
|
964 new selections the user performs. |
|
965 |
|
966 To set an item as the current item without selecting it, call |
|
967 |
|
968 \c{selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);} |
|
969 |
|
970 \sa currentIndex(), currentChanged(), selectionMode |
|
971 */ |
|
972 void QAbstractItemView::setCurrentIndex(const QModelIndex &index) |
|
973 { |
|
974 Q_D(QAbstractItemView); |
|
975 if (d->selectionModel && (!index.isValid() || d->isIndexEnabled(index))) { |
|
976 QItemSelectionModel::SelectionFlags command = selectionCommand(index, 0); |
|
977 d->selectionModel->setCurrentIndex(index, command); |
|
978 d->currentIndexSet = true; |
|
979 if ((command & QItemSelectionModel::Current) == 0) |
|
980 d->pressedPosition = visualRect(currentIndex()).center() + d->offset(); |
|
981 } |
|
982 } |
|
983 |
|
984 /*! |
|
985 Returns the model index of the current item. |
|
986 |
|
987 \sa setCurrentIndex() |
|
988 */ |
|
989 QModelIndex QAbstractItemView::currentIndex() const |
|
990 { |
|
991 Q_D(const QAbstractItemView); |
|
992 return d->selectionModel ? d->selectionModel->currentIndex() : QModelIndex(); |
|
993 } |
|
994 |
|
995 |
|
996 /*! |
|
997 Reset the internal state of the view. |
|
998 |
|
999 \warning This function will reset open editors, scroll bar positions, |
|
1000 selections, etc. Existing changes will not be committed. If you would like |
|
1001 to save your changes when resetting the view, you can reimplement this |
|
1002 function, commit your changes, and then call the superclass' |
|
1003 implementation. |
|
1004 */ |
|
1005 void QAbstractItemView::reset() |
|
1006 { |
|
1007 Q_D(QAbstractItemView); |
|
1008 d->delayedReset.stop(); //make sure we stop the timer |
|
1009 QList<QEditorInfo>::const_iterator it = d->editors.constBegin(); |
|
1010 for (; it != d->editors.constEnd(); ++it) |
|
1011 d->releaseEditor(it->editor); |
|
1012 d->editors.clear(); |
|
1013 d->persistent.clear(); |
|
1014 d->currentIndexSet = false; |
|
1015 setState(NoState); |
|
1016 setRootIndex(QModelIndex()); |
|
1017 if (d->selectionModel) |
|
1018 d->selectionModel->reset(); |
|
1019 } |
|
1020 |
|
1021 /*! |
|
1022 Sets the root item to the item at the given \a index. |
|
1023 |
|
1024 \sa rootIndex() |
|
1025 */ |
|
1026 void QAbstractItemView::setRootIndex(const QModelIndex &index) |
|
1027 { |
|
1028 Q_D(QAbstractItemView); |
|
1029 if (index.isValid() && index.model() != d->model) { |
|
1030 qWarning("QAbstractItemView::setRootIndex failed : index must be from the currently set model"); |
|
1031 return; |
|
1032 } |
|
1033 d->root = index; |
|
1034 d->doDelayedItemsLayout(); |
|
1035 } |
|
1036 |
|
1037 /*! |
|
1038 Returns the model index of the model's root item. The root item is |
|
1039 the parent item to the view's toplevel items. The root can be invalid. |
|
1040 |
|
1041 \sa setRootIndex() |
|
1042 */ |
|
1043 QModelIndex QAbstractItemView::rootIndex() const |
|
1044 { |
|
1045 return QModelIndex(d_func()->root); |
|
1046 } |
|
1047 |
|
1048 /*! |
|
1049 Selects all items in the view. |
|
1050 This function will use the selection behavior |
|
1051 set on the view when selecting. |
|
1052 |
|
1053 \sa setSelection(), selectedIndexes(), clearSelection() |
|
1054 */ |
|
1055 void QAbstractItemView::selectAll() |
|
1056 { |
|
1057 Q_D(QAbstractItemView); |
|
1058 SelectionMode mode = d->selectionMode; |
|
1059 if (mode == MultiSelection || mode == ExtendedSelection) |
|
1060 d->selectAll(QItemSelectionModel::ClearAndSelect |
|
1061 |d->selectionBehaviorFlags()); |
|
1062 else if (mode != SingleSelection) |
|
1063 d->selectAll(selectionCommand(d->model->index(0, 0, d->root))); |
|
1064 } |
|
1065 |
|
1066 /*! |
|
1067 Starts editing the item corresponding to the given \a index if it is |
|
1068 editable. |
|
1069 |
|
1070 Note that this function does not change the current index. Since the current |
|
1071 index defines the next and previous items to edit, users may find that |
|
1072 keyboard navigation does not work as expected. To provide consistent navigation |
|
1073 behavior, call setCurrentIndex() before this function with the same model |
|
1074 index. |
|
1075 |
|
1076 \sa QModelIndex::flags() |
|
1077 */ |
|
1078 void QAbstractItemView::edit(const QModelIndex &index) |
|
1079 { |
|
1080 Q_D(QAbstractItemView); |
|
1081 if (!d->isIndexValid(index)) |
|
1082 qWarning("edit: index was invalid"); |
|
1083 if (!edit(index, AllEditTriggers, 0)) |
|
1084 qWarning("edit: editing failed"); |
|
1085 } |
|
1086 |
|
1087 /*! |
|
1088 Deselects all selected items. The current index will not be changed. |
|
1089 |
|
1090 \sa setSelection(), selectAll() |
|
1091 */ |
|
1092 void QAbstractItemView::clearSelection() |
|
1093 { |
|
1094 Q_D(QAbstractItemView); |
|
1095 if (d->selectionModel) |
|
1096 d->selectionModel->clearSelection(); |
|
1097 } |
|
1098 |
|
1099 /*! |
|
1100 \internal |
|
1101 |
|
1102 This function is intended to lay out the items in the view. |
|
1103 The default implementation just calls updateGeometries() and updates the viewport. |
|
1104 */ |
|
1105 void QAbstractItemView::doItemsLayout() |
|
1106 { |
|
1107 Q_D(QAbstractItemView); |
|
1108 d->interruptDelayedItemsLayout(); |
|
1109 updateGeometries(); |
|
1110 d->viewport->update(); |
|
1111 } |
|
1112 |
|
1113 /*! |
|
1114 \property QAbstractItemView::editTriggers |
|
1115 \brief which actions will initiate item editing |
|
1116 |
|
1117 This property is a selection of flags defined by |
|
1118 \l{EditTrigger}, combined using the OR |
|
1119 operator. The view will only initiate the editing of an item if the |
|
1120 action performed is set in this property. |
|
1121 */ |
|
1122 void QAbstractItemView::setEditTriggers(EditTriggers actions) |
|
1123 { |
|
1124 Q_D(QAbstractItemView); |
|
1125 d->editTriggers = actions; |
|
1126 } |
|
1127 |
|
1128 QAbstractItemView::EditTriggers QAbstractItemView::editTriggers() const |
|
1129 { |
|
1130 Q_D(const QAbstractItemView); |
|
1131 return d->editTriggers; |
|
1132 } |
|
1133 |
|
1134 /*! |
|
1135 \since 4.2 |
|
1136 \property QAbstractItemView::verticalScrollMode |
|
1137 \brief how the view scrolls its contents in the vertical direction |
|
1138 |
|
1139 This property controlls how the view scroll its contents vertically. |
|
1140 Scrolling can be done either per pixel or per item. |
|
1141 */ |
|
1142 |
|
1143 void QAbstractItemView::setVerticalScrollMode(ScrollMode mode) |
|
1144 { |
|
1145 Q_D(QAbstractItemView); |
|
1146 if (mode == d->verticalScrollMode) |
|
1147 return; |
|
1148 QModelIndex topLeft = indexAt(QPoint(0, 0)); |
|
1149 d->verticalScrollMode = mode; |
|
1150 updateGeometries(); // update the scroll bars |
|
1151 scrollTo(topLeft, QAbstractItemView::PositionAtTop); |
|
1152 } |
|
1153 |
|
1154 QAbstractItemView::ScrollMode QAbstractItemView::verticalScrollMode() const |
|
1155 { |
|
1156 Q_D(const QAbstractItemView); |
|
1157 return d->verticalScrollMode; |
|
1158 } |
|
1159 |
|
1160 /*! |
|
1161 \since 4.2 |
|
1162 \property QAbstractItemView::horizontalScrollMode |
|
1163 \brief how the view scrolls its contents in the horizontal direction |
|
1164 |
|
1165 This property controlls how the view scroll its contents horizontally. |
|
1166 Scrolling can be done either per pixel or per item. |
|
1167 */ |
|
1168 |
|
1169 void QAbstractItemView::setHorizontalScrollMode(ScrollMode mode) |
|
1170 { |
|
1171 Q_D(QAbstractItemView); |
|
1172 d->horizontalScrollMode = mode; |
|
1173 updateGeometries(); // update the scroll bars |
|
1174 } |
|
1175 |
|
1176 QAbstractItemView::ScrollMode QAbstractItemView::horizontalScrollMode() const |
|
1177 { |
|
1178 Q_D(const QAbstractItemView); |
|
1179 return d->horizontalScrollMode; |
|
1180 } |
|
1181 |
|
1182 #ifndef QT_NO_DRAGANDDROP |
|
1183 /*! |
|
1184 \since 4.2 |
|
1185 \property QAbstractItemView::dragDropOverwriteMode |
|
1186 \brief the view's drag and drop behavior |
|
1187 |
|
1188 If its value is \c true, the selected data will overwrite the |
|
1189 existing item data when dropped, while moving the data will clear |
|
1190 the item. If its value is \c false, the selected data will be |
|
1191 inserted as a new item when the data is dropped. When the data is |
|
1192 moved, the item is removed as well. |
|
1193 |
|
1194 The default value is \c false, as in the QListView and QTreeView |
|
1195 subclasses. In the QTableView subclass, on the other hand, the |
|
1196 property has been set to \c true. |
|
1197 |
|
1198 Note: This is not intended to prevent overwriting of items. |
|
1199 The model's implementation of flags() should do that by not |
|
1200 returning Qt::ItemIsDropEnabled. |
|
1201 |
|
1202 \sa dragDropMode |
|
1203 */ |
|
1204 void QAbstractItemView::setDragDropOverwriteMode(bool overwrite) |
|
1205 { |
|
1206 Q_D(QAbstractItemView); |
|
1207 d->overwrite = overwrite; |
|
1208 } |
|
1209 |
|
1210 bool QAbstractItemView::dragDropOverwriteMode() const |
|
1211 { |
|
1212 Q_D(const QAbstractItemView); |
|
1213 return d->overwrite; |
|
1214 } |
|
1215 #endif |
|
1216 |
|
1217 /*! |
|
1218 \property QAbstractItemView::autoScroll |
|
1219 \brief whether autoscrolling in drag move events is enabled |
|
1220 |
|
1221 If this property is set to true (the default), the |
|
1222 QAbstractItemView automatically scrolls the contents of the view |
|
1223 if the user drags within 16 pixels of the viewport edge. If the current |
|
1224 item changes, then the view will scroll automatically to ensure that the |
|
1225 current item is fully visible. |
|
1226 |
|
1227 This property only works if the viewport accepts drops. Autoscroll is |
|
1228 switched off by setting this property to false. |
|
1229 */ |
|
1230 |
|
1231 void QAbstractItemView::setAutoScroll(bool enable) |
|
1232 { |
|
1233 Q_D(QAbstractItemView); |
|
1234 d->autoScroll = enable; |
|
1235 } |
|
1236 |
|
1237 bool QAbstractItemView::hasAutoScroll() const |
|
1238 { |
|
1239 Q_D(const QAbstractItemView); |
|
1240 return d->autoScroll; |
|
1241 } |
|
1242 |
|
1243 /*! |
|
1244 \since 4.4 |
|
1245 \property QAbstractItemView::autoScrollMargin |
|
1246 \brief the size of the area when auto scrolling is triggered |
|
1247 |
|
1248 This property controlls the size of the area at the edge of the viewport that |
|
1249 triggers autoscrolling. The default value is 16 pixels. |
|
1250 */ |
|
1251 void QAbstractItemView::setAutoScrollMargin(int margin) |
|
1252 { |
|
1253 Q_D(QAbstractItemView); |
|
1254 d->autoScrollMargin = margin; |
|
1255 } |
|
1256 |
|
1257 int QAbstractItemView::autoScrollMargin() const |
|
1258 { |
|
1259 Q_D(const QAbstractItemView); |
|
1260 return d->autoScrollMargin; |
|
1261 } |
|
1262 |
|
1263 /*! |
|
1264 \property QAbstractItemView::tabKeyNavigation |
|
1265 \brief whether item navigation with tab and backtab is enabled. |
|
1266 */ |
|
1267 |
|
1268 void QAbstractItemView::setTabKeyNavigation(bool enable) |
|
1269 { |
|
1270 Q_D(QAbstractItemView); |
|
1271 d->tabKeyNavigation = enable; |
|
1272 } |
|
1273 |
|
1274 bool QAbstractItemView::tabKeyNavigation() const |
|
1275 { |
|
1276 Q_D(const QAbstractItemView); |
|
1277 return d->tabKeyNavigation; |
|
1278 } |
|
1279 |
|
1280 #ifndef QT_NO_DRAGANDDROP |
|
1281 /*! |
|
1282 \property QAbstractItemView::showDropIndicator |
|
1283 \brief whether the drop indicator is shown when dragging items and dropping. |
|
1284 |
|
1285 \sa dragEnabled DragDropMode dragDropOverwriteMode acceptDrops |
|
1286 */ |
|
1287 |
|
1288 void QAbstractItemView::setDropIndicatorShown(bool enable) |
|
1289 { |
|
1290 Q_D(QAbstractItemView); |
|
1291 d->showDropIndicator = enable; |
|
1292 } |
|
1293 |
|
1294 bool QAbstractItemView::showDropIndicator() const |
|
1295 { |
|
1296 Q_D(const QAbstractItemView); |
|
1297 return d->showDropIndicator; |
|
1298 } |
|
1299 |
|
1300 /*! |
|
1301 \property QAbstractItemView::dragEnabled |
|
1302 \brief whether the view supports dragging of its own items |
|
1303 |
|
1304 \sa showDropIndicator DragDropMode dragDropOverwriteMode acceptDrops |
|
1305 */ |
|
1306 |
|
1307 void QAbstractItemView::setDragEnabled(bool enable) |
|
1308 { |
|
1309 Q_D(QAbstractItemView); |
|
1310 d->dragEnabled = enable; |
|
1311 } |
|
1312 |
|
1313 bool QAbstractItemView::dragEnabled() const |
|
1314 { |
|
1315 Q_D(const QAbstractItemView); |
|
1316 return d->dragEnabled; |
|
1317 } |
|
1318 |
|
1319 /*! |
|
1320 \since 4.2 |
|
1321 \enum QAbstractItemView::DragDropMode |
|
1322 |
|
1323 Describes the various drag and drop events the view can act upon. |
|
1324 By default the view does not support dragging or dropping (\c |
|
1325 NoDragDrop). |
|
1326 |
|
1327 \value NoDragDrop Does not support dragging or dropping. |
|
1328 \value DragOnly The view supports dragging of its own items |
|
1329 \value DropOnly The view accepts drops |
|
1330 \value DragDrop The view supports both dragging and dropping |
|
1331 \value InternalMove The view accepts move (\bold{not copy}) operations only |
|
1332 from itself. |
|
1333 |
|
1334 Note that the model used needs to provide support for drag and drop operations. |
|
1335 |
|
1336 \sa setDragDropMode() {Using Drag and Drop with Item Views} |
|
1337 */ |
|
1338 |
|
1339 /*! |
|
1340 \property QAbstractItemView::dragDropMode |
|
1341 \brief the drag and drop event the view will act upon |
|
1342 |
|
1343 \since 4.2 |
|
1344 \sa showDropIndicator dragDropOverwriteMode |
|
1345 */ |
|
1346 void QAbstractItemView::setDragDropMode(DragDropMode behavior) |
|
1347 { |
|
1348 Q_D(QAbstractItemView); |
|
1349 d->dragDropMode = behavior; |
|
1350 setDragEnabled(behavior == DragOnly || behavior == DragDrop || behavior == InternalMove); |
|
1351 setAcceptDrops(behavior == DropOnly || behavior == DragDrop || behavior == InternalMove); |
|
1352 } |
|
1353 |
|
1354 QAbstractItemView::DragDropMode QAbstractItemView::dragDropMode() const |
|
1355 { |
|
1356 Q_D(const QAbstractItemView); |
|
1357 DragDropMode setBehavior = d->dragDropMode; |
|
1358 if (!dragEnabled() && !acceptDrops()) |
|
1359 return NoDragDrop; |
|
1360 |
|
1361 if (dragEnabled() && !acceptDrops()) |
|
1362 return DragOnly; |
|
1363 |
|
1364 if (!dragEnabled() && acceptDrops()) |
|
1365 return DropOnly; |
|
1366 |
|
1367 if (dragEnabled() && acceptDrops()) { |
|
1368 if (setBehavior == InternalMove) |
|
1369 return setBehavior; |
|
1370 else |
|
1371 return DragDrop; |
|
1372 } |
|
1373 |
|
1374 return NoDragDrop; |
|
1375 } |
|
1376 |
|
1377 /*! |
|
1378 \property QAbstractItemView::defaultDropAction |
|
1379 \brief the drop action that will be used by default in QAbstractItemView::drag() |
|
1380 |
|
1381 If the property is not set, the drop action is CopyAction when the supported |
|
1382 actions support CopyAction. |
|
1383 |
|
1384 \since 4.6 |
|
1385 \sa showDropIndicator dragDropOverwriteMode |
|
1386 */ |
|
1387 void QAbstractItemView::setDefaultDropAction(Qt::DropAction dropAction) |
|
1388 { |
|
1389 Q_D(QAbstractItemView); |
|
1390 d->defaultDropAction = dropAction; |
|
1391 } |
|
1392 |
|
1393 Qt::DropAction QAbstractItemView::defaultDropAction() const |
|
1394 { |
|
1395 Q_D(const QAbstractItemView); |
|
1396 return d->defaultDropAction; |
|
1397 } |
|
1398 |
|
1399 #endif // QT_NO_DRAGANDDROP |
|
1400 |
|
1401 /*! |
|
1402 \property QAbstractItemView::alternatingRowColors |
|
1403 \brief whether to draw the background using alternating colors |
|
1404 |
|
1405 If this property is true, the item background will be drawn using |
|
1406 QPalette::Base and QPalette::AlternateBase; otherwise the background |
|
1407 will be drawn using the QPalette::Base color. |
|
1408 |
|
1409 By default, this property is false. |
|
1410 */ |
|
1411 void QAbstractItemView::setAlternatingRowColors(bool enable) |
|
1412 { |
|
1413 Q_D(QAbstractItemView); |
|
1414 d->alternatingColors = enable; |
|
1415 if (isVisible()) |
|
1416 d->viewport->update(); |
|
1417 } |
|
1418 |
|
1419 bool QAbstractItemView::alternatingRowColors() const |
|
1420 { |
|
1421 Q_D(const QAbstractItemView); |
|
1422 return d->alternatingColors; |
|
1423 } |
|
1424 |
|
1425 /*! |
|
1426 \property QAbstractItemView::iconSize |
|
1427 \brief the size of items' icons |
|
1428 |
|
1429 Setting this property when the view is visible will cause the |
|
1430 items to be laid out again. |
|
1431 */ |
|
1432 void QAbstractItemView::setIconSize(const QSize &size) |
|
1433 { |
|
1434 Q_D(QAbstractItemView); |
|
1435 if (size == d->iconSize) |
|
1436 return; |
|
1437 d->iconSize = size; |
|
1438 d->doDelayedItemsLayout(); |
|
1439 } |
|
1440 |
|
1441 QSize QAbstractItemView::iconSize() const |
|
1442 { |
|
1443 Q_D(const QAbstractItemView); |
|
1444 return d->iconSize; |
|
1445 } |
|
1446 |
|
1447 /*! |
|
1448 \property QAbstractItemView::textElideMode |
|
1449 |
|
1450 \brief the position of the "..." in elided text. |
|
1451 |
|
1452 The default value for all item views is Qt::ElideRight. |
|
1453 */ |
|
1454 void QAbstractItemView::setTextElideMode(Qt::TextElideMode mode) |
|
1455 { |
|
1456 Q_D(QAbstractItemView); |
|
1457 d->textElideMode = mode; |
|
1458 } |
|
1459 |
|
1460 Qt::TextElideMode QAbstractItemView::textElideMode() const |
|
1461 { |
|
1462 return d_func()->textElideMode; |
|
1463 } |
|
1464 |
|
1465 /*! |
|
1466 \reimp |
|
1467 */ |
|
1468 bool QAbstractItemView::focusNextPrevChild(bool next) |
|
1469 { |
|
1470 Q_D(QAbstractItemView); |
|
1471 if (d->tabKeyNavigation && isEnabled() && d->viewport->isEnabled()) { |
|
1472 QKeyEvent event(QEvent::KeyPress, next ? Qt::Key_Tab : Qt::Key_Backtab, Qt::NoModifier); |
|
1473 keyPressEvent(&event); |
|
1474 if (event.isAccepted()) |
|
1475 return true; |
|
1476 } |
|
1477 return QAbstractScrollArea::focusNextPrevChild(next); |
|
1478 } |
|
1479 |
|
1480 /*! |
|
1481 \reimp |
|
1482 */ |
|
1483 bool QAbstractItemView::event(QEvent *event) |
|
1484 { |
|
1485 Q_D(QAbstractItemView); |
|
1486 switch (event->type()) { |
|
1487 case QEvent::Paint: |
|
1488 //we call this here because the scrollbars' visibility might be altered |
|
1489 //so this can't be done in the paintEvent method |
|
1490 d->executePostedLayout(); //make sure we set the layout properly |
|
1491 break; |
|
1492 case QEvent::Show: |
|
1493 d->executePostedLayout(); //make sure we set the layout properly |
|
1494 if (d->shouldScrollToCurrentOnShow) { |
|
1495 d->shouldScrollToCurrentOnShow = false; |
|
1496 const QModelIndex current = currentIndex(); |
|
1497 if (current.isValid() && (d->state == QAbstractItemView::EditingState || d->autoScroll)) |
|
1498 scrollTo(current); |
|
1499 } |
|
1500 break; |
|
1501 case QEvent::LocaleChange: |
|
1502 viewport()->update(); |
|
1503 break; |
|
1504 case QEvent::LayoutDirectionChange: |
|
1505 case QEvent::ApplicationLayoutDirectionChange: |
|
1506 updateGeometries(); |
|
1507 break; |
|
1508 case QEvent::StyleChange: |
|
1509 doItemsLayout(); |
|
1510 break; |
|
1511 case QEvent::FocusOut: |
|
1512 d->checkPersistentEditorFocus(); |
|
1513 break; |
|
1514 case QEvent::FontChange: |
|
1515 d->doDelayedItemsLayout(); // the size of the items will change |
|
1516 break; |
|
1517 default: |
|
1518 break; |
|
1519 } |
|
1520 return QAbstractScrollArea::event(event); |
|
1521 } |
|
1522 |
|
1523 /*! |
|
1524 \fn bool QAbstractItemView::viewportEvent(QEvent *event) |
|
1525 |
|
1526 This function is used to handle tool tips, and What's |
|
1527 This? mode, if the given \a event is a QEvent::ToolTip,or a |
|
1528 QEvent::WhatsThis. It passes all other |
|
1529 events on to its base class viewportEvent() handler. |
|
1530 */ |
|
1531 bool QAbstractItemView::viewportEvent(QEvent *event) |
|
1532 { |
|
1533 Q_D(QAbstractItemView); |
|
1534 switch (event->type()) { |
|
1535 case QEvent::HoverEnter: { |
|
1536 QHoverEvent *he = static_cast<QHoverEvent*>(event); |
|
1537 d->hover = indexAt(he->pos()); |
|
1538 update(d->hover); |
|
1539 break; } |
|
1540 case QEvent::HoverLeave: { |
|
1541 update(d->hover); // update old |
|
1542 d->hover = QModelIndex(); |
|
1543 break; } |
|
1544 case QEvent::HoverMove: { |
|
1545 QHoverEvent *he = static_cast<QHoverEvent*>(event); |
|
1546 QModelIndex old = d->hover; |
|
1547 d->hover = indexAt(he->pos()); |
|
1548 if (d->hover != old) |
|
1549 d->viewport->update(visualRect(old)|visualRect(d->hover)); |
|
1550 break; } |
|
1551 case QEvent::Enter: |
|
1552 d->viewportEnteredNeeded = true; |
|
1553 break; |
|
1554 case QEvent::Leave: |
|
1555 d->enteredIndex = QModelIndex(); |
|
1556 break; |
|
1557 case QEvent::ToolTip: |
|
1558 case QEvent::QueryWhatsThis: |
|
1559 case QEvent::WhatsThis: { |
|
1560 QHelpEvent *he = static_cast<QHelpEvent*>(event); |
|
1561 const QModelIndex index = indexAt(he->pos()); |
|
1562 QStyleOptionViewItemV4 option = d->viewOptionsV4(); |
|
1563 option.rect = visualRect(index); |
|
1564 option.state |= (index == currentIndex() ? QStyle::State_HasFocus : QStyle::State_None); |
|
1565 bool retval = false; |
|
1566 // ### Qt 5: make this a normal function call to a virtual function |
|
1567 QMetaObject::invokeMethod(d->delegateForIndex(index), "helpEvent", |
|
1568 Q_RETURN_ARG(bool, retval), |
|
1569 Q_ARG(QHelpEvent *, he), |
|
1570 Q_ARG(QAbstractItemView *, this), |
|
1571 Q_ARG(QStyleOptionViewItem, option), |
|
1572 Q_ARG(QModelIndex, index)); |
|
1573 return retval; |
|
1574 } |
|
1575 case QEvent::FontChange: |
|
1576 d->doDelayedItemsLayout(); // the size of the items will change |
|
1577 break; |
|
1578 case QEvent::WindowActivate: |
|
1579 case QEvent::WindowDeactivate: |
|
1580 d->viewport->update(); |
|
1581 break; |
|
1582 default: |
|
1583 break; |
|
1584 } |
|
1585 return QAbstractScrollArea::viewportEvent(event); |
|
1586 } |
|
1587 |
|
1588 /*! |
|
1589 This function is called with the given \a event when a mouse button is pressed |
|
1590 while the cursor is inside the widget. If a valid item is pressed on it is made |
|
1591 into the current item. This function emits the pressed() signal. |
|
1592 */ |
|
1593 void QAbstractItemView::mousePressEvent(QMouseEvent *event) |
|
1594 { |
|
1595 Q_D(QAbstractItemView); |
|
1596 d->delayedAutoScroll.stop(); //any interaction with the view cancel the auto scrolling |
|
1597 QPoint pos = event->pos(); |
|
1598 QPersistentModelIndex index = indexAt(pos); |
|
1599 |
|
1600 if (!d->selectionModel |
|
1601 || (d->state == EditingState && d->hasEditor(index))) |
|
1602 return; |
|
1603 |
|
1604 d->pressedAlreadySelected = d->selectionModel->isSelected(index); |
|
1605 d->pressedIndex = index; |
|
1606 d->pressedModifiers = event->modifiers(); |
|
1607 QItemSelectionModel::SelectionFlags command = selectionCommand(index, event); |
|
1608 QPoint offset = d->offset(); |
|
1609 if ((command & QItemSelectionModel::Current) == 0) |
|
1610 d->pressedPosition = pos + offset; |
|
1611 else if (!indexAt(d->pressedPosition).isValid()) |
|
1612 d->pressedPosition = visualRect(currentIndex()).center() + offset; |
|
1613 |
|
1614 if (edit(index, NoEditTriggers, event)) |
|
1615 return; |
|
1616 |
|
1617 if (index.isValid() && d->isIndexEnabled(index)) { |
|
1618 // we disable scrollTo for mouse press so the item doesn't change position |
|
1619 // when the user is interacting with it (ie. clicking on it) |
|
1620 bool autoScroll = d->autoScroll; |
|
1621 d->autoScroll = false; |
|
1622 d->selectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate); |
|
1623 d->autoScroll = autoScroll; |
|
1624 QRect rect(d->pressedPosition - offset, pos); |
|
1625 if (command.testFlag(QItemSelectionModel::Toggle)) { |
|
1626 command &= ~QItemSelectionModel::Toggle; |
|
1627 d->ctrlDragSelectionFlag = d->selectionModel->isSelected(index) ? QItemSelectionModel::Deselect : QItemSelectionModel::Select; |
|
1628 command |= d->ctrlDragSelectionFlag; |
|
1629 } |
|
1630 setSelection(rect, command); |
|
1631 |
|
1632 // signal handlers may change the model |
|
1633 emit pressed(index); |
|
1634 if (d->autoScroll) { |
|
1635 //we delay the autoscrolling to filter out double click event |
|
1636 //100 is to be sure that there won't be a double-click misinterpreted as a 2 single clicks |
|
1637 d->delayedAutoScroll.start(QApplication::doubleClickInterval()+100, this); |
|
1638 } |
|
1639 |
|
1640 } else { |
|
1641 // Forces a finalize() even if mouse is pressed, but not on a item |
|
1642 d->selectionModel->select(QModelIndex(), QItemSelectionModel::Select); |
|
1643 } |
|
1644 } |
|
1645 |
|
1646 /*! |
|
1647 This function is called with the given \a event when a mouse move event is |
|
1648 sent to the widget. If a selection is in progress and new items are moved |
|
1649 over the selection is extended; if a drag is in progress it is continued. |
|
1650 */ |
|
1651 void QAbstractItemView::mouseMoveEvent(QMouseEvent *event) |
|
1652 { |
|
1653 Q_D(QAbstractItemView); |
|
1654 QPoint topLeft; |
|
1655 QPoint bottomRight = event->pos(); |
|
1656 |
|
1657 if (state() == ExpandingState || state() == CollapsingState) |
|
1658 return; |
|
1659 |
|
1660 #ifndef QT_NO_DRAGANDDROP |
|
1661 if (state() == DraggingState) { |
|
1662 topLeft = d->pressedPosition - d->offset(); |
|
1663 if ((topLeft - bottomRight).manhattanLength() > QApplication::startDragDistance()) { |
|
1664 d->pressedIndex = QModelIndex(); |
|
1665 startDrag(d->model->supportedDragActions()); |
|
1666 setState(NoState); // the startDrag will return when the dnd operation is done |
|
1667 stopAutoScroll(); |
|
1668 } |
|
1669 return; |
|
1670 } |
|
1671 #endif // QT_NO_DRAGANDDROP |
|
1672 |
|
1673 QPersistentModelIndex index = indexAt(bottomRight); |
|
1674 QModelIndex buddy = d->model->buddy(d->pressedIndex); |
|
1675 if ((state() == EditingState && d->hasEditor(buddy)) |
|
1676 || edit(index, NoEditTriggers, event)) |
|
1677 return; |
|
1678 |
|
1679 if (d->selectionMode != SingleSelection) |
|
1680 topLeft = d->pressedPosition - d->offset(); |
|
1681 else |
|
1682 topLeft = bottomRight; |
|
1683 |
|
1684 d->checkMouseMove(index); |
|
1685 |
|
1686 #ifndef QT_NO_DRAGANDDROP |
|
1687 if (d->pressedIndex.isValid() |
|
1688 && d->dragEnabled |
|
1689 && (state() != DragSelectingState) |
|
1690 && (event->buttons() != Qt::NoButton) |
|
1691 && !d->selectedDraggableIndexes().isEmpty()) { |
|
1692 setState(DraggingState); |
|
1693 return; |
|
1694 } |
|
1695 #endif |
|
1696 |
|
1697 if ((event->buttons() & Qt::LeftButton) && d->selectionAllowed(index) && d->selectionModel) { |
|
1698 setState(DragSelectingState); |
|
1699 QItemSelectionModel::SelectionFlags command = selectionCommand(index, event); |
|
1700 if (d->ctrlDragSelectionFlag != QItemSelectionModel::NoUpdate && command.testFlag(QItemSelectionModel::Toggle)) { |
|
1701 command &= ~QItemSelectionModel::Toggle; |
|
1702 command |= d->ctrlDragSelectionFlag; |
|
1703 } |
|
1704 |
|
1705 // Do the normalize ourselves, since QRect::normalized() is flawed |
|
1706 QRect selectionRect = QRect(topLeft, bottomRight); |
|
1707 setSelection(selectionRect, command); |
|
1708 |
|
1709 // set at the end because it might scroll the view |
|
1710 if (index.isValid() |
|
1711 && (index != d->selectionModel->currentIndex()) |
|
1712 && d->isIndexEnabled(index)) |
|
1713 d->selectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate); |
|
1714 } |
|
1715 } |
|
1716 |
|
1717 /*! |
|
1718 This function is called with the given \a event when a mouse button is released, |
|
1719 after a mouse press event on the widget. If a user presses the mouse inside your |
|
1720 widget and then drags the mouse to another location before releasing the mouse button, |
|
1721 your widget receives the release event. The function will emit the clicked() signal if an |
|
1722 item was being pressed. |
|
1723 */ |
|
1724 void QAbstractItemView::mouseReleaseEvent(QMouseEvent *event) |
|
1725 { |
|
1726 Q_D(QAbstractItemView); |
|
1727 |
|
1728 QPoint pos = event->pos(); |
|
1729 QPersistentModelIndex index = indexAt(pos); |
|
1730 |
|
1731 if (state() == EditingState) { |
|
1732 if (d->isIndexValid(index) |
|
1733 && d->isIndexEnabled(index) |
|
1734 && d->sendDelegateEvent(index, event)) |
|
1735 update(index); |
|
1736 return; |
|
1737 } |
|
1738 |
|
1739 bool click = (index == d->pressedIndex && index.isValid()); |
|
1740 bool selectedClicked = click && (event->button() & Qt::LeftButton) && d->pressedAlreadySelected; |
|
1741 EditTrigger trigger = (selectedClicked ? SelectedClicked : NoEditTriggers); |
|
1742 bool edited = edit(index, trigger, event); |
|
1743 |
|
1744 d->ctrlDragSelectionFlag = QItemSelectionModel::NoUpdate; |
|
1745 |
|
1746 //in the case the user presses on no item we might decide to clear the selection |
|
1747 if (d->selectionModel && !index.isValid()) |
|
1748 d->selectionModel->select(QModelIndex(), selectionCommand(index, event)); |
|
1749 |
|
1750 setState(NoState); |
|
1751 |
|
1752 if (click) { |
|
1753 emit clicked(index); |
|
1754 if (edited) |
|
1755 return; |
|
1756 if (style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, 0, this)) |
|
1757 emit activated(index); |
|
1758 } |
|
1759 } |
|
1760 |
|
1761 /*! |
|
1762 This function is called with the given \a event when a mouse button is |
|
1763 double clicked inside the widget. If the double-click is on a valid item it |
|
1764 emits the doubleClicked() signal and calls edit() on the item. |
|
1765 */ |
|
1766 void QAbstractItemView::mouseDoubleClickEvent(QMouseEvent *event) |
|
1767 { |
|
1768 Q_D(QAbstractItemView); |
|
1769 |
|
1770 QModelIndex index = indexAt(event->pos()); |
|
1771 if (!index.isValid() |
|
1772 || !d->isIndexEnabled(index) |
|
1773 || (d->pressedIndex != index)) { |
|
1774 QMouseEvent me(QEvent::MouseButtonPress, |
|
1775 event->pos(), event->button(), |
|
1776 event->buttons(), event->modifiers()); |
|
1777 mousePressEvent(&me); |
|
1778 return; |
|
1779 } |
|
1780 // signal handlers may change the model |
|
1781 QPersistentModelIndex persistent = index; |
|
1782 emit doubleClicked(persistent); |
|
1783 if ((event->button() & Qt::LeftButton) && !edit(persistent, DoubleClicked, event) |
|
1784 && !style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, 0, this)) |
|
1785 emit activated(persistent); |
|
1786 } |
|
1787 |
|
1788 #ifndef QT_NO_DRAGANDDROP |
|
1789 |
|
1790 /*! |
|
1791 This function is called with the given \a event when a drag and drop operation enters |
|
1792 the widget. If the drag is over a valid dropping place (e.g. over an item that |
|
1793 accepts drops), the event is accepted; otherwise it is ignored. |
|
1794 |
|
1795 \sa dropEvent() startDrag() |
|
1796 */ |
|
1797 void QAbstractItemView::dragEnterEvent(QDragEnterEvent *event) |
|
1798 { |
|
1799 if (dragDropMode() == InternalMove |
|
1800 && (event->source() != this|| !(event->possibleActions() & Qt::MoveAction))) |
|
1801 return; |
|
1802 |
|
1803 if (d_func()->canDecode(event)) { |
|
1804 event->accept(); |
|
1805 setState(DraggingState); |
|
1806 } else { |
|
1807 event->ignore(); |
|
1808 } |
|
1809 } |
|
1810 |
|
1811 /*! |
|
1812 This function is called continuously with the given \a event during a drag and |
|
1813 drop operation over the widget. It can cause the view to scroll if, for example, |
|
1814 the user drags a selection to view's right or bottom edge. In this case, the |
|
1815 event will be accepted; otherwise it will be ignored. |
|
1816 |
|
1817 \sa dropEvent() startDrag() |
|
1818 */ |
|
1819 void QAbstractItemView::dragMoveEvent(QDragMoveEvent *event) |
|
1820 { |
|
1821 Q_D(QAbstractItemView); |
|
1822 if (dragDropMode() == InternalMove |
|
1823 && (event->source() != this || !(event->possibleActions() & Qt::MoveAction))) |
|
1824 return; |
|
1825 |
|
1826 // ignore by default |
|
1827 event->ignore(); |
|
1828 |
|
1829 QModelIndex index = indexAt(event->pos()); |
|
1830 d->hover = index; |
|
1831 if (!d->droppingOnItself(event, index) |
|
1832 && d->canDecode(event)) { |
|
1833 |
|
1834 if (index.isValid() && d->showDropIndicator) { |
|
1835 QRect rect = visualRect(index); |
|
1836 d->dropIndicatorPosition = d->position(event->pos(), rect, index); |
|
1837 switch (d->dropIndicatorPosition) { |
|
1838 case AboveItem: |
|
1839 if (d->isIndexDropEnabled(index.parent())) { |
|
1840 d->dropIndicatorRect = QRect(rect.left(), rect.top(), rect.width(), 0); |
|
1841 event->accept(); |
|
1842 } else { |
|
1843 d->dropIndicatorRect = QRect(); |
|
1844 } |
|
1845 break; |
|
1846 case BelowItem: |
|
1847 if (d->isIndexDropEnabled(index.parent())) { |
|
1848 d->dropIndicatorRect = QRect(rect.left(), rect.bottom(), rect.width(), 0); |
|
1849 event->accept(); |
|
1850 } else { |
|
1851 d->dropIndicatorRect = QRect(); |
|
1852 } |
|
1853 break; |
|
1854 case OnItem: |
|
1855 if (d->isIndexDropEnabled(index)) { |
|
1856 d->dropIndicatorRect = rect; |
|
1857 event->accept(); |
|
1858 } else { |
|
1859 d->dropIndicatorRect = QRect(); |
|
1860 } |
|
1861 break; |
|
1862 case OnViewport: |
|
1863 d->dropIndicatorRect = QRect(); |
|
1864 if (d->isIndexDropEnabled(rootIndex())) { |
|
1865 event->accept(); // allow dropping in empty areas |
|
1866 } |
|
1867 break; |
|
1868 } |
|
1869 } else { |
|
1870 d->dropIndicatorRect = QRect(); |
|
1871 d->dropIndicatorPosition = OnViewport; |
|
1872 if (d->isIndexDropEnabled(rootIndex())) { |
|
1873 event->accept(); // allow dropping in empty areas |
|
1874 } |
|
1875 } |
|
1876 d->viewport->update(); |
|
1877 } // can decode |
|
1878 |
|
1879 if (d->shouldAutoScroll(event->pos())) |
|
1880 startAutoScroll(); |
|
1881 } |
|
1882 |
|
1883 /*! |
|
1884 \internal |
|
1885 Return true if this is a move from ourself and \a index is a child of the selection that |
|
1886 is being moved. |
|
1887 */ |
|
1888 bool QAbstractItemViewPrivate::droppingOnItself(QDropEvent *event, const QModelIndex &index) |
|
1889 { |
|
1890 Q_Q(QAbstractItemView); |
|
1891 Qt::DropAction dropAction = event->dropAction(); |
|
1892 if (q->dragDropMode() == QAbstractItemView::InternalMove) |
|
1893 dropAction = Qt::MoveAction; |
|
1894 if (event->source() == q |
|
1895 && event->possibleActions() & Qt::MoveAction |
|
1896 && dropAction == Qt::MoveAction) { |
|
1897 QModelIndexList selectedIndexes = q->selectedIndexes(); |
|
1898 QModelIndex child = index; |
|
1899 while (child.isValid() && child != root) { |
|
1900 if (selectedIndexes.contains(child)) |
|
1901 return true; |
|
1902 child = child.parent(); |
|
1903 } |
|
1904 } |
|
1905 return false; |
|
1906 } |
|
1907 |
|
1908 /*! |
|
1909 \fn void QAbstractItemView::dragLeaveEvent(QDragLeaveEvent *event) |
|
1910 |
|
1911 This function is called when the item being dragged leaves the view. |
|
1912 The \a event describes the state of the drag and drop operation. |
|
1913 */ |
|
1914 void QAbstractItemView::dragLeaveEvent(QDragLeaveEvent *) |
|
1915 { |
|
1916 Q_D(QAbstractItemView); |
|
1917 stopAutoScroll(); |
|
1918 setState(NoState); |
|
1919 d->hover = QModelIndex(); |
|
1920 d->viewport->update(); |
|
1921 } |
|
1922 |
|
1923 /*! |
|
1924 This function is called with the given \a event when a drop event occurs over |
|
1925 the widget. If the model accepts the even position the drop event is accepted; |
|
1926 otherwise it is ignored. |
|
1927 |
|
1928 \sa startDrag() |
|
1929 */ |
|
1930 void QAbstractItemView::dropEvent(QDropEvent *event) |
|
1931 { |
|
1932 Q_D(QAbstractItemView); |
|
1933 if (dragDropMode() == InternalMove) { |
|
1934 if (event->source() != this || !(event->possibleActions() & Qt::MoveAction)) |
|
1935 return; |
|
1936 } |
|
1937 |
|
1938 QModelIndex index; |
|
1939 int col = -1; |
|
1940 int row = -1; |
|
1941 if (d->dropOn(event, &row, &col, &index)) { |
|
1942 if (d->model->dropMimeData(event->mimeData(), |
|
1943 dragDropMode() == InternalMove ? Qt::MoveAction : event->dropAction(), row, col, index)) { |
|
1944 if (dragDropMode() == InternalMove) |
|
1945 event->setDropAction(Qt::MoveAction); |
|
1946 event->accept(); |
|
1947 } |
|
1948 } |
|
1949 stopAutoScroll(); |
|
1950 setState(NoState); |
|
1951 d->viewport->update(); |
|
1952 } |
|
1953 |
|
1954 /*! |
|
1955 If the event hasn't already been accepted, determines the index to drop on. |
|
1956 |
|
1957 if (row == -1 && col == -1) |
|
1958 // append to this drop index |
|
1959 else |
|
1960 // place at row, col in drop index |
|
1961 |
|
1962 If it returns true a drop can be done, and dropRow, dropCol and dropIndex reflects the position of the drop. |
|
1963 \internal |
|
1964 */ |
|
1965 bool QAbstractItemViewPrivate::dropOn(QDropEvent *event, int *dropRow, int *dropCol, QModelIndex *dropIndex) |
|
1966 { |
|
1967 Q_Q(QAbstractItemView); |
|
1968 if (event->isAccepted()) |
|
1969 return false; |
|
1970 |
|
1971 QModelIndex index; |
|
1972 // rootIndex() (i.e. the viewport) might be a valid index |
|
1973 if (viewport->rect().contains(event->pos())) { |
|
1974 index = q->indexAt(event->pos()); |
|
1975 if (!index.isValid() || !q->visualRect(index).contains(event->pos())) |
|
1976 index = root; |
|
1977 } |
|
1978 |
|
1979 // If we are allowed to do the drop |
|
1980 if (model->supportedDropActions() & event->dropAction()) { |
|
1981 int row = -1; |
|
1982 int col = -1; |
|
1983 if (index != root) { |
|
1984 dropIndicatorPosition = position(event->pos(), q->visualRect(index), index); |
|
1985 switch (dropIndicatorPosition) { |
|
1986 case QAbstractItemView::AboveItem: |
|
1987 row = index.row(); |
|
1988 col = index.column(); |
|
1989 index = index.parent(); |
|
1990 break; |
|
1991 case QAbstractItemView::BelowItem: |
|
1992 row = index.row() + 1; |
|
1993 col = index.column(); |
|
1994 index = index.parent(); |
|
1995 break; |
|
1996 case QAbstractItemView::OnItem: |
|
1997 case QAbstractItemView::OnViewport: |
|
1998 break; |
|
1999 } |
|
2000 } else { |
|
2001 dropIndicatorPosition = QAbstractItemView::OnViewport; |
|
2002 } |
|
2003 *dropIndex = index; |
|
2004 *dropRow = row; |
|
2005 *dropCol = col; |
|
2006 if (!droppingOnItself(event, index)) |
|
2007 return true; |
|
2008 } |
|
2009 return false; |
|
2010 } |
|
2011 |
|
2012 QAbstractItemView::DropIndicatorPosition |
|
2013 QAbstractItemViewPrivate::position(const QPoint &pos, const QRect &rect, const QModelIndex &index) const |
|
2014 { |
|
2015 QAbstractItemView::DropIndicatorPosition r = QAbstractItemView::OnViewport; |
|
2016 if (!overwrite) { |
|
2017 const int margin = 2; |
|
2018 if (pos.y() - rect.top() < margin) { |
|
2019 r = QAbstractItemView::AboveItem; |
|
2020 } else if (rect.bottom() - pos.y() < margin) { |
|
2021 r = QAbstractItemView::BelowItem; |
|
2022 } else if (rect.contains(pos, true)) { |
|
2023 r = QAbstractItemView::OnItem; |
|
2024 } |
|
2025 } else { |
|
2026 QRect touchingRect = rect; |
|
2027 touchingRect.adjust(-1, -1, 1, 1); |
|
2028 if (touchingRect.contains(pos, false)) { |
|
2029 r = QAbstractItemView::OnItem; |
|
2030 } |
|
2031 } |
|
2032 |
|
2033 if (r == QAbstractItemView::OnItem && (!(model->flags(index) & Qt::ItemIsDropEnabled))) |
|
2034 r = pos.y() < rect.center().y() ? QAbstractItemView::AboveItem : QAbstractItemView::BelowItem; |
|
2035 |
|
2036 return r; |
|
2037 } |
|
2038 |
|
2039 #endif // QT_NO_DRAGANDDROP |
|
2040 |
|
2041 /*! |
|
2042 This function is called with the given \a event when the widget obtains the focus. |
|
2043 By default, the event is ignored. |
|
2044 |
|
2045 \sa setFocus(), focusOutEvent() |
|
2046 */ |
|
2047 void QAbstractItemView::focusInEvent(QFocusEvent *event) |
|
2048 { |
|
2049 Q_D(QAbstractItemView); |
|
2050 QAbstractScrollArea::focusInEvent(event); |
|
2051 if (selectionModel() |
|
2052 && !d->currentIndexSet |
|
2053 && !currentIndex().isValid()) { |
|
2054 bool autoScroll = d->autoScroll; |
|
2055 d->autoScroll = false; |
|
2056 QModelIndex index = moveCursor(MoveNext, Qt::NoModifier); // first visible index |
|
2057 if (index.isValid() && d->isIndexEnabled(index)) |
|
2058 selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); |
|
2059 d->autoScroll = autoScroll; |
|
2060 } |
|
2061 d->viewport->update(); |
|
2062 } |
|
2063 |
|
2064 /*! |
|
2065 This function is called with the given \a event when the widget |
|
2066 looses the focus. By default, the event is ignored. |
|
2067 |
|
2068 \sa clearFocus(), focusInEvent() |
|
2069 */ |
|
2070 void QAbstractItemView::focusOutEvent(QFocusEvent *event) |
|
2071 { |
|
2072 Q_D(QAbstractItemView); |
|
2073 QAbstractScrollArea::focusOutEvent(event); |
|
2074 d->viewport->update(); |
|
2075 } |
|
2076 |
|
2077 /*! |
|
2078 This function is called with the given \a event when a key event is sent to |
|
2079 the widget. The default implementation handles basic cursor movement, e.g. Up, |
|
2080 Down, Left, Right, Home, PageUp, and PageDown; the activated() signal is |
|
2081 emitted if the current index is valid and the activation key is pressed |
|
2082 (e.g. Enter or Return, depending on the platform). |
|
2083 This function is where editing is initiated by key press, e.g. if F2 is |
|
2084 pressed. |
|
2085 |
|
2086 \sa edit(), moveCursor(), keyboardSearch(), tabKeyNavigation |
|
2087 */ |
|
2088 void QAbstractItemView::keyPressEvent(QKeyEvent *event) |
|
2089 { |
|
2090 Q_D(QAbstractItemView); |
|
2091 d->delayedAutoScroll.stop(); //any interaction with the view cancel the auto scrolling |
|
2092 |
|
2093 #ifdef QT_KEYPAD_NAVIGATION |
|
2094 switch (event->key()) { |
|
2095 case Qt::Key_Select: |
|
2096 if (QApplication::keypadNavigationEnabled()) { |
|
2097 if (!hasEditFocus()) { |
|
2098 setEditFocus(true); |
|
2099 #ifdef QT_SOFTKEYS_ENABLED |
|
2100 addAction(d->doneSoftKey); |
|
2101 #endif |
|
2102 return; |
|
2103 } |
|
2104 } |
|
2105 break; |
|
2106 case Qt::Key_Back: |
|
2107 if (QApplication::keypadNavigationEnabled() && hasEditFocus()) { |
|
2108 #ifdef QT_SOFTKEYS_ENABLED |
|
2109 removeAction(d->doneSoftKey); |
|
2110 #endif |
|
2111 setEditFocus(false); |
|
2112 } else { |
|
2113 event->ignore(); |
|
2114 } |
|
2115 return; |
|
2116 default: |
|
2117 if (QApplication::keypadNavigationEnabled() && !hasEditFocus()) { |
|
2118 event->ignore(); |
|
2119 return; |
|
2120 } |
|
2121 } |
|
2122 #endif |
|
2123 |
|
2124 #if !defined(QT_NO_CLIPBOARD) && !defined(QT_NO_SHORTCUT) |
|
2125 if (event == QKeySequence::Copy) { |
|
2126 QVariant variant; |
|
2127 if (d->model) |
|
2128 variant = d->model->data(currentIndex(), Qt::DisplayRole); |
|
2129 if (variant.type() == QVariant::String) |
|
2130 QApplication::clipboard()->setText(variant.toString()); |
|
2131 event->accept(); |
|
2132 } |
|
2133 #endif |
|
2134 |
|
2135 QPersistentModelIndex newCurrent; |
|
2136 switch (event->key()) { |
|
2137 case Qt::Key_Down: |
|
2138 newCurrent = moveCursor(MoveDown, event->modifiers()); |
|
2139 break; |
|
2140 case Qt::Key_Up: |
|
2141 newCurrent = moveCursor(MoveUp, event->modifiers()); |
|
2142 break; |
|
2143 case Qt::Key_Left: |
|
2144 newCurrent = moveCursor(MoveLeft, event->modifiers()); |
|
2145 break; |
|
2146 case Qt::Key_Right: |
|
2147 newCurrent = moveCursor(MoveRight, event->modifiers()); |
|
2148 break; |
|
2149 case Qt::Key_Home: |
|
2150 newCurrent = moveCursor(MoveHome, event->modifiers()); |
|
2151 break; |
|
2152 case Qt::Key_End: |
|
2153 newCurrent = moveCursor(MoveEnd, event->modifiers()); |
|
2154 break; |
|
2155 case Qt::Key_PageUp: |
|
2156 newCurrent = moveCursor(MovePageUp, event->modifiers()); |
|
2157 break; |
|
2158 case Qt::Key_PageDown: |
|
2159 newCurrent = moveCursor(MovePageDown, event->modifiers()); |
|
2160 break; |
|
2161 case Qt::Key_Tab: |
|
2162 if (d->tabKeyNavigation) |
|
2163 newCurrent = moveCursor(MoveNext, event->modifiers()); |
|
2164 break; |
|
2165 case Qt::Key_Backtab: |
|
2166 if (d->tabKeyNavigation) |
|
2167 newCurrent = moveCursor(MovePrevious, event->modifiers()); |
|
2168 break; |
|
2169 } |
|
2170 |
|
2171 QPersistentModelIndex oldCurrent = currentIndex(); |
|
2172 if (newCurrent != oldCurrent && newCurrent.isValid() && d->isIndexEnabled(newCurrent)) { |
|
2173 if (!hasFocus() && QApplication::focusWidget() == indexWidget(oldCurrent)) |
|
2174 setFocus(); |
|
2175 QItemSelectionModel::SelectionFlags command = selectionCommand(newCurrent, event); |
|
2176 if (command != QItemSelectionModel::NoUpdate |
|
2177 || style()->styleHint(QStyle::SH_ItemView_MovementWithoutUpdatingSelection, 0, this)) { |
|
2178 // note that we don't check if the new current index is enabled because moveCursor() makes sure it is |
|
2179 if (command & QItemSelectionModel::Current) { |
|
2180 d->selectionModel->setCurrentIndex(newCurrent, QItemSelectionModel::NoUpdate); |
|
2181 if (!indexAt(d->pressedPosition).isValid()) |
|
2182 d->pressedPosition = visualRect(oldCurrent).center() + d->offset(); |
|
2183 QRect rect(d->pressedPosition - d->offset(), visualRect(newCurrent).center()); |
|
2184 setSelection(rect, command); |
|
2185 } else { |
|
2186 d->selectionModel->setCurrentIndex(newCurrent, command); |
|
2187 d->pressedPosition = visualRect(newCurrent).center() + d->offset(); |
|
2188 // We copy the same behaviour as for mousePressEvent(). |
|
2189 QRect rect(d->pressedPosition - d->offset(), QSize(1, 1)); |
|
2190 setSelection(rect, command); |
|
2191 } |
|
2192 return; |
|
2193 } |
|
2194 } |
|
2195 |
|
2196 switch (event->key()) { |
|
2197 // ignored keys |
|
2198 case Qt::Key_Down: |
|
2199 case Qt::Key_Up: |
|
2200 #ifdef QT_KEYPAD_NAVIGATION |
|
2201 if (QApplication::keypadNavigationEnabled()) { |
|
2202 event->accept(); // don't change focus |
|
2203 break; |
|
2204 } |
|
2205 #endif |
|
2206 case Qt::Key_Left: |
|
2207 case Qt::Key_Right: |
|
2208 #ifdef QT_KEYPAD_NAVIGATION |
|
2209 if (QApplication::navigationMode() == Qt::NavigationModeKeypadDirectional) { |
|
2210 event->accept(); // don't change horizontal focus in directional mode |
|
2211 break; |
|
2212 } |
|
2213 #endif // QT_KEYPAD_NAVIGATION |
|
2214 case Qt::Key_Home: |
|
2215 case Qt::Key_End: |
|
2216 case Qt::Key_PageUp: |
|
2217 case Qt::Key_PageDown: |
|
2218 case Qt::Key_Escape: |
|
2219 case Qt::Key_Shift: |
|
2220 case Qt::Key_Control: |
|
2221 event->ignore(); |
|
2222 break; |
|
2223 case Qt::Key_Space: |
|
2224 case Qt::Key_Select: |
|
2225 if (!edit(currentIndex(), AnyKeyPressed, event) && d->selectionModel) |
|
2226 d->selectionModel->select(currentIndex(), selectionCommand(currentIndex(), event)); |
|
2227 #ifdef QT_KEYPAD_NAVIGATION |
|
2228 if ( event->key()==Qt::Key_Select ) { |
|
2229 // Also do Key_Enter action. |
|
2230 if (currentIndex().isValid()) { |
|
2231 if (state() != EditingState) |
|
2232 emit activated(currentIndex()); |
|
2233 } else { |
|
2234 event->ignore(); |
|
2235 } |
|
2236 } |
|
2237 #endif |
|
2238 break; |
|
2239 #ifdef Q_WS_MAC |
|
2240 case Qt::Key_Enter: |
|
2241 case Qt::Key_Return: |
|
2242 // Propagate the enter if you couldn't edit the item and there are no |
|
2243 // current editors (if there are editors, the event was most likely propagated from it). |
|
2244 if (!edit(currentIndex(), EditKeyPressed, event) && d->editors.isEmpty()) |
|
2245 event->ignore(); |
|
2246 break; |
|
2247 #else |
|
2248 case Qt::Key_F2: |
|
2249 if (!edit(currentIndex(), EditKeyPressed, event)) |
|
2250 event->ignore(); |
|
2251 break; |
|
2252 case Qt::Key_Enter: |
|
2253 case Qt::Key_Return: |
|
2254 // ### we can't open the editor on enter, becuse |
|
2255 // some widgets will forward the enter event back |
|
2256 // to the viewport, starting an endless loop |
|
2257 if (state() != EditingState || hasFocus()) { |
|
2258 if (currentIndex().isValid()) |
|
2259 emit activated(currentIndex()); |
|
2260 event->ignore(); |
|
2261 } |
|
2262 break; |
|
2263 #endif |
|
2264 case Qt::Key_A: |
|
2265 if (event->modifiers() & Qt::ControlModifier) { |
|
2266 selectAll(); |
|
2267 break; |
|
2268 } |
|
2269 default: { |
|
2270 #ifdef Q_WS_MAC |
|
2271 if (event->key() == Qt::Key_O && event->modifiers() & Qt::ControlModifier && currentIndex().isValid()) { |
|
2272 emit activated(currentIndex()); |
|
2273 break; |
|
2274 } |
|
2275 #endif |
|
2276 bool modified = (event->modifiers() & (Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier)); |
|
2277 if (!event->text().isEmpty() && !modified && !edit(currentIndex(), AnyKeyPressed, event)) { |
|
2278 keyboardSearch(event->text()); |
|
2279 event->accept(); |
|
2280 } else { |
|
2281 event->ignore(); |
|
2282 } |
|
2283 break; } |
|
2284 } |
|
2285 } |
|
2286 |
|
2287 /*! |
|
2288 This function is called with the given \a event when a resize event is sent to |
|
2289 the widget. |
|
2290 |
|
2291 \sa QWidget::resizeEvent() |
|
2292 */ |
|
2293 void QAbstractItemView::resizeEvent(QResizeEvent *event) |
|
2294 { |
|
2295 QAbstractScrollArea::resizeEvent(event); |
|
2296 updateGeometries(); |
|
2297 } |
|
2298 |
|
2299 /*! |
|
2300 This function is called with the given \a event when a timer event is sent |
|
2301 to the widget. |
|
2302 |
|
2303 \sa QObject::timerEvent() |
|
2304 */ |
|
2305 void QAbstractItemView::timerEvent(QTimerEvent *event) |
|
2306 { |
|
2307 Q_D(QAbstractItemView); |
|
2308 if (event->timerId() == d->fetchMoreTimer.timerId()) |
|
2309 d->fetchMore(); |
|
2310 else if (event->timerId() == d->delayedReset.timerId()) |
|
2311 reset(); |
|
2312 else if (event->timerId() == d->autoScrollTimer.timerId()) |
|
2313 doAutoScroll(); |
|
2314 else if (event->timerId() == d->updateTimer.timerId()) |
|
2315 d->updateDirtyRegion(); |
|
2316 else if (event->timerId() == d->delayedEditing.timerId()) { |
|
2317 d->delayedEditing.stop(); |
|
2318 edit(currentIndex()); |
|
2319 } else if (event->timerId() == d->delayedLayout.timerId()) { |
|
2320 d->delayedLayout.stop(); |
|
2321 if (isVisible()) { |
|
2322 d->interruptDelayedItemsLayout(); |
|
2323 doItemsLayout(); |
|
2324 const QModelIndex current = currentIndex(); |
|
2325 if (current.isValid() && d->state == QAbstractItemView::EditingState) |
|
2326 scrollTo(current); |
|
2327 } |
|
2328 } else if (event->timerId() == d->delayedAutoScroll.timerId()) { |
|
2329 d->delayedAutoScroll.stop(); |
|
2330 //end of the timer: if the current item is still the same as the one when the mouse press occurred |
|
2331 //we only get here if there was no double click |
|
2332 if (d->pressedIndex.isValid() && d->pressedIndex == currentIndex()) |
|
2333 scrollTo(d->pressedIndex); |
|
2334 } |
|
2335 } |
|
2336 |
|
2337 /*! |
|
2338 \reimp |
|
2339 */ |
|
2340 void QAbstractItemView::inputMethodEvent(QInputMethodEvent *event) |
|
2341 { |
|
2342 if (event->commitString().isEmpty() && event->preeditString().isEmpty()) { |
|
2343 event->ignore(); |
|
2344 return; |
|
2345 } |
|
2346 if (!edit(currentIndex(), AnyKeyPressed, event)) { |
|
2347 if (!event->commitString().isEmpty()) |
|
2348 keyboardSearch(event->commitString()); |
|
2349 event->ignore(); |
|
2350 } |
|
2351 } |
|
2352 |
|
2353 #ifndef QT_NO_DRAGANDDROP |
|
2354 /*! |
|
2355 \enum QAbstractItemView::DropIndicatorPosition |
|
2356 |
|
2357 This enum indicates the position of the drop indicator in |
|
2358 relation to the index at the current mouse position: |
|
2359 |
|
2360 \value OnItem The item will be dropped on the index. |
|
2361 |
|
2362 \value AboveItem The item will be dropped above the index. |
|
2363 |
|
2364 \value BelowItem The item will be dropped below the index. |
|
2365 |
|
2366 \value OnViewport The item will be dropped onto a region of the viewport with |
|
2367 no items. The way each view handles items dropped onto the viewport depends on |
|
2368 the behavior of the underlying model in use. |
|
2369 */ |
|
2370 |
|
2371 |
|
2372 /*! |
|
2373 \since 4.1 |
|
2374 |
|
2375 Returns the position of the drop indicator in relation to the closest item. |
|
2376 */ |
|
2377 QAbstractItemView::DropIndicatorPosition QAbstractItemView::dropIndicatorPosition() const |
|
2378 { |
|
2379 Q_D(const QAbstractItemView); |
|
2380 return d->dropIndicatorPosition; |
|
2381 } |
|
2382 #endif |
|
2383 |
|
2384 /*! |
|
2385 This convenience function returns a list of all selected and |
|
2386 non-hidden item indexes in the view. The list contains no |
|
2387 duplicates, and is not sorted. |
|
2388 |
|
2389 \sa QItemSelectionModel::selectedIndexes() |
|
2390 */ |
|
2391 QModelIndexList QAbstractItemView::selectedIndexes() const |
|
2392 { |
|
2393 Q_D(const QAbstractItemView); |
|
2394 QModelIndexList indexes; |
|
2395 if (d->selectionModel) { |
|
2396 indexes = d->selectionModel->selectedIndexes(); |
|
2397 QList<QModelIndex>::iterator it = indexes.begin(); |
|
2398 while (it != indexes.end()) |
|
2399 if (isIndexHidden(*it)) |
|
2400 it = indexes.erase(it); |
|
2401 else |
|
2402 ++it; |
|
2403 } |
|
2404 return indexes; |
|
2405 } |
|
2406 |
|
2407 /*! |
|
2408 Starts editing the item at \a index, creating an editor if |
|
2409 necessary, and returns true if the view's \l{State} is now |
|
2410 EditingState; otherwise returns false. |
|
2411 |
|
2412 The action that caused the editing process is described by |
|
2413 \a trigger, and the associated event is specified by \a event. |
|
2414 |
|
2415 Editing can be forced by specifying the \a trigger to be |
|
2416 QAbstractItemView::AllEditTriggers. |
|
2417 |
|
2418 \sa closeEditor() |
|
2419 */ |
|
2420 bool QAbstractItemView::edit(const QModelIndex &index, EditTrigger trigger, QEvent *event) |
|
2421 { |
|
2422 Q_D(QAbstractItemView); |
|
2423 |
|
2424 if (!d->isIndexValid(index)) |
|
2425 return false; |
|
2426 |
|
2427 if (QWidget *w = (d->persistent.isEmpty() ? static_cast<QWidget*>(0) : d->editorForIndex(index).editor.data())) { |
|
2428 if (w->focusPolicy() == Qt::NoFocus) |
|
2429 return false; |
|
2430 w->setFocus(); |
|
2431 return true; |
|
2432 } |
|
2433 |
|
2434 if (trigger == DoubleClicked) { |
|
2435 d->delayedEditing.stop(); |
|
2436 d->delayedAutoScroll.stop(); |
|
2437 } else if (trigger == CurrentChanged) { |
|
2438 d->delayedEditing.stop(); |
|
2439 } |
|
2440 |
|
2441 if (d->sendDelegateEvent(index, event)) { |
|
2442 update(index); |
|
2443 return true; |
|
2444 } |
|
2445 |
|
2446 // save the previous trigger before updating |
|
2447 EditTriggers lastTrigger = d->lastTrigger; |
|
2448 d->lastTrigger = trigger; |
|
2449 |
|
2450 if (!d->shouldEdit(trigger, d->model->buddy(index))) |
|
2451 return false; |
|
2452 |
|
2453 if (d->delayedEditing.isActive()) |
|
2454 return false; |
|
2455 |
|
2456 // we will receive a mouseButtonReleaseEvent after a |
|
2457 // mouseDoubleClickEvent, so we need to check the previous trigger |
|
2458 if (lastTrigger == DoubleClicked && trigger == SelectedClicked) |
|
2459 return false; |
|
2460 |
|
2461 // we may get a double click event later |
|
2462 if (trigger == SelectedClicked) |
|
2463 d->delayedEditing.start(QApplication::doubleClickInterval(), this); |
|
2464 else |
|
2465 d->openEditor(index, d->shouldForwardEvent(trigger, event) ? event : 0); |
|
2466 |
|
2467 return true; |
|
2468 } |
|
2469 |
|
2470 /*! |
|
2471 \internal |
|
2472 Updates the data shown in the open editor widgets in the view. |
|
2473 */ |
|
2474 void QAbstractItemView::updateEditorData() |
|
2475 { |
|
2476 Q_D(QAbstractItemView); |
|
2477 d->updateEditorData(QModelIndex(), QModelIndex()); |
|
2478 } |
|
2479 |
|
2480 /*! |
|
2481 \internal |
|
2482 Updates the geometry of the open editor widgets in the view. |
|
2483 */ |
|
2484 void QAbstractItemView::updateEditorGeometries() |
|
2485 { |
|
2486 Q_D(QAbstractItemView); |
|
2487 if(d->editors.isEmpty()) |
|
2488 return; |
|
2489 QStyleOptionViewItemV4 option = d->viewOptionsV4(); |
|
2490 QList<QEditorInfo>::iterator it = d->editors.begin(); |
|
2491 QWidgetList editorsToRelease; |
|
2492 while (it != d->editors.end()) { |
|
2493 QModelIndex index = it->index; |
|
2494 QWidget *editor = it->editor; |
|
2495 if (index.isValid() && editor) { |
|
2496 option.rect = visualRect(index); |
|
2497 if (option.rect.isValid()) { |
|
2498 editor->show(); |
|
2499 QAbstractItemDelegate *delegate = d->delegateForIndex(index); |
|
2500 if (delegate) |
|
2501 delegate->updateEditorGeometry(editor, option, index); |
|
2502 } else { |
|
2503 editor->hide(); |
|
2504 } |
|
2505 ++it; |
|
2506 } else { |
|
2507 it = d->editors.erase(it); |
|
2508 editorsToRelease << editor; |
|
2509 } |
|
2510 } |
|
2511 |
|
2512 //we release the editor outside of the loop because it might change the focus and try |
|
2513 //to change the d->editors list. |
|
2514 for (int i = 0; i < editorsToRelease.count(); ++i) { |
|
2515 d->releaseEditor(editorsToRelease.at(i)); |
|
2516 } |
|
2517 } |
|
2518 |
|
2519 /*! |
|
2520 \since 4.4 |
|
2521 |
|
2522 Updates the geometry of the child widgets of the view. |
|
2523 */ |
|
2524 void QAbstractItemView::updateGeometries() |
|
2525 { |
|
2526 updateEditorGeometries(); |
|
2527 d_func()->fetchMoreTimer.start(0, this); //fetch more later |
|
2528 } |
|
2529 |
|
2530 /*! |
|
2531 \internal |
|
2532 */ |
|
2533 void QAbstractItemView::verticalScrollbarValueChanged(int value) |
|
2534 { |
|
2535 Q_D(QAbstractItemView); |
|
2536 if (verticalScrollBar()->maximum() == value && d->model->canFetchMore(d->root)) |
|
2537 d->model->fetchMore(d->root); |
|
2538 d->checkMouseMove(viewport()->mapFromGlobal(QCursor::pos())); |
|
2539 } |
|
2540 |
|
2541 /*! |
|
2542 \internal |
|
2543 */ |
|
2544 void QAbstractItemView::horizontalScrollbarValueChanged(int value) |
|
2545 { |
|
2546 Q_D(QAbstractItemView); |
|
2547 if (horizontalScrollBar()->maximum() == value && d->model->canFetchMore(d->root)) |
|
2548 d->model->fetchMore(d->root); |
|
2549 d->checkMouseMove(viewport()->mapFromGlobal(QCursor::pos())); |
|
2550 } |
|
2551 |
|
2552 /*! |
|
2553 \internal |
|
2554 */ |
|
2555 void QAbstractItemView::verticalScrollbarAction(int) |
|
2556 { |
|
2557 //do nothing |
|
2558 } |
|
2559 |
|
2560 /*! |
|
2561 \internal |
|
2562 */ |
|
2563 void QAbstractItemView::horizontalScrollbarAction(int) |
|
2564 { |
|
2565 //do nothing |
|
2566 } |
|
2567 |
|
2568 /*! |
|
2569 Closes the given \a editor, and releases it. The \a hint is |
|
2570 used to specify how the view should respond to the end of the editing |
|
2571 operation. For example, the hint may indicate that the next item in |
|
2572 the view should be opened for editing. |
|
2573 |
|
2574 \sa edit(), commitData() |
|
2575 */ |
|
2576 |
|
2577 void QAbstractItemView::closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint) |
|
2578 { |
|
2579 Q_D(QAbstractItemView); |
|
2580 |
|
2581 // Close the editor |
|
2582 if (editor) { |
|
2583 bool isPersistent = d->persistent.contains(editor); |
|
2584 bool hadFocus = editor->hasFocus(); |
|
2585 QModelIndex index = d->indexForEditor(editor); |
|
2586 if (!index.isValid()) |
|
2587 return; // the editor was not registered |
|
2588 |
|
2589 if (!isPersistent) { |
|
2590 setState(NoState); |
|
2591 QModelIndex index = d->indexForEditor(editor); |
|
2592 editor->removeEventFilter(d->delegateForIndex(index)); |
|
2593 d->removeEditor(editor); |
|
2594 } |
|
2595 if (hadFocus) |
|
2596 setFocus(); // this will send a focusLost event to the editor |
|
2597 else |
|
2598 d->checkPersistentEditorFocus(); |
|
2599 |
|
2600 QPointer<QWidget> ed = editor; |
|
2601 QApplication::sendPostedEvents(editor, 0); |
|
2602 editor = ed; |
|
2603 |
|
2604 if (!isPersistent && editor) |
|
2605 d->releaseEditor(editor); |
|
2606 } |
|
2607 |
|
2608 // The EndEditHint part |
|
2609 QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::ClearAndSelect |
|
2610 | d->selectionBehaviorFlags(); |
|
2611 switch (hint) { |
|
2612 case QAbstractItemDelegate::EditNextItem: { |
|
2613 QModelIndex index = moveCursor(MoveNext, Qt::NoModifier); |
|
2614 if (index.isValid()) { |
|
2615 QPersistentModelIndex persistent(index); |
|
2616 d->selectionModel->setCurrentIndex(persistent, flags); |
|
2617 // currentChanged signal would have already started editing |
|
2618 if (index.flags() & Qt::ItemIsEditable |
|
2619 && (!(editTriggers() & QAbstractItemView::CurrentChanged))) |
|
2620 edit(persistent); |
|
2621 } break; } |
|
2622 case QAbstractItemDelegate::EditPreviousItem: { |
|
2623 QModelIndex index = moveCursor(MovePrevious, Qt::NoModifier); |
|
2624 if (index.isValid()) { |
|
2625 QPersistentModelIndex persistent(index); |
|
2626 d->selectionModel->setCurrentIndex(persistent, flags); |
|
2627 // currentChanged signal would have already started editing |
|
2628 if (index.flags() & Qt::ItemIsEditable |
|
2629 && (!(editTriggers() & QAbstractItemView::CurrentChanged))) |
|
2630 edit(persistent); |
|
2631 } break; } |
|
2632 case QAbstractItemDelegate::SubmitModelCache: |
|
2633 d->model->submit(); |
|
2634 break; |
|
2635 case QAbstractItemDelegate::RevertModelCache: |
|
2636 d->model->revert(); |
|
2637 break; |
|
2638 default: |
|
2639 break; |
|
2640 } |
|
2641 } |
|
2642 |
|
2643 /*! |
|
2644 Commit the data in the \a editor to the model. |
|
2645 |
|
2646 \sa closeEditor() |
|
2647 */ |
|
2648 void QAbstractItemView::commitData(QWidget *editor) |
|
2649 { |
|
2650 Q_D(QAbstractItemView); |
|
2651 if (!editor || !d->itemDelegate || d->currentlyCommittingEditor) |
|
2652 return; |
|
2653 QModelIndex index = d->indexForEditor(editor); |
|
2654 if (!index.isValid()) |
|
2655 return; |
|
2656 d->currentlyCommittingEditor = editor; |
|
2657 QAbstractItemDelegate *delegate = d->delegateForIndex(index); |
|
2658 editor->removeEventFilter(delegate); |
|
2659 delegate->setModelData(editor, d->model, index); |
|
2660 editor->installEventFilter(delegate); |
|
2661 d->currentlyCommittingEditor = 0; |
|
2662 } |
|
2663 |
|
2664 /*! |
|
2665 This function is called when the given \a editor has been destroyed. |
|
2666 |
|
2667 \sa closeEditor() |
|
2668 */ |
|
2669 void QAbstractItemView::editorDestroyed(QObject *editor) |
|
2670 { |
|
2671 Q_D(QAbstractItemView); |
|
2672 QWidget *w = qobject_cast<QWidget*>(editor); |
|
2673 d->removeEditor(w); |
|
2674 d->persistent.remove(w); |
|
2675 if (state() == EditingState) |
|
2676 setState(NoState); |
|
2677 } |
|
2678 |
|
2679 /*! |
|
2680 \obsolete |
|
2681 Sets the horizontal scroll bar's steps per item to \a steps. |
|
2682 |
|
2683 This is the number of steps used by the horizontal scroll bar to |
|
2684 represent the width of an item. |
|
2685 |
|
2686 Note that if the view has a horizontal header, the item steps |
|
2687 will be ignored and the header section size will be used instead. |
|
2688 |
|
2689 \sa horizontalStepsPerItem() setVerticalStepsPerItem() |
|
2690 */ |
|
2691 void QAbstractItemView::setHorizontalStepsPerItem(int steps) |
|
2692 { |
|
2693 Q_UNUSED(steps); |
|
2694 // do nothing |
|
2695 } |
|
2696 |
|
2697 /*! |
|
2698 \obsolete |
|
2699 Returns the horizontal scroll bar's steps per item. |
|
2700 |
|
2701 \sa setHorizontalStepsPerItem() verticalStepsPerItem() |
|
2702 */ |
|
2703 int QAbstractItemView::horizontalStepsPerItem() const |
|
2704 { |
|
2705 return 1; |
|
2706 } |
|
2707 |
|
2708 /*! |
|
2709 \obsolete |
|
2710 Sets the vertical scroll bar's steps per item to \a steps. |
|
2711 |
|
2712 This is the number of steps used by the vertical scroll bar to |
|
2713 represent the height of an item. |
|
2714 |
|
2715 Note that if the view has a vertical header, the item steps |
|
2716 will be ignored and the header section size will be used instead. |
|
2717 |
|
2718 \sa verticalStepsPerItem() setHorizontalStepsPerItem() |
|
2719 */ |
|
2720 void QAbstractItemView::setVerticalStepsPerItem(int steps) |
|
2721 { |
|
2722 Q_UNUSED(steps); |
|
2723 // do nothing |
|
2724 } |
|
2725 |
|
2726 /*! |
|
2727 \obsolete |
|
2728 Returns the vertical scroll bar's steps per item. |
|
2729 |
|
2730 \sa setVerticalStepsPerItem() horizontalStepsPerItem() |
|
2731 */ |
|
2732 int QAbstractItemView::verticalStepsPerItem() const |
|
2733 { |
|
2734 return 1; |
|
2735 } |
|
2736 |
|
2737 /*! |
|
2738 Moves to and selects the item best matching the string \a search. |
|
2739 If no item is found nothing happens. |
|
2740 |
|
2741 In the default implementation, the search is reset if \a search is empty, or |
|
2742 the time interval since the last search has exceeded |
|
2743 QApplication::keyboardInputInterval(). |
|
2744 */ |
|
2745 void QAbstractItemView::keyboardSearch(const QString &search) |
|
2746 { |
|
2747 Q_D(QAbstractItemView); |
|
2748 if (!d->model->rowCount(d->root) || !d->model->columnCount(d->root)) |
|
2749 return; |
|
2750 |
|
2751 QModelIndex start = currentIndex().isValid() ? currentIndex() |
|
2752 : d->model->index(0, 0, d->root); |
|
2753 QTime now(QTime::currentTime()); |
|
2754 bool skipRow = false; |
|
2755 if (search.isEmpty() |
|
2756 || (d->keyboardInputTime.msecsTo(now) > QApplication::keyboardInputInterval())) { |
|
2757 d->keyboardInput = search; |
|
2758 skipRow = currentIndex().isValid(); //if it is not valid we should really start at QModelIndex(0,0) |
|
2759 } else { |
|
2760 d->keyboardInput += search; |
|
2761 } |
|
2762 d->keyboardInputTime = now; |
|
2763 |
|
2764 // special case for searches with same key like 'aaaaa' |
|
2765 bool sameKey = false; |
|
2766 if (d->keyboardInput.length() > 1) { |
|
2767 int c = d->keyboardInput.count(d->keyboardInput.at(d->keyboardInput.length() - 1)); |
|
2768 sameKey = (c == d->keyboardInput.length()); |
|
2769 if (sameKey) |
|
2770 skipRow = true; |
|
2771 } |
|
2772 |
|
2773 // skip if we are searching for the same key or a new search started |
|
2774 if (skipRow) { |
|
2775 QModelIndex parent = start.parent(); |
|
2776 int newRow = (start.row() < d->model->rowCount(parent) - 1) ? start.row() + 1 : 0; |
|
2777 start = d->model->index(newRow, start.column(), parent); |
|
2778 } |
|
2779 |
|
2780 // search from start with wraparound |
|
2781 const QString searchString = sameKey ? QString(d->keyboardInput.at(0)) : d->keyboardInput; |
|
2782 QModelIndex current = start; |
|
2783 QModelIndexList match; |
|
2784 QModelIndex firstMatch; |
|
2785 QModelIndex startMatch; |
|
2786 QModelIndexList previous; |
|
2787 do { |
|
2788 match = d->model->match(current, Qt::DisplayRole, searchString); |
|
2789 if (match == previous) |
|
2790 break; |
|
2791 firstMatch = match.value(0); |
|
2792 previous = match; |
|
2793 if (firstMatch.isValid()) { |
|
2794 if (d->isIndexEnabled(firstMatch)) { |
|
2795 setCurrentIndex(firstMatch); |
|
2796 break; |
|
2797 } |
|
2798 int row = firstMatch.row() + 1; |
|
2799 if (row >= d->model->rowCount(firstMatch.parent())) |
|
2800 row = 0; |
|
2801 current = firstMatch.sibling(row, firstMatch.column()); |
|
2802 |
|
2803 //avoid infinite loop if all the matching items are disabled. |
|
2804 if (!startMatch.isValid()) |
|
2805 startMatch = firstMatch; |
|
2806 else if (startMatch == firstMatch) |
|
2807 break; |
|
2808 } |
|
2809 } while (current != start && firstMatch.isValid()); |
|
2810 } |
|
2811 |
|
2812 /*! |
|
2813 Returns the size hint for the item with the specified \a index or |
|
2814 an invalid size for invalid indexes. |
|
2815 |
|
2816 \sa sizeHintForRow(), sizeHintForColumn() |
|
2817 */ |
|
2818 QSize QAbstractItemView::sizeHintForIndex(const QModelIndex &index) const |
|
2819 { |
|
2820 Q_D(const QAbstractItemView); |
|
2821 if (!d->isIndexValid(index) || !d->itemDelegate) |
|
2822 return QSize(); |
|
2823 return d->delegateForIndex(index)->sizeHint(d->viewOptionsV4(), index); |
|
2824 } |
|
2825 |
|
2826 /*! |
|
2827 Returns the height size hint for the specified \a row or -1 if |
|
2828 there is no model. |
|
2829 |
|
2830 The returned height is calculated using the size hints of the |
|
2831 given \a row's items, i.e. the returned value is the maximum |
|
2832 height among the items. Note that to control the height of a row, |
|
2833 you must reimplement the QAbstractItemDelegate::sizeHint() |
|
2834 function. |
|
2835 |
|
2836 This function is used in views with a vertical header to find the |
|
2837 size hint for a header section based on the contents of the given |
|
2838 \a row. |
|
2839 |
|
2840 \sa sizeHintForColumn() |
|
2841 */ |
|
2842 int QAbstractItemView::sizeHintForRow(int row) const |
|
2843 { |
|
2844 Q_D(const QAbstractItemView); |
|
2845 |
|
2846 if (row < 0 || row >= d->model->rowCount() || !model()) |
|
2847 return -1; |
|
2848 |
|
2849 QStyleOptionViewItemV4 option = d->viewOptionsV4(); |
|
2850 int height = 0; |
|
2851 int colCount = d->model->columnCount(d->root); |
|
2852 QModelIndex index; |
|
2853 for (int c = 0; c < colCount; ++c) { |
|
2854 index = d->model->index(row, c, d->root); |
|
2855 if (QWidget *editor = d->editorForIndex(index).editor) |
|
2856 height = qMax(height, editor->size().height()); |
|
2857 int hint = d->delegateForIndex(index)->sizeHint(option, index).height(); |
|
2858 height = qMax(height, hint); |
|
2859 } |
|
2860 return height; |
|
2861 } |
|
2862 |
|
2863 /*! |
|
2864 Returns the width size hint for the specified \a column or -1 if there is no model. |
|
2865 |
|
2866 This function is used in views with a horizontal header to find the size hint for |
|
2867 a header section based on the contents of the given \a column. |
|
2868 |
|
2869 \sa sizeHintForRow() |
|
2870 */ |
|
2871 int QAbstractItemView::sizeHintForColumn(int column) const |
|
2872 { |
|
2873 Q_D(const QAbstractItemView); |
|
2874 |
|
2875 if (column < 0 || column >= d->model->columnCount() || !model()) |
|
2876 return -1; |
|
2877 |
|
2878 QStyleOptionViewItemV4 option = d->viewOptionsV4(); |
|
2879 int width = 0; |
|
2880 int rows = d->model->rowCount(d->root); |
|
2881 QModelIndex index; |
|
2882 for (int r = 0; r < rows; ++r) { |
|
2883 index = d->model->index(r, column, d->root); |
|
2884 if (QWidget *editor = d->editorForIndex(index).editor) |
|
2885 width = qMax(width, editor->sizeHint().width()); |
|
2886 int hint = d->delegateForIndex(index)->sizeHint(option, index).width(); |
|
2887 width = qMax(width, hint); |
|
2888 } |
|
2889 return width; |
|
2890 } |
|
2891 |
|
2892 /*! |
|
2893 Opens a persistent editor on the item at the given \a index. |
|
2894 If no editor exists, the delegate will create a new editor. |
|
2895 |
|
2896 \sa closePersistentEditor() |
|
2897 */ |
|
2898 void QAbstractItemView::openPersistentEditor(const QModelIndex &index) |
|
2899 { |
|
2900 Q_D(QAbstractItemView); |
|
2901 QStyleOptionViewItemV4 options = d->viewOptionsV4(); |
|
2902 options.rect = visualRect(index); |
|
2903 options.state |= (index == currentIndex() ? QStyle::State_HasFocus : QStyle::State_None); |
|
2904 |
|
2905 QWidget *editor = d->editor(index, options); |
|
2906 if (editor) { |
|
2907 editor->show(); |
|
2908 d->persistent.insert(editor); |
|
2909 } |
|
2910 } |
|
2911 |
|
2912 /*! |
|
2913 Closes the persistent editor for the item at the given \a index. |
|
2914 |
|
2915 \sa openPersistentEditor() |
|
2916 */ |
|
2917 void QAbstractItemView::closePersistentEditor(const QModelIndex &index) |
|
2918 { |
|
2919 Q_D(QAbstractItemView); |
|
2920 QWidget *editor = d->editorForIndex(index).editor; |
|
2921 if (editor) { |
|
2922 if (index == selectionModel()->currentIndex()) |
|
2923 closeEditor(editor, QAbstractItemDelegate::RevertModelCache); |
|
2924 d->persistent.remove(editor); |
|
2925 d->removeEditor(editor); |
|
2926 d->releaseEditor(editor); |
|
2927 } |
|
2928 } |
|
2929 |
|
2930 /*! |
|
2931 \since 4.1 |
|
2932 |
|
2933 Sets the given \a widget on the item at the given \a index, passing the |
|
2934 ownership of the widget to the viewport. |
|
2935 |
|
2936 If \a index is invalid (e.g., if you pass the root index), this function |
|
2937 will do nothing. |
|
2938 |
|
2939 The given \a widget's \l{QWidget}{autoFillBackground} property must be set |
|
2940 to true, otherwise the widget's background will be transparent, showing |
|
2941 both the model data and the item at the given \a index. |
|
2942 |
|
2943 If index widget A is replaced with index widget B, index widget A will be |
|
2944 deleted. For example, in the code snippet below, the QLineEdit object will |
|
2945 be deleted. |
|
2946 |
|
2947 \snippet doc/src/snippets/code/src_gui_itemviews_qabstractitemview.cpp 1 |
|
2948 |
|
2949 This function should only be used to display static content within the |
|
2950 visible area corresponding to an item of data. If you want to display |
|
2951 custom dynamic content or implement a custom editor widget, subclass |
|
2952 QItemDelegate instead. |
|
2953 |
|
2954 \sa {Delegate Classes} |
|
2955 */ |
|
2956 void QAbstractItemView::setIndexWidget(const QModelIndex &index, QWidget *widget) |
|
2957 { |
|
2958 Q_D(QAbstractItemView); |
|
2959 if (!d->isIndexValid(index)) |
|
2960 return; |
|
2961 if (QWidget *oldWidget = indexWidget(index)) { |
|
2962 d->removeEditor(oldWidget); |
|
2963 oldWidget->deleteLater(); |
|
2964 } |
|
2965 if (widget) { |
|
2966 widget->setParent(viewport()); |
|
2967 d->persistent.insert(widget); |
|
2968 d->addEditor(index, widget, true); |
|
2969 widget->show(); |
|
2970 dataChanged(index, index); // update the geometry |
|
2971 if (!d->delayedPendingLayout) |
|
2972 widget->setGeometry(visualRect(index)); |
|
2973 } |
|
2974 } |
|
2975 |
|
2976 /*! |
|
2977 \since 4.1 |
|
2978 |
|
2979 Returns the widget for the item at the given \a index. |
|
2980 */ |
|
2981 QWidget* QAbstractItemView::indexWidget(const QModelIndex &index) const |
|
2982 { |
|
2983 Q_D(const QAbstractItemView); |
|
2984 if (!d->isIndexValid(index)) |
|
2985 return 0; |
|
2986 return d->editorForIndex(index).editor; |
|
2987 } |
|
2988 |
|
2989 /*! |
|
2990 \since 4.1 |
|
2991 |
|
2992 Scrolls the view to the top. |
|
2993 |
|
2994 \sa scrollTo(), scrollToBottom() |
|
2995 */ |
|
2996 void QAbstractItemView::scrollToTop() |
|
2997 { |
|
2998 verticalScrollBar()->setValue(verticalScrollBar()->minimum()); |
|
2999 } |
|
3000 |
|
3001 /*! |
|
3002 \since 4.1 |
|
3003 |
|
3004 Scrolls the view to the bottom. |
|
3005 |
|
3006 \sa scrollTo(), scrollToTop() |
|
3007 */ |
|
3008 void QAbstractItemView::scrollToBottom() |
|
3009 { |
|
3010 Q_D(QAbstractItemView); |
|
3011 if (d->delayedPendingLayout) { |
|
3012 d->executePostedLayout(); |
|
3013 updateGeometries(); |
|
3014 } |
|
3015 verticalScrollBar()->setValue(verticalScrollBar()->maximum()); |
|
3016 } |
|
3017 |
|
3018 /*! |
|
3019 \since 4.3 |
|
3020 |
|
3021 Updates the area occupied by the given \a index. |
|
3022 |
|
3023 */ |
|
3024 void QAbstractItemView::update(const QModelIndex &index) |
|
3025 { |
|
3026 Q_D(QAbstractItemView); |
|
3027 if (index.isValid()) { |
|
3028 const QRect rect = visualRect(index); |
|
3029 //this test is important for peformance reason |
|
3030 //For example in dataChanged we simply update all the cells without checking |
|
3031 //it can be a major bottleneck to update rects that aren't even part of the viewport |
|
3032 if (d->viewport->rect().intersects(rect)) |
|
3033 d->viewport->update(rect); |
|
3034 } |
|
3035 } |
|
3036 |
|
3037 /*! |
|
3038 This slot is called when items are changed in the model. The |
|
3039 changed items are those from \a topLeft to \a bottomRight |
|
3040 inclusive. If just one item is changed \a topLeft == \a |
|
3041 bottomRight. |
|
3042 */ |
|
3043 void QAbstractItemView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) |
|
3044 { |
|
3045 // Single item changed |
|
3046 Q_D(QAbstractItemView); |
|
3047 if (topLeft == bottomRight && topLeft.isValid()) { |
|
3048 const QEditorInfo editorInfo = d->editorForIndex(topLeft); |
|
3049 //we don't update the edit data if it is static |
|
3050 if (!editorInfo.isStatic && editorInfo.editor) { |
|
3051 QAbstractItemDelegate *delegate = d->delegateForIndex(topLeft); |
|
3052 if (delegate) { |
|
3053 delegate->setEditorData(editorInfo.editor, topLeft); |
|
3054 } |
|
3055 } |
|
3056 if (isVisible() && !d->delayedPendingLayout) { |
|
3057 // otherwise the items will be update later anyway |
|
3058 update(topLeft); |
|
3059 } |
|
3060 return; |
|
3061 } |
|
3062 d->updateEditorData(topLeft, bottomRight); |
|
3063 if (!isVisible() || d->delayedPendingLayout) |
|
3064 return; // no need to update |
|
3065 d->viewport->update(); |
|
3066 } |
|
3067 |
|
3068 /*! |
|
3069 This slot is called when rows are inserted. The new rows are those |
|
3070 under the given \a parent from \a start to \a end inclusive. The |
|
3071 base class implementation calls fetchMore() on the model to check |
|
3072 for more data. |
|
3073 |
|
3074 \sa rowsAboutToBeRemoved() |
|
3075 */ |
|
3076 void QAbstractItemView::rowsInserted(const QModelIndex &, int, int) |
|
3077 { |
|
3078 if (!isVisible()) |
|
3079 d_func()->fetchMoreTimer.start(0, this); //fetch more later |
|
3080 else |
|
3081 updateEditorGeometries(); |
|
3082 } |
|
3083 |
|
3084 /*! |
|
3085 This slot is called when rows are about to be removed. The deleted rows are |
|
3086 those under the given \a parent from \a start to \a end inclusive. |
|
3087 |
|
3088 \sa rowsInserted() |
|
3089 */ |
|
3090 void QAbstractItemView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) |
|
3091 { |
|
3092 Q_D(QAbstractItemView); |
|
3093 |
|
3094 setState(CollapsingState); |
|
3095 |
|
3096 // Ensure one selected item in single selection mode. |
|
3097 QModelIndex current = currentIndex(); |
|
3098 if (d->selectionMode == SingleSelection |
|
3099 && current.isValid() |
|
3100 && current.row() >= start |
|
3101 && current.row() <= end |
|
3102 && current.parent() == parent) { |
|
3103 int totalToRemove = end - start + 1; |
|
3104 if (d->model->rowCount(parent) <= totalToRemove) { // no more children |
|
3105 QModelIndex index = parent; |
|
3106 while (index != d->root && !d->isIndexEnabled(index)) |
|
3107 index = index.parent(); |
|
3108 if (index != d->root) |
|
3109 setCurrentIndex(index); |
|
3110 } else { |
|
3111 int row = end + 1; |
|
3112 QModelIndex next; |
|
3113 do { // find the next visible and enabled item |
|
3114 next = d->model->index(row++, current.column(), current.parent()); |
|
3115 } while (next.isValid() && (isIndexHidden(next) || !d->isIndexEnabled(next))); |
|
3116 if (row > d->model->rowCount(parent)) { |
|
3117 row = start - 1; |
|
3118 do { // find the previous visible and enabled item |
|
3119 next = d->model->index(row--, current.column(), current.parent()); |
|
3120 } while (next.isValid() && (isIndexHidden(next) || !d->isIndexEnabled(next))); |
|
3121 } |
|
3122 setCurrentIndex(next); |
|
3123 } |
|
3124 } |
|
3125 |
|
3126 // Remove all affected editors; this is more efficient than waiting for updateGeometries() to clean out editors for invalid indexes |
|
3127 for (int i = d->editors.size() - 1; i >= 0; --i) { |
|
3128 const QModelIndex index = d->editors.at(i).index; |
|
3129 QWidget *editor = d->editors.at(i).editor; |
|
3130 if (index.row() >= start && index.row() <= end && d->model->parent(index) == parent) { |
|
3131 d->editors.removeAt(i); |
|
3132 d->releaseEditor(editor); |
|
3133 } |
|
3134 } |
|
3135 } |
|
3136 |
|
3137 /*! |
|
3138 \internal |
|
3139 |
|
3140 This slot is called when rows have been removed. The deleted |
|
3141 rows are those under the given \a parent from \a start to \a end |
|
3142 inclusive. |
|
3143 */ |
|
3144 void QAbstractItemViewPrivate::_q_rowsRemoved(const QModelIndex &, int, int) |
|
3145 { |
|
3146 Q_Q(QAbstractItemView); |
|
3147 if (q->isVisible()) |
|
3148 q->updateEditorGeometries(); |
|
3149 q->setState(QAbstractItemView::NoState); |
|
3150 } |
|
3151 |
|
3152 /*! |
|
3153 \internal |
|
3154 |
|
3155 This slot is called when columns are about to be removed. The deleted |
|
3156 columns are those under the given \a parent from \a start to \a end |
|
3157 inclusive. |
|
3158 */ |
|
3159 void QAbstractItemViewPrivate::_q_columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end) |
|
3160 { |
|
3161 Q_Q(QAbstractItemView); |
|
3162 |
|
3163 q->setState(QAbstractItemView::CollapsingState); |
|
3164 |
|
3165 // Ensure one selected item in single selection mode. |
|
3166 QModelIndex current = q->currentIndex(); |
|
3167 if (current.isValid() |
|
3168 && selectionMode == QAbstractItemView::SingleSelection |
|
3169 && current.column() >= start |
|
3170 && current.column() <= end) { |
|
3171 int totalToRemove = end - start + 1; |
|
3172 if (model->columnCount(parent) < totalToRemove) { // no more columns |
|
3173 QModelIndex index = parent; |
|
3174 while (index.isValid() && !isIndexEnabled(index)) |
|
3175 index = index.parent(); |
|
3176 if (index.isValid()) |
|
3177 q->setCurrentIndex(index); |
|
3178 } else { |
|
3179 int column = end; |
|
3180 QModelIndex next; |
|
3181 do { // find the next visible and enabled item |
|
3182 next = model->index(current.row(), column++, current.parent()); |
|
3183 } while (next.isValid() && (q->isIndexHidden(next) || !isIndexEnabled(next))); |
|
3184 q->setCurrentIndex(next); |
|
3185 } |
|
3186 } |
|
3187 |
|
3188 // Remove all affected editors; this is more efficient than waiting for updateGeometries() to clean out editors for invalid indexes |
|
3189 QList<QEditorInfo>::iterator it = editors.begin(); |
|
3190 while (it != editors.end()) { |
|
3191 QModelIndex index = it->index; |
|
3192 if (index.column() <= start && index.column() >= end && model->parent(index) == parent) { |
|
3193 QWidget *editor = it->editor; |
|
3194 it = editors.erase(it); |
|
3195 releaseEditor(editor); |
|
3196 } else { |
|
3197 ++it; |
|
3198 } |
|
3199 } |
|
3200 } |
|
3201 |
|
3202 /*! |
|
3203 \internal |
|
3204 |
|
3205 This slot is called when columns have been removed. The deleted |
|
3206 rows are those under the given \a parent from \a start to \a end |
|
3207 inclusive. |
|
3208 */ |
|
3209 void QAbstractItemViewPrivate::_q_columnsRemoved(const QModelIndex &, int, int) |
|
3210 { |
|
3211 Q_Q(QAbstractItemView); |
|
3212 if (q->isVisible()) |
|
3213 q->updateEditorGeometries(); |
|
3214 q->setState(QAbstractItemView::NoState); |
|
3215 } |
|
3216 |
|
3217 /*! |
|
3218 \internal |
|
3219 |
|
3220 This slot is called when rows have been inserted. |
|
3221 */ |
|
3222 void QAbstractItemViewPrivate::_q_columnsInserted(const QModelIndex &, int, int) |
|
3223 { |
|
3224 Q_Q(QAbstractItemView); |
|
3225 if (q->isVisible()) |
|
3226 q->updateEditorGeometries(); |
|
3227 } |
|
3228 |
|
3229 |
|
3230 |
|
3231 /*! |
|
3232 \internal |
|
3233 */ |
|
3234 void QAbstractItemViewPrivate::_q_modelDestroyed() |
|
3235 { |
|
3236 model = QAbstractItemModelPrivate::staticEmptyModel(); |
|
3237 doDelayedReset(); |
|
3238 } |
|
3239 |
|
3240 /*! |
|
3241 \internal |
|
3242 |
|
3243 This slot is called when the layout is changed. |
|
3244 */ |
|
3245 void QAbstractItemViewPrivate::_q_layoutChanged() |
|
3246 { |
|
3247 doDelayedItemsLayout(); |
|
3248 } |
|
3249 |
|
3250 /*! |
|
3251 This slot is called when the selection is changed. The previous |
|
3252 selection (which may be empty), is specified by \a deselected, and the |
|
3253 new selection by \a selected. |
|
3254 |
|
3255 \sa setSelection() |
|
3256 */ |
|
3257 void QAbstractItemView::selectionChanged(const QItemSelection &selected, |
|
3258 const QItemSelection &deselected) |
|
3259 { |
|
3260 Q_D(QAbstractItemView); |
|
3261 if (isVisible() && updatesEnabled()) { |
|
3262 d->viewport->update(visualRegionForSelection(deselected) | visualRegionForSelection(selected)); |
|
3263 } |
|
3264 } |
|
3265 |
|
3266 /*! |
|
3267 This slot is called when a new item becomes the current item. |
|
3268 The previous current item is specified by the \a previous index, and the new |
|
3269 item by the \a current index. |
|
3270 |
|
3271 If you want to know about changes to items see the |
|
3272 dataChanged() signal. |
|
3273 */ |
|
3274 void QAbstractItemView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) |
|
3275 { |
|
3276 Q_D(QAbstractItemView); |
|
3277 Q_ASSERT(d->model); |
|
3278 |
|
3279 if (previous.isValid()) { |
|
3280 QModelIndex buddy = d->model->buddy(previous); |
|
3281 QWidget *editor = d->editorForIndex(buddy).editor; |
|
3282 if (editor && !d->persistent.contains(editor)) { |
|
3283 commitData(editor); |
|
3284 if (current.row() != previous.row()) |
|
3285 closeEditor(editor, QAbstractItemDelegate::SubmitModelCache); |
|
3286 else |
|
3287 closeEditor(editor, QAbstractItemDelegate::NoHint); |
|
3288 } |
|
3289 if (isVisible()) { |
|
3290 update(previous); |
|
3291 } |
|
3292 } |
|
3293 |
|
3294 if (current.isValid() && !d->autoScrollTimer.isActive()) { |
|
3295 if (isVisible()) { |
|
3296 if (d->autoScroll) |
|
3297 scrollTo(current); |
|
3298 update(current); |
|
3299 edit(current, CurrentChanged, 0); |
|
3300 if (current.row() == (d->model->rowCount(d->root) - 1)) |
|
3301 d->fetchMore(); |
|
3302 } else { |
|
3303 d->shouldScrollToCurrentOnShow = d->autoScroll; |
|
3304 } |
|
3305 } |
|
3306 } |
|
3307 |
|
3308 #ifndef QT_NO_DRAGANDDROP |
|
3309 /*! |
|
3310 Starts a drag by calling drag->exec() using the given \a supportedActions. |
|
3311 */ |
|
3312 void QAbstractItemView::startDrag(Qt::DropActions supportedActions) |
|
3313 { |
|
3314 Q_D(QAbstractItemView); |
|
3315 QModelIndexList indexes = d->selectedDraggableIndexes(); |
|
3316 if (indexes.count() > 0) { |
|
3317 QMimeData *data = d->model->mimeData(indexes); |
|
3318 if (!data) |
|
3319 return; |
|
3320 QRect rect; |
|
3321 QPixmap pixmap = d->renderToPixmap(indexes, &rect); |
|
3322 rect.adjust(horizontalOffset(), verticalOffset(), 0, 0); |
|
3323 QDrag *drag = new QDrag(this); |
|
3324 drag->setPixmap(pixmap); |
|
3325 drag->setMimeData(data); |
|
3326 drag->setHotSpot(d->pressedPosition - rect.topLeft()); |
|
3327 Qt::DropAction defaultDropAction = Qt::IgnoreAction; |
|
3328 if (d->defaultDropAction != Qt::IgnoreAction && (supportedActions & d->defaultDropAction)) |
|
3329 defaultDropAction = d->defaultDropAction; |
|
3330 else if (supportedActions & Qt::CopyAction && dragDropMode() != QAbstractItemView::InternalMove) |
|
3331 defaultDropAction = Qt::CopyAction; |
|
3332 if (drag->exec(supportedActions, defaultDropAction) == Qt::MoveAction) |
|
3333 d->clearOrRemove(); |
|
3334 } |
|
3335 } |
|
3336 #endif // QT_NO_DRAGANDDROP |
|
3337 |
|
3338 /*! |
|
3339 Returns a QStyleOptionViewItem structure populated with the view's |
|
3340 palette, font, state, alignments etc. |
|
3341 */ |
|
3342 QStyleOptionViewItem QAbstractItemView::viewOptions() const |
|
3343 { |
|
3344 Q_D(const QAbstractItemView); |
|
3345 QStyleOptionViewItem option; |
|
3346 option.init(this); |
|
3347 option.state &= ~QStyle::State_MouseOver; |
|
3348 option.font = font(); |
|
3349 |
|
3350 #ifndef Q_WS_MAC |
|
3351 // On mac the focus appearance follows window activation |
|
3352 // not widget activation |
|
3353 if (!hasFocus()) |
|
3354 option.state &= ~QStyle::State_Active; |
|
3355 #endif |
|
3356 |
|
3357 option.state &= ~QStyle::State_HasFocus; |
|
3358 if (d->iconSize.isValid()) { |
|
3359 option.decorationSize = d->iconSize; |
|
3360 } else { |
|
3361 int pm = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this); |
|
3362 option.decorationSize = QSize(pm, pm); |
|
3363 } |
|
3364 option.decorationPosition = QStyleOptionViewItem::Left; |
|
3365 option.decorationAlignment = Qt::AlignCenter; |
|
3366 option.displayAlignment = Qt::AlignLeft|Qt::AlignVCenter; |
|
3367 option.textElideMode = d->textElideMode; |
|
3368 option.rect = QRect(); |
|
3369 option.showDecorationSelected = style()->styleHint(QStyle::SH_ItemView_ShowDecorationSelected, 0, this); |
|
3370 return option; |
|
3371 } |
|
3372 |
|
3373 QStyleOptionViewItemV4 QAbstractItemViewPrivate::viewOptionsV4() const |
|
3374 { |
|
3375 Q_Q(const QAbstractItemView); |
|
3376 QStyleOptionViewItemV4 option = q->viewOptions(); |
|
3377 if (wrapItemText) |
|
3378 option.features = QStyleOptionViewItemV2::WrapText; |
|
3379 option.locale = q->locale(); |
|
3380 option.locale.setNumberOptions(QLocale::OmitGroupSeparator); |
|
3381 option.widget = q; |
|
3382 return option; |
|
3383 } |
|
3384 |
|
3385 /*! |
|
3386 Returns the item view's state. |
|
3387 |
|
3388 \sa setState() |
|
3389 */ |
|
3390 QAbstractItemView::State QAbstractItemView::state() const |
|
3391 { |
|
3392 Q_D(const QAbstractItemView); |
|
3393 return d->state; |
|
3394 } |
|
3395 |
|
3396 /*! |
|
3397 Sets the item view's state to the given \a state. |
|
3398 |
|
3399 \sa state() |
|
3400 */ |
|
3401 void QAbstractItemView::setState(State state) |
|
3402 { |
|
3403 Q_D(QAbstractItemView); |
|
3404 d->state = state; |
|
3405 } |
|
3406 |
|
3407 /*! |
|
3408 Schedules a layout of the items in the view to be executed when the |
|
3409 event processing starts. |
|
3410 |
|
3411 Even if scheduleDelayedItemsLayout() is called multiple times before |
|
3412 events are processed, the view will only do the layout once. |
|
3413 |
|
3414 \sa executeDelayedItemsLayout() |
|
3415 */ |
|
3416 void QAbstractItemView::scheduleDelayedItemsLayout() |
|
3417 { |
|
3418 Q_D(QAbstractItemView); |
|
3419 d->doDelayedItemsLayout(); |
|
3420 } |
|
3421 |
|
3422 /*! |
|
3423 Executes the scheduled layouts without waiting for the event processing |
|
3424 to begin. |
|
3425 |
|
3426 \sa scheduleDelayedItemsLayout() |
|
3427 */ |
|
3428 void QAbstractItemView::executeDelayedItemsLayout() |
|
3429 { |
|
3430 Q_D(QAbstractItemView); |
|
3431 d->executePostedLayout(); |
|
3432 } |
|
3433 |
|
3434 /*! |
|
3435 \since 4.1 |
|
3436 |
|
3437 Marks the given \a region as dirty and schedules it to be updated. |
|
3438 You only need to call this function if you are implementing |
|
3439 your own view subclass. |
|
3440 |
|
3441 \sa scrollDirtyRegion(), dirtyRegionOffset() |
|
3442 */ |
|
3443 |
|
3444 void QAbstractItemView::setDirtyRegion(const QRegion ®ion) |
|
3445 { |
|
3446 Q_D(QAbstractItemView); |
|
3447 d->setDirtyRegion(region); |
|
3448 } |
|
3449 |
|
3450 /*! |
|
3451 Prepares the view for scrolling by (\a{dx},\a{dy}) pixels by moving the dirty regions in the |
|
3452 opposite direction. You only need to call this function if you are implementing a scrolling |
|
3453 viewport in your view subclass. |
|
3454 |
|
3455 If you implement scrollContentsBy() in a subclass of QAbstractItemView, call this function |
|
3456 before you call QWidget::scroll() on the viewport. Alternatively, just call update(). |
|
3457 |
|
3458 \sa scrollContentsBy(), dirtyRegionOffset(), setDirtyRegion() |
|
3459 */ |
|
3460 void QAbstractItemView::scrollDirtyRegion(int dx, int dy) |
|
3461 { |
|
3462 Q_D(QAbstractItemView); |
|
3463 d->scrollDirtyRegion(dx, dy); |
|
3464 } |
|
3465 |
|
3466 /*! |
|
3467 Returns the offset of the dirty regions in the view. |
|
3468 |
|
3469 If you use scrollDirtyRegion() and implement a paintEvent() in a subclass of |
|
3470 QAbstractItemView, you should translate the area given by the paint event with |
|
3471 the offset returned from this function. |
|
3472 |
|
3473 \sa scrollDirtyRegion(), setDirtyRegion() |
|
3474 */ |
|
3475 QPoint QAbstractItemView::dirtyRegionOffset() const |
|
3476 { |
|
3477 Q_D(const QAbstractItemView); |
|
3478 return d->scrollDelayOffset; |
|
3479 } |
|
3480 |
|
3481 /*! |
|
3482 \internal |
|
3483 */ |
|
3484 void QAbstractItemView::startAutoScroll() |
|
3485 { |
|
3486 d_func()->startAutoScroll(); |
|
3487 } |
|
3488 |
|
3489 /*! |
|
3490 \internal |
|
3491 */ |
|
3492 void QAbstractItemView::stopAutoScroll() |
|
3493 { |
|
3494 d_func()->stopAutoScroll(); |
|
3495 } |
|
3496 |
|
3497 /*! |
|
3498 \internal |
|
3499 */ |
|
3500 void QAbstractItemView::doAutoScroll() |
|
3501 { |
|
3502 // find how much we should scroll with |
|
3503 Q_D(QAbstractItemView); |
|
3504 int verticalStep = verticalScrollBar()->pageStep(); |
|
3505 int horizontalStep = horizontalScrollBar()->pageStep(); |
|
3506 if (d->autoScrollCount < qMax(verticalStep, horizontalStep)) |
|
3507 ++d->autoScrollCount; |
|
3508 |
|
3509 int margin = d->autoScrollMargin; |
|
3510 int verticalValue = verticalScrollBar()->value(); |
|
3511 int horizontalValue = horizontalScrollBar()->value(); |
|
3512 |
|
3513 QPoint pos = d->viewport->mapFromGlobal(QCursor::pos()); |
|
3514 QRect area = static_cast<QAbstractItemView*>(d->viewport)->d_func()->clipRect(); // access QWidget private by bending C++ rules |
|
3515 |
|
3516 // do the scrolling if we are in the scroll margins |
|
3517 if (pos.y() - area.top() < margin) |
|
3518 verticalScrollBar()->setValue(verticalValue - d->autoScrollCount); |
|
3519 else if (area.bottom() - pos.y() < margin) |
|
3520 verticalScrollBar()->setValue(verticalValue + d->autoScrollCount); |
|
3521 if (pos.x() - area.left() < margin) |
|
3522 horizontalScrollBar()->setValue(horizontalValue - d->autoScrollCount); |
|
3523 else if (area.right() - pos.x() < margin) |
|
3524 horizontalScrollBar()->setValue(horizontalValue + d->autoScrollCount); |
|
3525 // if nothing changed, stop scrolling |
|
3526 bool verticalUnchanged = (verticalValue == verticalScrollBar()->value()); |
|
3527 bool horizontalUnchanged = (horizontalValue == horizontalScrollBar()->value()); |
|
3528 if (verticalUnchanged && horizontalUnchanged) { |
|
3529 stopAutoScroll(); |
|
3530 } else { |
|
3531 #ifndef QT_NO_DRAGANDDROP |
|
3532 d->dropIndicatorRect = QRect(); |
|
3533 d->dropIndicatorPosition = QAbstractItemView::OnViewport; |
|
3534 #endif |
|
3535 d->viewport->update(); |
|
3536 } |
|
3537 } |
|
3538 |
|
3539 /*! |
|
3540 Returns the SelectionFlags to be used when updating a selection with |
|
3541 to include the \a index specified. The \a event is a user input event, |
|
3542 such as a mouse or keyboard event. |
|
3543 |
|
3544 Reimplement this function to define your own selection behavior. |
|
3545 |
|
3546 \sa setSelection() |
|
3547 */ |
|
3548 QItemSelectionModel::SelectionFlags QAbstractItemView::selectionCommand(const QModelIndex &index, |
|
3549 const QEvent *event) const |
|
3550 { |
|
3551 Q_D(const QAbstractItemView); |
|
3552 switch (d->selectionMode) { |
|
3553 case NoSelection: // Never update selection model |
|
3554 return QItemSelectionModel::NoUpdate; |
|
3555 case SingleSelection: // ClearAndSelect on valid index otherwise NoUpdate |
|
3556 if (event && event->type() == QEvent::MouseButtonRelease) |
|
3557 return QItemSelectionModel::NoUpdate; |
|
3558 return QItemSelectionModel::ClearAndSelect|d->selectionBehaviorFlags(); |
|
3559 case MultiSelection: |
|
3560 return d->multiSelectionCommand(index, event); |
|
3561 case ExtendedSelection: |
|
3562 return d->extendedSelectionCommand(index, event); |
|
3563 case ContiguousSelection: |
|
3564 return d->contiguousSelectionCommand(index, event); |
|
3565 } |
|
3566 return QItemSelectionModel::NoUpdate; |
|
3567 } |
|
3568 |
|
3569 QItemSelectionModel::SelectionFlags QAbstractItemViewPrivate::multiSelectionCommand( |
|
3570 const QModelIndex &index, const QEvent *event) const |
|
3571 { |
|
3572 Q_UNUSED(index); |
|
3573 |
|
3574 if (event) { |
|
3575 switch (event->type()) { |
|
3576 case QEvent::KeyPress: |
|
3577 if (static_cast<const QKeyEvent*>(event)->key() == Qt::Key_Space |
|
3578 || static_cast<const QKeyEvent*>(event)->key() == Qt::Key_Select) |
|
3579 return QItemSelectionModel::Toggle|selectionBehaviorFlags(); |
|
3580 break; |
|
3581 case QEvent::MouseButtonPress: |
|
3582 if (static_cast<const QMouseEvent*>(event)->button() == Qt::LeftButton) |
|
3583 return QItemSelectionModel::Toggle|selectionBehaviorFlags(); // toggle |
|
3584 break; |
|
3585 case QEvent::MouseButtonRelease: |
|
3586 if (static_cast<const QMouseEvent*>(event)->button() == Qt::LeftButton) |
|
3587 return QItemSelectionModel::NoUpdate|selectionBehaviorFlags(); // finalize |
|
3588 break; |
|
3589 case QEvent::MouseMove: |
|
3590 if (static_cast<const QMouseEvent*>(event)->buttons() & Qt::LeftButton) |
|
3591 return QItemSelectionModel::ToggleCurrent|selectionBehaviorFlags(); // toggle drag select |
|
3592 default: |
|
3593 break; |
|
3594 } |
|
3595 return QItemSelectionModel::NoUpdate; |
|
3596 } |
|
3597 |
|
3598 return QItemSelectionModel::Toggle|selectionBehaviorFlags(); |
|
3599 } |
|
3600 |
|
3601 QItemSelectionModel::SelectionFlags QAbstractItemViewPrivate::extendedSelectionCommand( |
|
3602 const QModelIndex &index, const QEvent *event) const |
|
3603 { |
|
3604 Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers(); |
|
3605 if (event) { |
|
3606 switch (event->type()) { |
|
3607 case QEvent::MouseMove: { |
|
3608 // Toggle on MouseMove |
|
3609 modifiers = static_cast<const QMouseEvent*>(event)->modifiers(); |
|
3610 if (modifiers & Qt::ControlModifier) |
|
3611 return QItemSelectionModel::ToggleCurrent|selectionBehaviorFlags(); |
|
3612 break; |
|
3613 } |
|
3614 case QEvent::MouseButtonPress: { |
|
3615 modifiers = static_cast<const QMouseEvent*>(event)->modifiers(); |
|
3616 const Qt::MouseButton button = static_cast<const QMouseEvent*>(event)->button(); |
|
3617 const bool rightButtonPressed = button & Qt::RightButton; |
|
3618 const bool shiftKeyPressed = modifiers & Qt::ShiftModifier; |
|
3619 const bool controlKeyPressed = modifiers & Qt::ControlModifier; |
|
3620 const bool indexIsSelected = selectionModel->isSelected(index); |
|
3621 if ((shiftKeyPressed || controlKeyPressed) && rightButtonPressed) |
|
3622 return QItemSelectionModel::NoUpdate; |
|
3623 if (!shiftKeyPressed && !controlKeyPressed && indexIsSelected) |
|
3624 return QItemSelectionModel::NoUpdate; |
|
3625 if (!index.isValid() && !rightButtonPressed && !shiftKeyPressed && !controlKeyPressed) |
|
3626 return QItemSelectionModel::Clear; |
|
3627 if (!index.isValid()) |
|
3628 return QItemSelectionModel::NoUpdate; |
|
3629 break; |
|
3630 } |
|
3631 case QEvent::MouseButtonRelease: { |
|
3632 // ClearAndSelect on MouseButtonRelease if MouseButtonPress on selected item or empty area |
|
3633 modifiers = static_cast<const QMouseEvent*>(event)->modifiers(); |
|
3634 const Qt::MouseButton button = static_cast<const QMouseEvent*>(event)->button(); |
|
3635 const bool rightButtonPressed = button & Qt::RightButton; |
|
3636 const bool shiftKeyPressed = modifiers & Qt::ShiftModifier; |
|
3637 const bool controlKeyPressed = modifiers & Qt::ControlModifier; |
|
3638 if (((index == pressedIndex && selectionModel->isSelected(index)) |
|
3639 || !index.isValid()) && state != QAbstractItemView::DragSelectingState |
|
3640 && !shiftKeyPressed && !controlKeyPressed && !rightButtonPressed) |
|
3641 return QItemSelectionModel::ClearAndSelect|selectionBehaviorFlags(); |
|
3642 return QItemSelectionModel::NoUpdate; |
|
3643 } |
|
3644 case QEvent::KeyPress: { |
|
3645 // NoUpdate on Key movement and Ctrl |
|
3646 modifiers = static_cast<const QKeyEvent*>(event)->modifiers(); |
|
3647 switch (static_cast<const QKeyEvent*>(event)->key()) { |
|
3648 case Qt::Key_Backtab: |
|
3649 modifiers = modifiers & ~Qt::ShiftModifier; // special case for backtab |
|
3650 case Qt::Key_Down: |
|
3651 case Qt::Key_Up: |
|
3652 case Qt::Key_Left: |
|
3653 case Qt::Key_Right: |
|
3654 case Qt::Key_Home: |
|
3655 case Qt::Key_End: |
|
3656 case Qt::Key_PageUp: |
|
3657 case Qt::Key_PageDown: |
|
3658 case Qt::Key_Tab: |
|
3659 if (modifiers & Qt::ControlModifier |
|
3660 #ifdef QT_KEYPAD_NAVIGATION |
|
3661 // Preserve historical tab order navigation behavior |
|
3662 || QApplication::navigationMode() == Qt::NavigationModeKeypadTabOrder |
|
3663 #endif |
|
3664 ) |
|
3665 return QItemSelectionModel::NoUpdate; |
|
3666 break; |
|
3667 case Qt::Key_Select: |
|
3668 return QItemSelectionModel::Toggle|selectionBehaviorFlags(); |
|
3669 case Qt::Key_Space:// Toggle on Ctrl-Qt::Key_Space, Select on Space |
|
3670 if (modifiers & Qt::ControlModifier) |
|
3671 return QItemSelectionModel::Toggle|selectionBehaviorFlags(); |
|
3672 return QItemSelectionModel::Select|selectionBehaviorFlags(); |
|
3673 default: |
|
3674 break; |
|
3675 } |
|
3676 } |
|
3677 default: |
|
3678 break; |
|
3679 } |
|
3680 } |
|
3681 |
|
3682 if (modifiers & Qt::ShiftModifier) |
|
3683 return QItemSelectionModel::SelectCurrent|selectionBehaviorFlags(); |
|
3684 if (modifiers & Qt::ControlModifier) |
|
3685 return QItemSelectionModel::Toggle|selectionBehaviorFlags(); |
|
3686 if (state == QAbstractItemView::DragSelectingState) { |
|
3687 //when drag-selecting we need to clear any previous selection and select the current one |
|
3688 return QItemSelectionModel::Clear|QItemSelectionModel::SelectCurrent|selectionBehaviorFlags(); |
|
3689 } |
|
3690 |
|
3691 return QItemSelectionModel::ClearAndSelect|selectionBehaviorFlags(); |
|
3692 } |
|
3693 |
|
3694 QItemSelectionModel::SelectionFlags |
|
3695 QAbstractItemViewPrivate::contiguousSelectionCommand(const QModelIndex &index, |
|
3696 const QEvent *event) const |
|
3697 { |
|
3698 QItemSelectionModel::SelectionFlags flags = extendedSelectionCommand(index, event); |
|
3699 const int Mask = QItemSelectionModel::Clear | QItemSelectionModel::Select |
|
3700 | QItemSelectionModel::Deselect | QItemSelectionModel::Toggle |
|
3701 | QItemSelectionModel::Current; |
|
3702 |
|
3703 switch (flags & Mask) { |
|
3704 case QItemSelectionModel::Clear: |
|
3705 case QItemSelectionModel::ClearAndSelect: |
|
3706 case QItemSelectionModel::SelectCurrent: |
|
3707 return flags; |
|
3708 case QItemSelectionModel::NoUpdate: |
|
3709 if (event && |
|
3710 (event->type() == QEvent::MouseButtonPress |
|
3711 || event->type() == QEvent::MouseButtonRelease)) |
|
3712 return flags; |
|
3713 return QItemSelectionModel::ClearAndSelect|selectionBehaviorFlags(); |
|
3714 default: |
|
3715 return QItemSelectionModel::SelectCurrent|selectionBehaviorFlags(); |
|
3716 } |
|
3717 } |
|
3718 |
|
3719 void QAbstractItemViewPrivate::fetchMore() |
|
3720 { |
|
3721 fetchMoreTimer.stop(); |
|
3722 if (!model->canFetchMore(root)) |
|
3723 return; |
|
3724 int last = model->rowCount(root) - 1; |
|
3725 if (last < 0) { |
|
3726 model->fetchMore(root); |
|
3727 return; |
|
3728 } |
|
3729 |
|
3730 QModelIndex index = model->index(last, 0, root); |
|
3731 QRect rect = q_func()->visualRect(index); |
|
3732 if (viewport->rect().intersects(rect)) |
|
3733 model->fetchMore(root); |
|
3734 } |
|
3735 |
|
3736 bool QAbstractItemViewPrivate::shouldEdit(QAbstractItemView::EditTrigger trigger, |
|
3737 const QModelIndex &index) const |
|
3738 { |
|
3739 if (!index.isValid()) |
|
3740 return false; |
|
3741 Qt::ItemFlags flags = model->flags(index); |
|
3742 if (((flags & Qt::ItemIsEditable) == 0) || ((flags & Qt::ItemIsEnabled) == 0)) |
|
3743 return false; |
|
3744 if (state == QAbstractItemView::EditingState) |
|
3745 return false; |
|
3746 if (hasEditor(index)) |
|
3747 return false; |
|
3748 if (trigger == QAbstractItemView::AllEditTriggers) // force editing |
|
3749 return true; |
|
3750 if ((trigger & editTriggers) == QAbstractItemView::SelectedClicked |
|
3751 && !selectionModel->isSelected(index)) |
|
3752 return false; |
|
3753 return (trigger & editTriggers); |
|
3754 } |
|
3755 |
|
3756 bool QAbstractItemViewPrivate::shouldForwardEvent(QAbstractItemView::EditTrigger trigger, |
|
3757 const QEvent *event) const |
|
3758 { |
|
3759 if (!event || (trigger & editTriggers) != QAbstractItemView::AnyKeyPressed) |
|
3760 return false; |
|
3761 |
|
3762 switch (event->type()) { |
|
3763 case QEvent::KeyPress: |
|
3764 case QEvent::MouseButtonDblClick: |
|
3765 case QEvent::MouseButtonPress: |
|
3766 case QEvent::MouseButtonRelease: |
|
3767 case QEvent::MouseMove: |
|
3768 return true; |
|
3769 default: |
|
3770 break; |
|
3771 }; |
|
3772 |
|
3773 return false; |
|
3774 } |
|
3775 |
|
3776 bool QAbstractItemViewPrivate::shouldAutoScroll(const QPoint &pos) const |
|
3777 { |
|
3778 if (!autoScroll) |
|
3779 return false; |
|
3780 QRect area = static_cast<QAbstractItemView*>(viewport)->d_func()->clipRect(); // access QWidget private by bending C++ rules |
|
3781 return (pos.y() - area.top() < autoScrollMargin) |
|
3782 || (area.bottom() - pos.y() < autoScrollMargin) |
|
3783 || (pos.x() - area.left() < autoScrollMargin) |
|
3784 || (area.right() - pos.x() < autoScrollMargin); |
|
3785 } |
|
3786 |
|
3787 void QAbstractItemViewPrivate::doDelayedItemsLayout(int delay) |
|
3788 { |
|
3789 if (!delayedPendingLayout) { |
|
3790 delayedPendingLayout = true; |
|
3791 delayedLayout.start(delay, q_func()); |
|
3792 } |
|
3793 } |
|
3794 |
|
3795 void QAbstractItemViewPrivate::interruptDelayedItemsLayout() const |
|
3796 { |
|
3797 delayedLayout.stop(); |
|
3798 delayedPendingLayout = false; |
|
3799 } |
|
3800 |
|
3801 |
|
3802 |
|
3803 QWidget *QAbstractItemViewPrivate::editor(const QModelIndex &index, |
|
3804 const QStyleOptionViewItem &options) |
|
3805 { |
|
3806 Q_Q(QAbstractItemView); |
|
3807 QWidget *w = editorForIndex(index).editor; |
|
3808 if (!w) { |
|
3809 QAbstractItemDelegate *delegate = delegateForIndex(index); |
|
3810 if (!delegate) |
|
3811 return 0; |
|
3812 w = delegate->createEditor(viewport, options, index); |
|
3813 if (w) { |
|
3814 w->installEventFilter(delegate); |
|
3815 QObject::connect(w, SIGNAL(destroyed(QObject*)), q, SLOT(editorDestroyed(QObject*))); |
|
3816 delegate->updateEditorGeometry(w, options, index); |
|
3817 delegate->setEditorData(w, index); |
|
3818 addEditor(index, w, false); |
|
3819 if (w->parent() == viewport) |
|
3820 QWidget::setTabOrder(q, w); |
|
3821 |
|
3822 // Special cases for some editors containing QLineEdit |
|
3823 QWidget *focusWidget = w; |
|
3824 while (QWidget *fp = focusWidget->focusProxy()) |
|
3825 focusWidget = fp; |
|
3826 #ifndef QT_NO_LINEEDIT |
|
3827 if (QLineEdit *le = qobject_cast<QLineEdit*>(focusWidget)) |
|
3828 le->selectAll(); |
|
3829 #endif |
|
3830 #ifndef QT_NO_SPINBOX |
|
3831 if (QSpinBox *sb = qobject_cast<QSpinBox*>(focusWidget)) |
|
3832 sb->selectAll(); |
|
3833 else if (QDoubleSpinBox *dsb = qobject_cast<QDoubleSpinBox*>(focusWidget)) |
|
3834 dsb->selectAll(); |
|
3835 #endif |
|
3836 } |
|
3837 } |
|
3838 return w; |
|
3839 } |
|
3840 |
|
3841 void QAbstractItemViewPrivate::updateEditorData(const QModelIndex &tl, const QModelIndex &br) |
|
3842 { |
|
3843 // we are counting on having relatively few editors |
|
3844 const bool checkIndexes = tl.isValid() && br.isValid(); |
|
3845 const QModelIndex parent = tl.parent(); |
|
3846 QList<QEditorInfo>::const_iterator it = editors.constBegin(); |
|
3847 for (; it != editors.constEnd(); ++it) { |
|
3848 QWidget *editor = it->editor; |
|
3849 const QModelIndex index = it->index; |
|
3850 if (it->isStatic || editor == 0 || !index.isValid() || |
|
3851 (checkIndexes |
|
3852 && (index.row() < tl.row() || index.row() > br.row() |
|
3853 || index.column() < tl.column() || index.column() > br.column() |
|
3854 || index.parent() != parent))) |
|
3855 continue; |
|
3856 |
|
3857 QAbstractItemDelegate *delegate = delegateForIndex(index); |
|
3858 if (delegate) { |
|
3859 delegate->setEditorData(editor, index); |
|
3860 } |
|
3861 } |
|
3862 } |
|
3863 |
|
3864 /*! |
|
3865 \internal |
|
3866 |
|
3867 In DND if something has been moved then this is called. |
|
3868 Typically this means you should "remove" the selected item or row, |
|
3869 but the behavior is view dependant (table just clears the selected indexes for example). |
|
3870 |
|
3871 Either remove the selected rows or clear them |
|
3872 */ |
|
3873 void QAbstractItemViewPrivate::clearOrRemove() |
|
3874 { |
|
3875 #ifndef QT_NO_DRAGANDDROP |
|
3876 const QItemSelection selection = selectionModel->selection(); |
|
3877 QList<QItemSelectionRange>::const_iterator it = selection.constBegin(); |
|
3878 |
|
3879 if (!overwrite) { |
|
3880 for (; it != selection.constEnd(); ++it) { |
|
3881 QModelIndex parent = (*it).parent(); |
|
3882 if ((*it).left() != 0) |
|
3883 continue; |
|
3884 if ((*it).right() != (model->columnCount(parent) - 1)) |
|
3885 continue; |
|
3886 int count = (*it).bottom() - (*it).top() + 1; |
|
3887 model->removeRows((*it).top(), count, parent); |
|
3888 } |
|
3889 } else { |
|
3890 // we can't remove the rows so reset the items (i.e. the view is like a table) |
|
3891 QModelIndexList list = selection.indexes(); |
|
3892 for (int i=0; i < list.size(); ++i) { |
|
3893 QModelIndex index = list.at(i); |
|
3894 QMap<int, QVariant> roles = model->itemData(index); |
|
3895 for (QMap<int, QVariant>::Iterator it = roles.begin(); it != roles.end(); ++it) |
|
3896 it.value() = QVariant(); |
|
3897 model->setItemData(index, roles); |
|
3898 } |
|
3899 } |
|
3900 #endif |
|
3901 } |
|
3902 |
|
3903 /*! |
|
3904 \internal |
|
3905 |
|
3906 When persistent aeditor gets/loses focus, we need to check |
|
3907 and setcorrectly the current index. |
|
3908 */ |
|
3909 void QAbstractItemViewPrivate::checkPersistentEditorFocus() |
|
3910 { |
|
3911 Q_Q(QAbstractItemView); |
|
3912 if (QWidget *widget = QApplication::focusWidget()) { |
|
3913 if (persistent.contains(widget)) { |
|
3914 //a persistent editor has gained the focus |
|
3915 QModelIndex index = indexForEditor(widget); |
|
3916 if (selectionModel->currentIndex() != index) |
|
3917 q->setCurrentIndex(index); |
|
3918 } |
|
3919 } |
|
3920 } |
|
3921 |
|
3922 |
|
3923 QEditorInfo QAbstractItemViewPrivate::editorForIndex(const QModelIndex &index) const |
|
3924 { |
|
3925 QList<QEditorInfo>::const_iterator it = editors.constBegin(); |
|
3926 for (; it != editors.constEnd(); ++it) { |
|
3927 if (it->index == index) |
|
3928 return *it; |
|
3929 } |
|
3930 |
|
3931 return QEditorInfo(); |
|
3932 } |
|
3933 |
|
3934 QModelIndex QAbstractItemViewPrivate::indexForEditor(QWidget *editor) const |
|
3935 { |
|
3936 QList<QEditorInfo>::const_iterator it = editors.constBegin(); |
|
3937 for (; it != editors.constEnd(); ++it) { |
|
3938 if (it->editor == editor) |
|
3939 return it->index; |
|
3940 } |
|
3941 return QModelIndex(); |
|
3942 } |
|
3943 |
|
3944 void QAbstractItemViewPrivate::removeEditor(QWidget *editor) |
|
3945 { |
|
3946 QList<QEditorInfo>::iterator it = editors.begin(); |
|
3947 for (; it != editors.end(); ) { |
|
3948 if (it->editor == editor) |
|
3949 it = editors.erase(it); |
|
3950 else |
|
3951 ++it; |
|
3952 } |
|
3953 } |
|
3954 |
|
3955 void QAbstractItemViewPrivate::addEditor(const QModelIndex &index, QWidget *editor, bool isStatic) |
|
3956 { |
|
3957 editors.append(QEditorInfo(index, editor, isStatic)); |
|
3958 } |
|
3959 |
|
3960 bool QAbstractItemViewPrivate::sendDelegateEvent(const QModelIndex &index, QEvent *event) const |
|
3961 { |
|
3962 Q_Q(const QAbstractItemView); |
|
3963 QModelIndex buddy = model->buddy(index); |
|
3964 QStyleOptionViewItemV4 options = viewOptionsV4(); |
|
3965 options.rect = q->visualRect(buddy); |
|
3966 options.state |= (buddy == q->currentIndex() ? QStyle::State_HasFocus : QStyle::State_None); |
|
3967 QAbstractItemDelegate *delegate = delegateForIndex(index); |
|
3968 return (event && delegate && delegate->editorEvent(event, model, options, buddy)); |
|
3969 } |
|
3970 |
|
3971 bool QAbstractItemViewPrivate::openEditor(const QModelIndex &index, QEvent *event) |
|
3972 { |
|
3973 Q_Q(QAbstractItemView); |
|
3974 |
|
3975 QModelIndex buddy = model->buddy(index); |
|
3976 QStyleOptionViewItemV4 options = viewOptionsV4(); |
|
3977 options.rect = q->visualRect(buddy); |
|
3978 options.state |= (buddy == q->currentIndex() ? QStyle::State_HasFocus : QStyle::State_None); |
|
3979 |
|
3980 QWidget *w = editor(buddy, options); |
|
3981 if (!w) |
|
3982 return false; |
|
3983 |
|
3984 if (event) |
|
3985 QApplication::sendEvent(w->focusProxy() ? w->focusProxy() : w, event); |
|
3986 |
|
3987 q->setState(QAbstractItemView::EditingState); |
|
3988 w->show(); |
|
3989 w->setFocus(); |
|
3990 |
|
3991 return true; |
|
3992 } |
|
3993 |
|
3994 /* |
|
3995 \internal |
|
3996 |
|
3997 returns the pair QRect/QModelIndex that should be painted on the viewports's rect |
|
3998 */ |
|
3999 |
|
4000 QItemViewPaintPairs QAbstractItemViewPrivate::draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const |
|
4001 { |
|
4002 Q_ASSERT(r); |
|
4003 Q_Q(const QAbstractItemView); |
|
4004 QRect &rect = *r; |
|
4005 const QRect viewportRect = viewport->rect(); |
|
4006 QItemViewPaintPairs ret; |
|
4007 for (int i = 0; i < indexes.count(); ++i) { |
|
4008 const QModelIndex &index = indexes.at(i); |
|
4009 const QRect current = q->visualRect(index); |
|
4010 if (current.intersects(viewportRect)) { |
|
4011 ret += qMakePair(current, index); |
|
4012 rect |= current; |
|
4013 } |
|
4014 } |
|
4015 rect &= viewportRect; |
|
4016 return ret; |
|
4017 } |
|
4018 |
|
4019 QPixmap QAbstractItemViewPrivate::renderToPixmap(const QModelIndexList &indexes, QRect *r) const |
|
4020 { |
|
4021 Q_ASSERT(r); |
|
4022 QItemViewPaintPairs paintPairs = draggablePaintPairs(indexes, r); |
|
4023 if (paintPairs.isEmpty()) |
|
4024 return QPixmap(); |
|
4025 QPixmap pixmap(r->size()); |
|
4026 pixmap.fill(Qt::transparent); |
|
4027 QPainter painter(&pixmap); |
|
4028 QStyleOptionViewItemV4 option = viewOptionsV4(); |
|
4029 option.state |= QStyle::State_Selected; |
|
4030 for (int j = 0; j < paintPairs.count(); ++j) { |
|
4031 option.rect = paintPairs.at(j).first.translated(-r->topLeft()); |
|
4032 const QModelIndex ¤t = paintPairs.at(j).second; |
|
4033 delegateForIndex(current)->paint(&painter, option, current); |
|
4034 } |
|
4035 return pixmap; |
|
4036 } |
|
4037 |
|
4038 void QAbstractItemViewPrivate::selectAll(QItemSelectionModel::SelectionFlags command) |
|
4039 { |
|
4040 if (!selectionModel) |
|
4041 return; |
|
4042 |
|
4043 QItemSelection selection; |
|
4044 QModelIndex tl = model->index(0, 0, root); |
|
4045 QModelIndex br = model->index(model->rowCount(root) - 1, |
|
4046 model->columnCount(root) - 1, |
|
4047 root); |
|
4048 selection.append(QItemSelectionRange(tl, br)); |
|
4049 selectionModel->select(selection, command); |
|
4050 } |
|
4051 |
|
4052 QModelIndexList QAbstractItemViewPrivate::selectedDraggableIndexes() const |
|
4053 { |
|
4054 Q_Q(const QAbstractItemView); |
|
4055 QModelIndexList indexes = q->selectedIndexes(); |
|
4056 for(int i = indexes.count() - 1 ; i >= 0; --i) { |
|
4057 if (!isIndexDragEnabled(indexes.at(i))) |
|
4058 indexes.removeAt(i); |
|
4059 } |
|
4060 return indexes; |
|
4061 } |
|
4062 |
|
4063 |
|
4064 QT_END_NAMESPACE |
|
4065 |
|
4066 #include "moc_qabstractitemview.cpp" |
|
4067 |
|
4068 #endif // QT_NO_ITEMVIEWS |