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