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 "qcombobox.h"
41
42#include <qstylepainter.h>
43#include <qpa/qplatformtheme.h>
44#include <qpa/qplatformmenu.h>
45#include <qlineedit.h>
46#include <qapplication.h>
47#include <qdesktopwidget.h>
48#include <private/qdesktopwidget_p.h>
49#include <qlistview.h>
50#if QT_CONFIG(tableview)
51#include <qtableview.h>
52#endif
53#include <qitemdelegate.h>
54#include <qmap.h>
55#if QT_CONFIG(menu)
56#include <qmenu.h>
57#endif
58#include <qevent.h>
59#include <qlayout.h>
60#include <qscrollbar.h>
61#if QT_CONFIG(treeview)
62#include <qtreeview.h>
63#endif
64#include <qheaderview.h>
65#include <qmath.h>
66#include <qmetaobject.h>
67#include <qabstractproxymodel.h>
68#include <qstylehints.h>
69#include <private/qguiapplication_p.h>
70#include <private/qhighdpiscaling_p.h>
71#include <private/qapplication_p.h>
72#include <private/qcombobox_p.h>
73#include <private/qabstractitemmodel_p.h>
74#include <private/qabstractscrollarea_p.h>
75#include <private/qlineedit_p.h>
76#if QT_CONFIG(completer)
77#include <private/qcompleter_p.h>
78#endif
79#include <qdebug.h>
80#if QT_CONFIG(effects)
81# include <private/qeffects_p.h>
82#endif
83#include <private/qstyle_p.h>
84#ifndef QT_NO_ACCESSIBILITY
85#include "qaccessible.h"
86#endif
87
88QT_BEGIN_NAMESPACE
89
90QComboBoxPrivate::QComboBoxPrivate()
91 : QWidgetPrivate(),
92 model(0),
93 lineEdit(0),
94 container(0),
95 insertPolicy(QComboBox::InsertAtBottom),
96 sizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow),
97 minimumContentsLength(0),
98 shownOnce(false),
99 autoCompletion(true),
100 duplicatesEnabled(false),
101 frame(true),
102 maxVisibleItems(10),
103 maxCount(INT_MAX),
104 modelColumn(0),
105 inserting(false),
106 arrowState(QStyle::State_None),
107 hoverControl(QStyle::SC_None),
108 autoCompletionCaseSensitivity(Qt::CaseInsensitive),
109 indexBeforeChange(-1)
110#ifdef Q_OS_MAC
111 , m_platformMenu(0)
112#endif
113#if QT_CONFIG(completer)
114 , completer(0)
115#endif
116{
117}
118
119QComboBoxPrivate::~QComboBoxPrivate()
120{
121#ifdef Q_OS_MAC
122 cleanupNativePopup();
123#endif
124}
125
126QStyleOptionMenuItem QComboMenuDelegate::getStyleOption(const QStyleOptionViewItem &option,
127 const QModelIndex &index) const
128{
129 QStyleOptionMenuItem menuOption;
130
131 QPalette resolvedpalette = option.palette.resolve(QApplication::palette("QMenu"));
132 QVariant value = index.data(Qt::ForegroundRole);
133 if (value.canConvert<QBrush>()) {
134 resolvedpalette.setBrush(QPalette::WindowText, qvariant_cast<QBrush>(value));
135 resolvedpalette.setBrush(QPalette::ButtonText, qvariant_cast<QBrush>(value));
136 resolvedpalette.setBrush(QPalette::Text, qvariant_cast<QBrush>(value));
137 }
138 menuOption.palette = resolvedpalette;
139 menuOption.state = QStyle::State_None;
140 if (mCombo->window()->isActiveWindow())
141 menuOption.state = QStyle::State_Active;
142 if ((option.state & QStyle::State_Enabled) && (index.model()->flags(index) & Qt::ItemIsEnabled))
143 menuOption.state |= QStyle::State_Enabled;
144 else
145 menuOption.palette.setCurrentColorGroup(QPalette::Disabled);
146 if (option.state & QStyle::State_Selected)
147 menuOption.state |= QStyle::State_Selected;
148 menuOption.checkType = QStyleOptionMenuItem::NonExclusive;
149 menuOption.checked = mCombo->currentIndex() == index.row();
150 if (QComboBoxDelegate::isSeparator(index))
151 menuOption.menuItemType = QStyleOptionMenuItem::Separator;
152 else
153 menuOption.menuItemType = QStyleOptionMenuItem::Normal;
154
155 QVariant variant = index.model()->data(index, Qt::DecorationRole);
156 switch (variant.type()) {
157 case QVariant::Icon:
158 menuOption.icon = qvariant_cast<QIcon>(variant);
159 break;
160 case QVariant::Color: {
161 static QPixmap pixmap(option.decorationSize);
162 pixmap.fill(qvariant_cast<QColor>(variant));
163 menuOption.icon = pixmap;
164 break; }
165 default:
166 menuOption.icon = qvariant_cast<QPixmap>(variant);
167 break;
168 }
169 if (index.data(Qt::BackgroundRole).canConvert<QBrush>()) {
170 menuOption.palette.setBrush(QPalette::All, QPalette::Window,
171 qvariant_cast<QBrush>(index.data(Qt::BackgroundRole)));
172 }
173 menuOption.text = index.model()->data(index, Qt::DisplayRole).toString()
174 .replace(QLatin1Char('&'), QLatin1String("&&"));
175 menuOption.tabWidth = 0;
176 menuOption.maxIconWidth = option.decorationSize.width() + 4;
177 menuOption.menuRect = option.rect;
178 menuOption.rect = option.rect;
179
180 // Make sure fonts set on the model or on the combo box, in
181 // that order, also override the font for the popup menu.
182 QVariant fontRoleData = index.data(Qt::FontRole);
183 if (fontRoleData.isValid()) {
184 menuOption.font = fontRoleData.value<QFont>();
185 } else if (mCombo->testAttribute(Qt::WA_SetFont)
186 || mCombo->testAttribute(Qt::WA_MacSmallSize)
187 || mCombo->testAttribute(Qt::WA_MacMiniSize)
188 || mCombo->font() != qt_app_fonts_hash()->value("QComboBox", QFont())) {
189 menuOption.font = mCombo->font();
190 } else {
191 menuOption.font = qt_app_fonts_hash()->value("QComboMenuItem", mCombo->font());
192 }
193
194 menuOption.fontMetrics = QFontMetrics(menuOption.font);
195
196 return menuOption;
197}
198
199#if QT_CONFIG(completer)
200void QComboBoxPrivate::_q_completerActivated(const QModelIndex &index)
201{
202 Q_Q(QComboBox);
203 if (index.isValid() && q->completer()) {
204 QAbstractProxyModel *proxy = qobject_cast<QAbstractProxyModel *>(q->completer()->completionModel());
205 if (proxy) {
206 const QModelIndex &completerIndex = proxy->mapToSource(index);
207 int row = -1;
208 if (completerIndex.model() == model) {
209 row = completerIndex.row();
210 } else {
211 // if QCompleter uses a proxy model to host widget's one - map again
212 QAbstractProxyModel *completerProxy = qobject_cast<QAbstractProxyModel *>(q->completer()->model());
213 if (completerProxy && completerProxy->sourceModel() == model) {
214 row = completerProxy->mapToSource(completerIndex).row();
215 } else {
216 QString match = q->completer()->model()->data(completerIndex).toString();
217 row = q->findText(match, matchFlags());
218 }
219 }
220 q->setCurrentIndex(row);
221 emitActivated(currentIndex);
222 }
223 }
224
225# ifdef QT_KEYPAD_NAVIGATION
226 if ( QApplicationPrivate::keypadNavigationEnabled()
227 && q->isEditable()
228 && q->completer()
229 && q->completer()->completionMode() == QCompleter::UnfilteredPopupCompletion ) {
230 q->setEditFocus(false);
231 }
232# endif // QT_KEYPAD_NAVIGATION
233}
234#endif // QT_CONFIG(completer)
235
236void QComboBoxPrivate::updateArrow(QStyle::StateFlag state)
237{
238 Q_Q(QComboBox);
239 if (arrowState == state)
240 return;
241 arrowState = state;
242 QStyleOptionComboBox opt;
243 q->initStyleOption(&opt);
244 q->update(q->rect());
245}
246
247void QComboBoxPrivate::_q_modelReset()
248{
249 Q_Q(QComboBox);
250 if (lineEdit) {
251 lineEdit->setText(QString());
252 updateLineEditGeometry();
253 }
254 if (currentIndex.row() != indexBeforeChange)
255 _q_emitCurrentIndexChanged(currentIndex);
256 modelChanged();
257 q->update();
258}
259
260void QComboBoxPrivate::_q_modelDestroyed()
261{
262 model = QAbstractItemModelPrivate::staticEmptyModel();
263}
264
265QRect QComboBoxPrivate::popupGeometry(int screen) const
266{
267 return QStylePrivate::useFullScreenForPopup()
268 ? QDesktopWidgetPrivate::screenGeometry(screen)
269 : QDesktopWidgetPrivate::availableGeometry(screen);
270}
271
272bool QComboBoxPrivate::updateHoverControl(const QPoint &pos)
273{
274
275 Q_Q(QComboBox);
276 QRect lastHoverRect = hoverRect;
277 QStyle::SubControl lastHoverControl = hoverControl;
278 bool doesHover = q->testAttribute(Qt::WA_Hover);
279 if (lastHoverControl != newHoverControl(pos) && doesHover) {
280 q->update(lastHoverRect);
281 q->update(hoverRect);
282 return true;
283 }
284 return !doesHover;
285}
286
287QStyle::SubControl QComboBoxPrivate::newHoverControl(const QPoint &pos)
288{
289 Q_Q(QComboBox);
290 QStyleOptionComboBox opt;
291 q->initStyleOption(&opt);
292 opt.subControls = QStyle::SC_All;
293 hoverControl = q->style()->hitTestComplexControl(QStyle::CC_ComboBox, &opt, pos, q);
294 hoverRect = (hoverControl != QStyle::SC_None)
295 ? q->style()->subControlRect(QStyle::CC_ComboBox, &opt, hoverControl, q)
296 : QRect();
297 return hoverControl;
298}
299
300/*
301 Computes a size hint based on the maximum width
302 for the items in the combobox.
303*/
304int QComboBoxPrivate::computeWidthHint() const
305{
306 Q_Q(const QComboBox);
307
308 int width = 0;
309 const int count = q->count();
310 const int iconWidth = q->iconSize().width() + 4;
311 const QFontMetrics &fontMetrics = q->fontMetrics();
312
313 for (int i = 0; i < count; ++i) {
314 const int textWidth = fontMetrics.horizontalAdvance(q->itemText(i));
315 if (q->itemIcon(i).isNull())
316 width = (qMax(width, textWidth));
317 else
318 width = (qMax(width, textWidth + iconWidth));
319 }
320
321 QStyleOptionComboBox opt;
322 q->initStyleOption(&opt);
323 QSize tmp(width, 0);
324 tmp = q->style()->sizeFromContents(QStyle::CT_ComboBox, &opt, tmp, q);
325 return tmp.width();
326}
327
328QSize QComboBoxPrivate::recomputeSizeHint(QSize &sh) const
329{
330 Q_Q(const QComboBox);
331 if (!sh.isValid()) {
332 bool hasIcon = sizeAdjustPolicy == QComboBox::AdjustToMinimumContentsLengthWithIcon;
333 int count = q->count();
334 QSize iconSize = q->iconSize();
335 const QFontMetrics &fm = q->fontMetrics();
336
337 // text width
338 if (&sh == &sizeHint || minimumContentsLength == 0) {
339 switch (sizeAdjustPolicy) {
340 case QComboBox::AdjustToContents:
341 case QComboBox::AdjustToContentsOnFirstShow:
342 if (count == 0) {
343 sh.rwidth() = 7 * fm.horizontalAdvance(QLatin1Char('x'));
344 } else {
345 for (int i = 0; i < count; ++i) {
346 if (!q->itemIcon(i).isNull()) {
347 hasIcon = true;
348 sh.setWidth(qMax(sh.width(), fm.boundingRect(q->itemText(i)).width() + iconSize.width() + 4));
349 } else {
350 sh.setWidth(qMax(sh.width(), fm.boundingRect(q->itemText(i)).width()));
351 }
352 }
353 }
354 break;
355 case QComboBox::AdjustToMinimumContentsLength:
356 for (int i = 0; i < count && !hasIcon; ++i)
357 hasIcon = !q->itemIcon(i).isNull();
358 default:
359 ;
360 }
361 } else {
362 for (int i = 0; i < count && !hasIcon; ++i)
363 hasIcon = !q->itemIcon(i).isNull();
364 }
365 if (minimumContentsLength > 0)
366 sh.setWidth(qMax(sh.width(), minimumContentsLength * fm.horizontalAdvance(QLatin1Char('X')) + (hasIcon ? iconSize.width() + 4 : 0)));
367
368
369 // height
370 sh.setHeight(qMax(qCeil(QFontMetricsF(fm).height()), 14) + 2);
371 if (hasIcon) {
372 sh.setHeight(qMax(sh.height(), iconSize.height() + 2));
373 }
374
375 // add style and strut values
376 QStyleOptionComboBox opt;
377 q->initStyleOption(&opt);
378 sh = q->style()->sizeFromContents(QStyle::CT_ComboBox, &opt, sh, q);
379 }
380 return sh.expandedTo(QApplication::globalStrut());
381}
382
383void QComboBoxPrivate::adjustComboBoxSize()
384{
385 viewContainer()->adjustSizeTimer.start(20, container);
386}
387
388void QComboBoxPrivate::updateLayoutDirection()
389{
390 Q_Q(const QComboBox);
391 QStyleOptionComboBox opt;
392 q->initStyleOption(&opt);
393 Qt::LayoutDirection dir = Qt::LayoutDirection(
394 q->style()->styleHint(QStyle::SH_ComboBox_LayoutDirection, &opt, q));
395 if (lineEdit)
396 lineEdit->setLayoutDirection(dir);
397 if (container)
398 container->setLayoutDirection(dir);
399}
400
401
402void QComboBoxPrivateContainer::timerEvent(QTimerEvent *timerEvent)
403{
404 if (timerEvent->timerId() == adjustSizeTimer.timerId()) {
405 adjustSizeTimer.stop();
406 if (combo->sizeAdjustPolicy() == QComboBox::AdjustToContents) {
407 combo->updateGeometry();
408 combo->adjustSize();
409 combo->update();
410 }
411 }
412}
413
414void QComboBoxPrivateContainer::resizeEvent(QResizeEvent *e)
415{
416 QStyleOptionComboBox opt = comboStyleOption();
417 if (combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo)) {
418 QStyleOption myOpt;
419 myOpt.initFrom(this);
420 QStyleHintReturnMask mask;
421 if (combo->style()->styleHint(QStyle::SH_Menu_Mask, &myOpt, this, &mask)) {
422 setMask(mask.region);
423 }
424 } else {
425 clearMask();
426 }
427 QFrame::resizeEvent(e);
428}
429
430void QComboBoxPrivateContainer::paintEvent(QPaintEvent *e)
431{
432 QStyleOptionComboBox cbOpt = comboStyleOption();
433 if (combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &cbOpt, combo)
434 && mask().isEmpty()) {
435 QStyleOption opt;
436 opt.initFrom(this);
437 QPainter p(this);
438 style()->drawPrimitive(QStyle::PE_PanelMenu, &opt, &p, this);
439 }
440
441 QFrame::paintEvent(e);
442}
443
444void QComboBoxPrivateContainer::leaveEvent(QEvent *)
445{
446// On Mac using the Mac style we want to clear the selection
447// when the mouse moves outside the popup.
448#if 0 // Used to be included in Qt4 for Q_WS_MAC
449 QStyleOptionComboBox opt = comboStyleOption();
450 if (combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo))
451 view->clearSelection();
452#endif
453}
454
455QComboBoxPrivateContainer::QComboBoxPrivateContainer(QAbstractItemView *itemView, QComboBox *parent)
456 : QFrame(parent, Qt::Popup), combo(parent), view(0), top(0), bottom(0), maybeIgnoreMouseButtonRelease(false)
457{
458 // we need the combobox and itemview
459 Q_ASSERT(parent);
460 Q_ASSERT(itemView);
461
462 setAttribute(Qt::WA_WindowPropagation);
463 setAttribute(Qt::WA_X11NetWmWindowTypeCombo);
464
465 // setup container
466 blockMouseReleaseTimer.setSingleShot(true);
467
468 // we need a vertical layout
469 QBoxLayout *layout = new QBoxLayout(QBoxLayout::TopToBottom, this);
470 layout->setSpacing(0);
471 layout->setContentsMargins(QMargins());
472
473 // set item view
474 setItemView(itemView);
475
476 // add scroller arrows if style needs them
477 QStyleOptionComboBox opt = comboStyleOption();
478 const bool usePopup = combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo);
479 if (usePopup) {
480 top = new QComboBoxPrivateScroller(QAbstractSlider::SliderSingleStepSub, this);
481 bottom = new QComboBoxPrivateScroller(QAbstractSlider::SliderSingleStepAdd, this);
482 top->hide();
483 bottom->hide();
484 } else {
485 setLineWidth(1);
486 }
487
488 setFrameStyle(combo->style()->styleHint(QStyle::SH_ComboBox_PopupFrameStyle, &opt, combo));
489
490 if (top) {
491 layout->insertWidget(0, top);
492 connect(top, SIGNAL(doScroll(int)), this, SLOT(scrollItemView(int)));
493 }
494 if (bottom) {
495 layout->addWidget(bottom);
496 connect(bottom, SIGNAL(doScroll(int)), this, SLOT(scrollItemView(int)));
497 }
498
499 // Some styles (Mac) have a margin at the top and bottom of the popup.
500 layout->insertSpacing(0, 0);
501 layout->addSpacing(0);
502 updateTopBottomMargin();
503}
504
505void QComboBoxPrivateContainer::scrollItemView(int action)
506{
507#if QT_CONFIG(scrollbar)
508 if (view->verticalScrollBar())
509 view->verticalScrollBar()->triggerAction(static_cast<QAbstractSlider::SliderAction>(action));
510#endif
511}
512
513void QComboBoxPrivateContainer::hideScrollers()
514{
515 if (top)
516 top->hide();
517 if (bottom)
518 bottom->hide();
519}
520
521/*
522 Hides or shows the scrollers when we emulate a popupmenu
523*/
524void QComboBoxPrivateContainer::updateScrollers()
525{
526#if QT_CONFIG(scrollbar)
527 if (!top || !bottom)
528 return;
529
530 if (isVisible() == false)
531 return;
532
533 QStyleOptionComboBox opt = comboStyleOption();
534 if (combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo) &&
535 view->verticalScrollBar()->minimum() < view->verticalScrollBar()->maximum()) {
536
537 bool needTop = view->verticalScrollBar()->value()
538 > (view->verticalScrollBar()->minimum() + topMargin());
539 bool needBottom = view->verticalScrollBar()->value()
540 < (view->verticalScrollBar()->maximum() - bottomMargin() - topMargin());
541 if (needTop)
542 top->show();
543 else
544 top->hide();
545 if (needBottom)
546 bottom->show();
547 else
548 bottom->hide();
549 } else {
550 top->hide();
551 bottom->hide();
552 }
553#endif // QT_CONFIG(scrollbar)
554}
555
556/*
557 Cleans up when the view is destroyed.
558*/
559void QComboBoxPrivateContainer::viewDestroyed()
560{
561 view = 0;
562 setItemView(new QComboBoxListView());
563}
564
565/*
566 Returns the item view used for the combobox popup.
567*/
568QAbstractItemView *QComboBoxPrivateContainer::itemView() const
569{
570 return view;
571}
572
573/*!
574 Sets the item view to be used for the combobox popup.
575*/
576void QComboBoxPrivateContainer::setItemView(QAbstractItemView *itemView)
577{
578 Q_ASSERT(itemView);
579
580 // clean up old one
581 if (view) {
582 view->removeEventFilter(this);
583 view->viewport()->removeEventFilter(this);
584#if QT_CONFIG(scrollbar)
585 disconnect(view->verticalScrollBar(), SIGNAL(valueChanged(int)),
586 this, SLOT(updateScrollers()));
587 disconnect(view->verticalScrollBar(), SIGNAL(rangeChanged(int,int)),
588 this, SLOT(updateScrollers()));
589#endif
590 disconnect(view, SIGNAL(destroyed()),
591 this, SLOT(viewDestroyed()));
592
593 if (isAncestorOf(view))
594 delete view;
595 view = 0;
596 }
597
598 // setup the item view
599 view = itemView;
600 view->setParent(this);
601 view->setAttribute(Qt::WA_MacShowFocusRect, false);
602 qobject_cast<QBoxLayout*>(layout())->insertWidget(top ? 2 : 0, view);
603 view->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
604 view->installEventFilter(this);
605 view->viewport()->installEventFilter(this);
606 view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
607 QStyleOptionComboBox opt = comboStyleOption();
608 const bool usePopup = combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo);
609#if QT_CONFIG(scrollbar)
610 if (usePopup)
611 view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
612#endif
613 if (combo->style()->styleHint(QStyle::SH_ComboBox_ListMouseTracking, &opt, combo) ||
614 usePopup) {
615 view->setMouseTracking(true);
616 }
617 view->setSelectionMode(QAbstractItemView::SingleSelection);
618 view->setFrameStyle(QFrame::NoFrame);
619 view->setLineWidth(0);
620 view->setEditTriggers(QAbstractItemView::NoEditTriggers);
621#if QT_CONFIG(scrollbar)
622 connect(view->verticalScrollBar(), SIGNAL(valueChanged(int)),
623 this, SLOT(updateScrollers()));
624 connect(view->verticalScrollBar(), SIGNAL(rangeChanged(int,int)),
625 this, SLOT(updateScrollers()));
626#endif
627 connect(view, SIGNAL(destroyed()),
628 this, SLOT(viewDestroyed()));
629}
630
631/*!
632 Returns the top/bottom vertical margin of the view.
633*/
634int QComboBoxPrivateContainer::topMargin() const
635{
636 if (const QListView *lview = qobject_cast<const QListView*>(view))
637 return lview->spacing();
638#if QT_CONFIG(tableview)
639 if (const QTableView *tview = qobject_cast<const QTableView*>(view))
640 return tview->showGrid() ? 1 : 0;
641#endif
642 return 0;
643}
644
645/*!
646 Returns the spacing between the items in the view.
647*/
648int QComboBoxPrivateContainer::spacing() const
649{
650 QListView *lview = qobject_cast<QListView*>(view);
651 if (lview)
652 return 2 * lview->spacing(); // QListView::spacing is the padding around the item.
653#if QT_CONFIG(tableview)
654 QTableView *tview = qobject_cast<QTableView*>(view);
655 if (tview)
656 return tview->showGrid() ? 1 : 0;
657#endif
658 return 0;
659}
660
661void QComboBoxPrivateContainer::updateTopBottomMargin()
662{
663 if (!layout() || layout()->count() < 1)
664 return;
665
666 QBoxLayout *boxLayout = qobject_cast<QBoxLayout *>(layout());
667 if (!boxLayout)
668 return;
669
670 const QStyleOptionComboBox opt = comboStyleOption();
671 const bool usePopup = combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo);
672 const int margin = usePopup ? combo->style()->pixelMetric(QStyle::PM_MenuVMargin, &opt, combo) : 0;
673
674 QSpacerItem *topSpacer = boxLayout->itemAt(0)->spacerItem();
675 if (topSpacer)
676 topSpacer->changeSize(0, margin, QSizePolicy::Minimum, QSizePolicy::Fixed);
677
678 QSpacerItem *bottomSpacer = boxLayout->itemAt(boxLayout->count() - 1)->spacerItem();
679 if (bottomSpacer && bottomSpacer != topSpacer)
680 bottomSpacer->changeSize(0, margin, QSizePolicy::Minimum, QSizePolicy::Fixed);
681
682 boxLayout->invalidate();
683}
684
685void QComboBoxPrivateContainer::changeEvent(QEvent *e)
686{
687 if (e->type() == QEvent::StyleChange) {
688 QStyleOptionComboBox opt = comboStyleOption();
689 view->setMouseTracking(combo->style()->styleHint(QStyle::SH_ComboBox_ListMouseTracking, &opt, combo) ||
690 combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo));
691 setFrameStyle(combo->style()->styleHint(QStyle::SH_ComboBox_PopupFrameStyle, &opt, combo));
692 }
693
694 QFrame::changeEvent(e);
695}
696
697
698bool QComboBoxPrivateContainer::eventFilter(QObject *o, QEvent *e)
699{
700 switch (e->type()) {
701 case QEvent::ShortcutOverride: {
702 QKeyEvent *keyEvent = static_cast<QKeyEvent*>(e);
703 switch (keyEvent->key()) {
704 case Qt::Key_Enter:
705 case Qt::Key_Return:
706#ifdef QT_KEYPAD_NAVIGATION
707 case Qt::Key_Select:
708#endif
709 if (view->currentIndex().isValid() && (view->currentIndex().flags() & Qt::ItemIsEnabled) ) {
710 combo->hidePopup();
711 emit itemSelected(view->currentIndex());
712 }
713 return true;
714 case Qt::Key_Down:
715 if (!(keyEvent->modifiers() & Qt::AltModifier))
716 break;
717 Q_FALLTHROUGH();
718 case Qt::Key_F4:
719 combo->hidePopup();
720 return true;
721 default:
722#if QT_CONFIG(shortcut)
723 if (keyEvent->matches(QKeySequence::Cancel)) {
724 combo->hidePopup();
725 return true;
726 }
727#endif
728 break;
729 }
730 break;
731 }
732 case QEvent::MouseMove:
733 if (isVisible()) {
734 QMouseEvent *m = static_cast<QMouseEvent *>(e);
735 QWidget *widget = static_cast<QWidget *>(o);
736 QPoint vector = widget->mapToGlobal(m->pos()) - initialClickPosition;
737 if (vector.manhattanLength() > 9 && blockMouseReleaseTimer.isActive())
738 blockMouseReleaseTimer.stop();
739 QModelIndex indexUnderMouse = view->indexAt(m->pos());
740 if (indexUnderMouse.isValid()
741 && !QComboBoxDelegate::isSeparator(indexUnderMouse)) {
742 view->setCurrentIndex(indexUnderMouse);
743 }
744 }
745 break;
746 case QEvent::MouseButtonPress:
747 maybeIgnoreMouseButtonRelease = false;
748 break;
749 case QEvent::MouseButtonRelease: {
750 bool ignoreEvent = maybeIgnoreMouseButtonRelease && popupTimer.elapsed() < QApplication::doubleClickInterval();
751
752 QMouseEvent *m = static_cast<QMouseEvent *>(e);
753 if (isVisible() && view->rect().contains(m->pos()) && view->currentIndex().isValid()
754 && !blockMouseReleaseTimer.isActive() && !ignoreEvent
755 && (view->currentIndex().flags() & Qt::ItemIsEnabled)
756 && (view->currentIndex().flags() & Qt::ItemIsSelectable)) {
757 combo->hidePopup();
758 emit itemSelected(view->currentIndex());
759 return true;
760 }
761 break;
762 }
763 default:
764 break;
765 }
766 return QFrame::eventFilter(o, e);
767}
768
769void QComboBoxPrivateContainer::showEvent(QShowEvent *)
770{
771 combo->update();
772}
773
774void QComboBoxPrivateContainer::hideEvent(QHideEvent *)
775{
776 emit resetButton();
777 combo->update();
778#if QT_CONFIG(graphicsview)
779 // QGraphicsScenePrivate::removePopup closes the combo box popup, it hides it non-explicitly.
780 // Hiding/showing the QComboBox after this will unexpectedly show the popup as well.
781 // Re-hiding the popup container makes sure it is explicitly hidden.
782 if (QGraphicsProxyWidget *proxy = graphicsProxyWidget())
783 proxy->hide();
784#endif
785}
786
787void QComboBoxPrivateContainer::mousePressEvent(QMouseEvent *e)
788{
789
790 QStyleOptionComboBox opt = comboStyleOption();
791 opt.subControls = QStyle::SC_All;
792 opt.activeSubControls = QStyle::SC_ComboBoxArrow;
793 QStyle::SubControl sc = combo->style()->hitTestComplexControl(QStyle::CC_ComboBox, &opt,
794 combo->mapFromGlobal(e->globalPos()),
795 combo);
796 if ((combo->isEditable() && sc == QStyle::SC_ComboBoxArrow)
797 || (!combo->isEditable() && sc != QStyle::SC_None))
798 setAttribute(Qt::WA_NoMouseReplay);
799 combo->hidePopup();
800}
801
802void QComboBoxPrivateContainer::mouseReleaseEvent(QMouseEvent *e)
803{
804 Q_UNUSED(e);
805 if (!blockMouseReleaseTimer.isActive()){
806 combo->hidePopup();
807 emit resetButton();
808 }
809}
810
811QStyleOptionComboBox QComboBoxPrivateContainer::comboStyleOption() const
812{
813 // ### This should use QComboBox's initStyleOption(), but it's protected
814 // perhaps, we could cheat by having the QCombo private instead?
815 QStyleOptionComboBox opt;
816 opt.initFrom(combo);
817 opt.subControls = QStyle::SC_All;
818 opt.activeSubControls = QStyle::SC_None;
819 opt.editable = combo->isEditable();
820 return opt;
821}
822
823/*!
824 \enum QComboBox::InsertPolicy
825
826 This enum specifies what the QComboBox should do when a new string is
827 entered by the user.
828
829 \value NoInsert The string will not be inserted into the combobox.
830 \value InsertAtTop The string will be inserted as the first item in the combobox.
831 \value InsertAtCurrent The current item will be \e replaced by the string.
832 \value InsertAtBottom The string will be inserted after the last item in the combobox.
833 \value InsertAfterCurrent The string is inserted after the current item in the combobox.
834 \value InsertBeforeCurrent The string is inserted before the current item in the combobox.
835 \value InsertAlphabetically The string is inserted in the alphabetic order in the combobox.
836*/
837
838/*!
839 \enum QComboBox::SizeAdjustPolicy
840
841 This enum specifies how the size hint of the QComboBox should
842 adjust when new content is added or content changes.
843
844 \value AdjustToContents The combobox will always adjust to the contents
845 \value AdjustToContentsOnFirstShow The combobox will adjust to its contents the first time it is shown.
846 \value AdjustToMinimumContentsLength Use AdjustToContents or AdjustToContentsOnFirstShow instead.
847 \value AdjustToMinimumContentsLengthWithIcon The combobox will adjust to \l minimumContentsLength plus space for an icon. For performance reasons use this policy on large models.
848*/
849
850/*!
851 \fn void QComboBox::activated(int index)
852
853 This signal is sent when the user chooses an item in the combobox.
854 The item's \a index is passed. Note that this signal is sent even
855 when the choice is not changed. If you need to know when the
856 choice actually changes, use signal currentIndexChanged().
857
858*/
859
860/*!
861 \fn void QComboBox::activated(const QString &text)
862
863 This signal is sent when the user chooses an item in the combobox.
864 The item's \a text is passed. Note that this signal is sent even
865 when the choice is not changed. If you need to know when the
866 choice actually changes, use signal currentIndexChanged().
867
868*/
869
870/*!
871 \fn void QComboBox::highlighted(int index)
872
873 This signal is sent when an item in the combobox popup list is
874 highlighted by the user. The item's \a index is passed.
875*/
876
877/*!
878 \fn void QComboBox::highlighted(const QString &text)
879
880 This signal is sent when an item in the combobox popup list is
881 highlighted by the user. The item's \a text is passed.
882*/
883
884/*!
885 \fn void QComboBox::currentIndexChanged(int index)
886 \since 4.1
887
888 This signal is sent whenever the currentIndex in the combobox
889 changes either through user interaction or programmatically. The
890 item's \a index is passed or -1 if the combobox becomes empty or the
891 currentIndex was reset.
892*/
893
894#if QT_DEPRECATED_SINCE(5, 13)
895/*!
896 \fn void QComboBox::currentIndexChanged(const QString &text)
897 \since 4.1
898
899 This signal is sent whenever the currentIndex in the combobox
900 changes either through user interaction or programmatically. The
901 item's \a text is passed.
902*/
903#endif
904
905/*!
906 \fn void QComboBox::currentTextChanged(const QString &text)
907 \since 5.0
908
909 This signal is sent whenever currentText changes. The new value
910 is passed as \a text.
911*/
912
913/*!
914 Constructs a combobox with the given \a parent, using the default
915 model QStandardItemModel.
916*/
917QComboBox::QComboBox(QWidget *parent)
918 : QWidget(*new QComboBoxPrivate(), parent, 0)
919{
920 Q_D(QComboBox);
921 d->init();
922}
923
924/*!
925 \internal
926*/
927QComboBox::QComboBox(QComboBoxPrivate &dd, QWidget *parent)
928 : QWidget(dd, parent, 0)
929{
930 Q_D(QComboBox);
931 d->init();
932}
933
934
935/*!
936 \class QComboBox
937 \brief The QComboBox widget is a combined button and popup list.
938
939 \ingroup basicwidgets
940 \inmodule QtWidgets
941
942 \image windows-combobox.png
943
944 A QComboBox provides a means of presenting a list of options to the user
945 in a way that takes up the minimum amount of screen space.
946
947 A combobox is a selection widget that displays the current item,
948 and can pop up a list of selectable items. A combobox may be editable,
949 allowing the user to modify each item in the list.
950
951 Comboboxes can contain pixmaps as well as strings; the
952 insertItem() and setItemText() functions are suitably overloaded.
953 For editable comboboxes, the function clearEditText() is provided,
954 to clear the displayed string without changing the combobox's
955 contents.
956
957 There are two signals emitted if the current item of a combobox
958 changes, currentIndexChanged() and activated().
959 currentIndexChanged() is always emitted regardless if the change
960 was done programmatically or by user interaction, while
961 activated() is only emitted when the change is caused by user
962 interaction. The highlighted() signal is emitted when the user
963 highlights an item in the combobox popup list. All three signals
964 exist in two versions, one with a QString argument and one with an
965 \c int argument. If the user selects or highlights a pixmap, only
966 the \c int signals are emitted. Whenever the text of an editable
967 combobox is changed the editTextChanged() signal is emitted.
968
969 When the user enters a new string in an editable combobox, the
970 widget may or may not insert it, and it can insert it in several
971 locations. The default policy is \l InsertAtBottom but you can change
972 this using setInsertPolicy().
973
974 It is possible to constrain the input to an editable combobox
975 using QValidator; see setValidator(). By default, any input is
976 accepted.
977
978 A combobox can be populated using the insert functions,
979 insertItem() and insertItems() for example. Items can be
980 changed with setItemText(). An item can be removed with
981 removeItem() and all items can be removed with clear(). The text
982 of the current item is returned by currentText(), and the text of
983 a numbered item is returned with text(). The current item can be
984 set with setCurrentIndex(). The number of items in the combobox is
985 returned by count(); the maximum number of items can be set with
986 setMaxCount(). You can allow editing using setEditable(). For
987 editable comboboxes you can set auto-completion using
988 setCompleter() and whether or not the user can add duplicates
989 is set with setDuplicatesEnabled().
990
991 QComboBox uses the \l{Model/View Programming}{model/view
992 framework} for its popup list and to store its items. By default
993 a QStandardItemModel stores the items and a QListView subclass
994 displays the popuplist. You can access the model and view directly
995 (with model() and view()), but QComboBox also provides functions
996 to set and get item data (e.g., setItemData() and itemText()). You
997 can also set a new model and view (with setModel() and setView()).
998 For the text and icon in the combobox label, the data in the model
999 that has the Qt::DisplayRole and Qt::DecorationRole is used. Note
1000 that you cannot alter the \l{QAbstractItemView::}{SelectionMode}
1001 of the view(), e.g., by using
1002 \l{QAbstractItemView::}{setSelectionMode()}.
1003
1004 \sa QLineEdit, QSpinBox, QRadioButton, QButtonGroup,
1005 {fowler}{GUI Design Handbook: Combo Box, Drop-Down List Box}
1006*/
1007
1008void QComboBoxPrivate::init()
1009{
1010 Q_Q(QComboBox);
1011#ifdef Q_OS_OSX
1012 // On OS X, only line edits and list views always get tab focus. It's only
1013 // when we enable full keyboard access that other controls can get tab focus.
1014 // When it's not editable, a combobox looks like a button, and it behaves as
1015 // such in this respect.
1016 if (!q->isEditable())
1017 q->setFocusPolicy(Qt::TabFocus);
1018 else
1019#endif
1020 q->setFocusPolicy(Qt::WheelFocus);
1021
1022 q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed,
1023 QSizePolicy::ComboBox));
1024 setLayoutItemMargins(QStyle::SE_ComboBoxLayoutItem);
1025 q->setModel(new QStandardItemModel(0, 1, q));
1026 if (!q->isEditable())
1027 q->setAttribute(Qt::WA_InputMethodEnabled, false);
1028 else
1029 q->setAttribute(Qt::WA_InputMethodEnabled);
1030}
1031
1032QComboBoxPrivateContainer* QComboBoxPrivate::viewContainer()
1033{
1034 if (container)
1035 return container;
1036
1037 Q_Q(QComboBox);
1038 container = new QComboBoxPrivateContainer(new QComboBoxListView(q), q);
1039 container->itemView()->setModel(model);
1040 container->itemView()->setTextElideMode(Qt::ElideMiddle);
1041 updateDelegate(true);
1042 updateLayoutDirection();
1043 updateViewContainerPaletteAndOpacity();
1044 QObject::connect(container, SIGNAL(itemSelected(QModelIndex)),
1045 q, SLOT(_q_itemSelected(QModelIndex)));
1046 QObject::connect(container->itemView()->selectionModel(),
1047 SIGNAL(currentChanged(QModelIndex,QModelIndex)),
1048 q, SLOT(_q_emitHighlighted(QModelIndex)));
1049 QObject::connect(container, SIGNAL(resetButton()), q, SLOT(_q_resetButton()));
1050 return container;
1051}
1052
1053
1054void QComboBoxPrivate::_q_resetButton()
1055{
1056 updateArrow(QStyle::State_None);
1057}
1058
1059void QComboBoxPrivate::_q_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
1060{
1061 Q_Q(QComboBox);
1062 if (inserting || topLeft.parent() != root)
1063 return;
1064
1065 if (sizeAdjustPolicy == QComboBox::AdjustToContents) {
1066 sizeHint = QSize();
1067 adjustComboBoxSize();
1068 q->updateGeometry();
1069 }
1070
1071 if (currentIndex.row() >= topLeft.row() && currentIndex.row() <= bottomRight.row()) {
1072 const QString text = q->itemText(currentIndex.row());
1073 if (lineEdit) {
1074 lineEdit->setText(text);
1075 updateLineEditGeometry();
1076 } else {
1077 emit q->currentTextChanged(text);
1078 }
1079 q->update();
1080#ifndef QT_NO_ACCESSIBILITY
1081 QAccessibleValueChangeEvent event(q, text);
1082 QAccessible::updateAccessibility(&event);
1083#endif
1084 }
1085}
1086
1087void QComboBoxPrivate::_q_rowsInserted(const QModelIndex &parent, int start, int end)
1088{
1089 Q_Q(QComboBox);
1090 if (inserting || parent != root)
1091 return;
1092
1093 if (sizeAdjustPolicy == QComboBox::AdjustToContents) {
1094 sizeHint = QSize();
1095 adjustComboBoxSize();
1096 q->updateGeometry();
1097 }
1098
1099 // set current index if combo was previously empty
1100 if (start == 0 && (end - start + 1) == q->count() && !currentIndex.isValid()) {
1101 q->setCurrentIndex(0);
1102 // need to emit changed if model updated index "silently"
1103 } else if (currentIndex.row() != indexBeforeChange) {
1104 q->update();
1105 _q_emitCurrentIndexChanged(currentIndex);
1106 }
1107}
1108
1109void QComboBoxPrivate::_q_updateIndexBeforeChange()
1110{
1111 indexBeforeChange = currentIndex.row();
1112}
1113
1114void QComboBoxPrivate::_q_rowsRemoved(const QModelIndex &parent, int /*start*/, int /*end*/)
1115{
1116 Q_Q(QComboBox);
1117 if (parent != root)
1118 return;
1119
1120 if (sizeAdjustPolicy == QComboBox::AdjustToContents) {
1121 sizeHint = QSize();
1122 adjustComboBoxSize();
1123 q->updateGeometry();
1124 }
1125
1126 // model has changed the currentIndex
1127 if (currentIndex.row() != indexBeforeChange) {
1128 if (!currentIndex.isValid() && q->count()) {
1129 q->setCurrentIndex(qMin(q->count() - 1, qMax(indexBeforeChange, 0)));
1130 return;
1131 }
1132 if (lineEdit) {
1133 lineEdit->setText(q->itemText(currentIndex.row()));
1134 updateLineEditGeometry();
1135 }
1136 q->update();
1137 _q_emitCurrentIndexChanged(currentIndex);
1138 }
1139}
1140
1141
1142void QComboBoxPrivate::updateViewContainerPaletteAndOpacity()
1143{
1144 if (!container)
1145 return;
1146 Q_Q(QComboBox);
1147 QStyleOptionComboBox opt;
1148 q->initStyleOption(&opt);
1149#if QT_CONFIG(menu)
1150 if (q->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, q)) {
1151 QMenu menu;
1152 menu.ensurePolished();
1153 container->setPalette(menu.palette());
1154 container->setWindowOpacity(menu.windowOpacity());
1155 } else
1156#endif
1157 {
1158 container->setPalette(q->palette());
1159 container->setWindowOpacity(1.0);
1160 }
1161 if (lineEdit)
1162 lineEdit->setPalette(q->palette());
1163}
1164
1165void QComboBoxPrivate::updateFocusPolicy()
1166{
1167#ifdef Q_OS_OSX
1168 Q_Q(QComboBox);
1169
1170 // See comment in QComboBoxPrivate::init()
1171 if (q->isEditable())
1172 q->setFocusPolicy(Qt::WheelFocus);
1173 else
1174 q->setFocusPolicy(Qt::TabFocus);
1175#endif
1176}
1177
1178/*!
1179 Initialize \a option with the values from this QComboBox. This method
1180 is useful for subclasses when they need a QStyleOptionComboBox, but don't want
1181 to fill in all the information themselves.
1182
1183 \sa QStyleOption::initFrom()
1184*/
1185void QComboBox::initStyleOption(QStyleOptionComboBox *option) const
1186{
1187 if (!option)
1188 return;
1189
1190 Q_D(const QComboBox);
1191 option->initFrom(this);
1192 option->editable = isEditable();
1193 option->frame = d->frame;
1194 if (hasFocus() && !option->editable)
1195 option->state |= QStyle::State_Selected;
1196 option->subControls = QStyle::SC_All;
1197 if (d->arrowState == QStyle::State_Sunken) {
1198 option->activeSubControls = QStyle::SC_ComboBoxArrow;
1199 option->state |= d->arrowState;
1200 } else {
1201 option->activeSubControls = d->hoverControl;
1202 }
1203 if (d->currentIndex.isValid()) {
1204 option->currentText = currentText();
1205 option->currentIcon = d->itemIcon(d->currentIndex);
1206 }
1207 option->iconSize = iconSize();
1208 if (d->container && d->container->isVisible())
1209 option->state |= QStyle::State_On;
1210}
1211
1212void QComboBoxPrivate::updateLineEditGeometry()
1213{
1214 if (!lineEdit)
1215 return;
1216
1217 Q_Q(QComboBox);
1218 QStyleOptionComboBox opt;
1219 q->initStyleOption(&opt);
1220 QRect editRect = q->style()->subControlRect(QStyle::CC_ComboBox, &opt,
1221 QStyle::SC_ComboBoxEditField, q);
1222 if (!q->itemIcon(q->currentIndex()).isNull()) {
1223 QRect comboRect(editRect);
1224 editRect.setWidth(editRect.width() - q->iconSize().width() - 4);
1225 editRect = QStyle::alignedRect(q->layoutDirection(), Qt::AlignRight,
1226 editRect.size(), comboRect);
1227 }
1228 lineEdit->setGeometry(editRect);
1229}
1230
1231Qt::MatchFlags QComboBoxPrivate::matchFlags() const
1232{
1233 // Base how duplicates are determined on the autocompletion case sensitivity
1234 Qt::MatchFlags flags = Qt::MatchFixedString;
1235#if QT_CONFIG(completer)
1236 if (!lineEdit->completer() || lineEdit->completer()->caseSensitivity() == Qt::CaseSensitive)
1237#endif
1238 flags |= Qt::MatchCaseSensitive;
1239 return flags;
1240}
1241
1242
1243void QComboBoxPrivate::_q_editingFinished()
1244{
1245 Q_Q(QComboBox);
1246 if (!lineEdit)
1247 return;
1248 const auto leText = lineEdit->text();
1249 if (!leText.isEmpty() && itemText(currentIndex) != leText) {
1250#if QT_CONFIG(completer)
1251 const auto *leCompleter = lineEdit->completer();
1252 const auto *popup = leCompleter ? QCompleterPrivate::get(leCompleter)->popup : nullptr;
1253 if (popup && popup->isVisible()) {
1254 // QLineEdit::editingFinished() will be emitted before the code flow returns
1255 // to QCompleter::eventFilter(), where QCompleter::activated() may be emitted.
1256 // We know that the completer popup will still be visible at this point, and
1257 // that any selection should be valid.
1258 const QItemSelectionModel *selModel = popup->selectionModel();
1259 const QModelIndex curIndex = popup->currentIndex();
1260 const bool completerIsActive = selModel && selModel->selectedIndexes().contains(curIndex);
1261
1262 if (completerIsActive)
1263 return;
1264 }
1265#endif
1266 const int index = q_func()->findText(leText, matchFlags());
1267 if (index != -1) {
1268 q->setCurrentIndex(index);
1269 emitActivated(currentIndex);
1270 }
1271 }
1272
1273}
1274
1275void QComboBoxPrivate::_q_returnPressed()
1276{
1277 Q_Q(QComboBox);
1278
1279 // The insertion code below does not apply when the policy is QComboBox::NoInsert.
1280 // In case a completer is installed, item activation via the completer is handled
1281 // in _q_completerActivated(). Otherwise _q_editingFinished() updates the current
1282 // index as appropriate.
1283 if (insertPolicy == QComboBox::NoInsert)
1284 return;
1285
1286 if (lineEdit && !lineEdit->text().isEmpty()) {
1287 if (q->count() >= maxCount && !(this->insertPolicy == QComboBox::InsertAtCurrent))
1288 return;
1289 lineEdit->deselect();
1290 lineEdit->end(false);
1291 QString text = lineEdit->text();
1292 // check for duplicates (if not enabled) and quit
1293 int index = -1;
1294 if (!duplicatesEnabled) {
1295 index = q->findText(text, matchFlags());
1296 if (index != -1) {
1297 q->setCurrentIndex(index);
1298 emitActivated(currentIndex);
1299 return;
1300 }
1301 }
1302 switch (insertPolicy) {
1303 case QComboBox::InsertAtTop:
1304 index = 0;
1305 break;
1306 case QComboBox::InsertAtBottom:
1307 index = q->count();
1308 break;
1309 case QComboBox::InsertAtCurrent:
1310 case QComboBox::InsertAfterCurrent:
1311 case QComboBox::InsertBeforeCurrent:
1312 if (!q->count() || !currentIndex.isValid())
1313 index = 0;
1314 else if (insertPolicy == QComboBox::InsertAtCurrent)
1315 q->setItemText(q->currentIndex(), text);
1316 else if (insertPolicy == QComboBox::InsertAfterCurrent)
1317 index = q->currentIndex() + 1;
1318 else if (insertPolicy == QComboBox::InsertBeforeCurrent)
1319 index = q->currentIndex();
1320 break;
1321 case QComboBox::InsertAlphabetically:
1322 index = 0;
1323 for (int i=0; i< q->count(); i++, index++ ) {
1324 if (text.toLower() < q->itemText(i).toLower())
1325 break;
1326 }
1327 break;
1328 default:
1329 break;
1330 }
1331 if (index >= 0) {
1332 q->insertItem(index, text);
1333 q->setCurrentIndex(index);
1334 emitActivated(currentIndex);
1335 }
1336 }
1337}
1338
1339void QComboBoxPrivate::_q_itemSelected(const QModelIndex &item)
1340{
1341 Q_Q(QComboBox);
1342 if (item != currentIndex) {
1343 setCurrentIndex(item);
1344 } else if (lineEdit) {
1345 lineEdit->selectAll();
1346 lineEdit->setText(q->itemText(currentIndex.row()));
1347 }
1348 emitActivated(currentIndex);
1349}
1350
1351void QComboBoxPrivate::emitActivated(const QModelIndex &index)
1352{
1353 Q_Q(QComboBox);
1354 if (!index.isValid())
1355 return;
1356 QString text(itemText(index));
1357 emit q->activated(index.row());
1358 emit q->textActivated(text);
1359#if QT_DEPRECATED_SINCE(5, 15)
1360QT_WARNING_PUSH
1361QT_WARNING_DISABLE_DEPRECATED
1362 emit q->activated(text);
1363QT_WARNING_POP
1364#endif
1365}
1366
1367void QComboBoxPrivate::_q_emitHighlighted(const QModelIndex &index)
1368{
1369 Q_Q(QComboBox);
1370 if (!index.isValid())
1371 return;
1372 QString text(itemText(index));
1373 emit q->highlighted(index.row());
1374 emit q->textHighlighted(text);
1375#if QT_DEPRECATED_SINCE(5, 15)
1376QT_WARNING_PUSH
1377QT_WARNING_DISABLE_DEPRECATED
1378 emit q->highlighted(text);
1379QT_WARNING_POP
1380#endif
1381}
1382
1383void QComboBoxPrivate::_q_emitCurrentIndexChanged(const QModelIndex &index)
1384{
1385 Q_Q(QComboBox);
1386 const QString text = itemText(index);
1387 emit q->currentIndexChanged(index.row());
1388#if QT_DEPRECATED_SINCE(5, 13)
1389 QT_WARNING_PUSH
1390 QT_WARNING_DISABLE_DEPRECATED
1391 emit q->currentIndexChanged(text);
1392 QT_WARNING_POP
1393#endif
1394 // signal lineEdit.textChanged already connected to signal currentTextChanged, so don't emit double here
1395 if (!lineEdit)
1396 emit q->currentTextChanged(text);
1397#ifndef QT_NO_ACCESSIBILITY
1398 QAccessibleValueChangeEvent event(q, text);
1399 QAccessible::updateAccessibility(&event);
1400#endif
1401}
1402
1403QString QComboBoxPrivate::itemText(const QModelIndex &index) const
1404{
1405 return index.isValid() ? model->data(index, itemRole()).toString() : QString();
1406}
1407
1408int QComboBoxPrivate::itemRole() const
1409{
1410 return q_func()->isEditable() ? Qt::EditRole : Qt::DisplayRole;
1411}
1412
1413/*!
1414 Destroys the combobox.
1415*/
1416QComboBox::~QComboBox()
1417{
1418 // ### check delegateparent and delete delegate if us?
1419 Q_D(QComboBox);
1420
1421 QT_TRY {
1422 disconnect(d->model, SIGNAL(destroyed()),
1423 this, SLOT(_q_modelDestroyed()));
1424 } QT_CATCH(...) {
1425 ; // objects can't throw in destructor
1426 }
1427}
1428
1429/*!
1430 \property QComboBox::maxVisibleItems
1431 \brief the maximum allowed size on screen of the combo box, measured in items
1432
1433 By default, this property has a value of 10.
1434
1435 \note This property is ignored for non-editable comboboxes in styles that returns
1436 true for QStyle::SH_ComboBox_Popup such as the Mac style or the Gtk+ Style.
1437*/
1438int QComboBox::maxVisibleItems() const
1439{
1440 Q_D(const QComboBox);
1441 return d->maxVisibleItems;
1442}
1443
1444void QComboBox::setMaxVisibleItems(int maxItems)
1445{
1446 Q_D(QComboBox);
1447 if (Q_UNLIKELY(maxItems < 0)) {
1448 qWarning("QComboBox::setMaxVisibleItems: "
1449 "Invalid max visible items (%d) must be >= 0", maxItems);
1450 return;
1451 }
1452 d->maxVisibleItems = maxItems;
1453}
1454
1455/*!
1456 \property QComboBox::count
1457 \brief the number of items in the combobox
1458
1459 By default, for an empty combo box, this property has a value of 0.
1460*/
1461int QComboBox::count() const
1462{
1463 Q_D(const QComboBox);
1464 return d->model->rowCount(d->root);
1465}
1466
1467/*!
1468 \property QComboBox::maxCount
1469 \brief the maximum number of items allowed in the combobox
1470
1471 \note If you set the maximum number to be less then the current
1472 amount of items in the combobox, the extra items will be
1473 truncated. This also applies if you have set an external model on
1474 the combobox.
1475
1476 By default, this property's value is derived from the highest
1477 signed integer available (typically 2147483647).
1478*/
1479void QComboBox::setMaxCount(int max)
1480{
1481 Q_D(QComboBox);
1482 if (Q_UNLIKELY(max < 0)) {
1483 qWarning("QComboBox::setMaxCount: Invalid count (%d) must be >= 0", max);
1484 return;
1485 }
1486
1487 const int rowCount = count();
1488 if (rowCount > max)
1489 d->model->removeRows(max, rowCount - max, d->root);
1490
1491 d->maxCount = max;
1492}
1493
1494int QComboBox::maxCount() const
1495{
1496 Q_D(const QComboBox);
1497 return d->maxCount;
1498}
1499
1500#if QT_CONFIG(completer)
1501#if QT_DEPRECATED_SINCE(5, 13)
1502
1503/*!
1504 \property QComboBox::autoCompletion
1505 \brief whether the combobox provides auto-completion for editable items
1506 \since 4.1
1507 \obsolete
1508
1509 Use setCompleter() instead.
1510
1511 By default, this property is \c true.
1512
1513 \sa editable
1514*/
1515
1516/*!
1517 \obsolete
1518
1519 Use completer() instead.
1520*/
1521bool QComboBox::autoCompletion() const
1522{
1523 Q_D(const QComboBox);
1524 return d->autoCompletion;
1525}
1526
1527/*!
1528 \obsolete
1529
1530 Use setCompleter() instead.
1531*/
1532void QComboBox::setAutoCompletion(bool enable)
1533{
1534 Q_D(QComboBox);
1535
1536#ifdef QT_KEYPAD_NAVIGATION
1537 if (Q_UNLIKELY(QApplicationPrivate::keypadNavigationEnabled() && !enable && isEditable()))
1538 qWarning("QComboBox::setAutoCompletion: auto completion is mandatory when combo box editable");
1539#endif
1540
1541 d->autoCompletion = enable;
1542 if (!d->lineEdit)
1543 return;
1544 if (enable) {
1545 if (d->lineEdit->completer())
1546 return;
1547 d->completer = new QCompleter(d->model, d->lineEdit);
1548 connect(d->completer, SIGNAL(activated(QModelIndex)), this, SLOT(_q_completerActivated(QModelIndex)));
1549 d->completer->setCaseSensitivity(d->autoCompletionCaseSensitivity);
1550 d->completer->setCompletionMode(QCompleter::InlineCompletion);
1551 d->completer->setCompletionColumn(d->modelColumn);
1552 d->lineEdit->setCompleter(d->completer);
1553 d->completer->setWidget(this);
1554 } else {
1555 d->lineEdit->setCompleter(0);
1556 }
1557}
1558
1559/*!
1560 \property QComboBox::autoCompletionCaseSensitivity
1561 \brief whether string comparisons are case-sensitive or case-insensitive for auto-completion
1562 \obsolete
1563
1564 By default, this property is Qt::CaseInsensitive.
1565
1566 Use setCompleter() instead. Case sensitivity of the auto completion can be
1567 changed using QCompleter::setCaseSensitivity().
1568
1569 \sa autoCompletion
1570*/
1571
1572/*!
1573 \obsolete
1574
1575 Use setCompleter() and QCompleter::setCaseSensitivity() instead.
1576*/
1577Qt::CaseSensitivity QComboBox::autoCompletionCaseSensitivity() const
1578{
1579 Q_D(const QComboBox);
1580 return d->autoCompletionCaseSensitivity;
1581}
1582
1583/*!
1584 \obsolete
1585
1586 Use setCompleter() and QCompleter::setCaseSensitivity() instead.
1587*/
1588void QComboBox::setAutoCompletionCaseSensitivity(Qt::CaseSensitivity sensitivity)
1589{
1590 Q_D(QComboBox);
1591 d->autoCompletionCaseSensitivity = sensitivity;
1592 if (d->lineEdit && d->lineEdit->completer())
1593 d->lineEdit->completer()->setCaseSensitivity(sensitivity);
1594}
1595#endif // QT_DEPRECATED_SINCE(5, 13)
1596
1597#endif // QT_CONFIG(completer)
1598
1599/*!
1600 \property QComboBox::duplicatesEnabled
1601 \brief whether the user can enter duplicate items into the combobox
1602
1603 Note that it is always possible to programmatically insert duplicate items into the
1604 combobox.
1605
1606 By default, this property is \c false (duplicates are not allowed).
1607*/
1608bool QComboBox::duplicatesEnabled() const
1609{
1610 Q_D(const QComboBox);
1611 return d->duplicatesEnabled;
1612}
1613
1614void QComboBox::setDuplicatesEnabled(bool enable)
1615{
1616 Q_D(QComboBox);
1617 d->duplicatesEnabled = enable;
1618}
1619
1620/*! \fn int QComboBox::findText(const QString &text, Qt::MatchFlags flags = Qt::MatchExactly|Qt::MatchCaseSensitive) const
1621
1622 Returns the index of the item containing the given \a text; otherwise
1623 returns -1.
1624
1625 The \a flags specify how the items in the combobox are searched.
1626*/
1627
1628/*!
1629 Returns the index of the item containing the given \a data for the
1630 given \a role; otherwise returns -1.
1631
1632 The \a flags specify how the items in the combobox are searched.
1633*/
1634int QComboBox::findData(const QVariant &data, int role, Qt::MatchFlags flags) const
1635{
1636 Q_D(const QComboBox);
1637 QModelIndex start = d->model->index(0, d->modelColumn, d->root);
1638 const QModelIndexList result = d->model->match(start, role, data, 1, flags);
1639 if (result.isEmpty())
1640 return -1;
1641 return result.first().row();
1642}
1643
1644/*!
1645 \property QComboBox::insertPolicy
1646 \brief the policy used to determine where user-inserted items should
1647 appear in the combobox
1648
1649 The default value is \l InsertAtBottom, indicating that new items will appear
1650 at the bottom of the list of items.
1651
1652 \sa InsertPolicy
1653*/
1654
1655QComboBox::InsertPolicy QComboBox::insertPolicy() const
1656{
1657 Q_D(const QComboBox);
1658 return d->insertPolicy;
1659}
1660
1661void QComboBox::setInsertPolicy(InsertPolicy policy)
1662{
1663 Q_D(QComboBox);
1664 d->insertPolicy = policy;
1665}
1666
1667/*!
1668 \property QComboBox::sizeAdjustPolicy
1669 \brief the policy describing how the size of the combobox changes
1670 when the content changes
1671
1672 The default value is \l AdjustToContentsOnFirstShow.
1673
1674 \sa SizeAdjustPolicy
1675*/
1676
1677QComboBox::SizeAdjustPolicy QComboBox::sizeAdjustPolicy() const
1678{
1679 Q_D(const QComboBox);
1680 return d->sizeAdjustPolicy;
1681}
1682
1683void QComboBox::setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy policy)
1684{
1685 Q_D(QComboBox);
1686 if (policy == d->sizeAdjustPolicy)
1687 return;
1688
1689 d->sizeAdjustPolicy = policy;
1690 d->sizeHint = QSize();
1691 d->adjustComboBoxSize();
1692 updateGeometry();
1693}
1694
1695/*!
1696 \property QComboBox::minimumContentsLength
1697 \brief the minimum number of characters that should fit into the combobox.
1698
1699 The default value is 0.
1700
1701 If this property is set to a positive value, the
1702 minimumSizeHint() and sizeHint() take it into account.
1703
1704 \sa sizeAdjustPolicy
1705*/
1706int QComboBox::minimumContentsLength() const
1707{
1708 Q_D(const QComboBox);
1709 return d->minimumContentsLength;
1710}
1711
1712void QComboBox::setMinimumContentsLength(int characters)
1713{
1714 Q_D(QComboBox);
1715 if (characters == d->minimumContentsLength || characters < 0)
1716 return;
1717
1718 d->minimumContentsLength = characters;
1719
1720 if (d->sizeAdjustPolicy == AdjustToContents
1721 || d->sizeAdjustPolicy == AdjustToMinimumContentsLength
1722 || d->sizeAdjustPolicy == AdjustToMinimumContentsLengthWithIcon) {
1723 d->sizeHint = QSize();
1724 d->adjustComboBoxSize();
1725 updateGeometry();
1726 }
1727}
1728
1729/*!
1730 \property QComboBox::iconSize
1731 \brief the size of the icons shown in the combobox.
1732
1733 Unless explicitly set this returns the default value of the
1734 current style. This size is the maximum size that icons can have;
1735 icons of smaller size are not scaled up.
1736*/
1737
1738QSize QComboBox::iconSize() const
1739{
1740 Q_D(const QComboBox);
1741 if (d->iconSize.isValid())
1742 return d->iconSize;
1743
1744 int iconWidth = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this);
1745 return QSize(iconWidth, iconWidth);
1746}
1747
1748void QComboBox::setIconSize(const QSize &size)
1749{
1750 Q_D(QComboBox);
1751 if (size == d->iconSize)
1752 return;
1753
1754 view()->setIconSize(size);
1755 d->iconSize = size;
1756 d->sizeHint = QSize();
1757 updateGeometry();
1758}
1759
1760/*!
1761 \property QComboBox::editable
1762 \brief whether the combo box can be edited by the user
1763
1764 By default, this property is \c false. The effect of editing depends
1765 on the insert policy.
1766
1767 \note When disabling the \a editable state, the validator and
1768 completer are removed.
1769
1770 \sa InsertPolicy
1771*/
1772bool QComboBox::isEditable() const
1773{
1774 Q_D(const QComboBox);
1775 return d->lineEdit != 0;
1776}
1777
1778/*! \internal
1779 update the default delegate
1780 depending on the style's SH_ComboBox_Popup hint, we use a different default delegate.
1781
1782 but we do not change the delegate is the combobox use a custom delegate,
1783 unless \a force is set to true.
1784 */
1785void QComboBoxPrivate::updateDelegate(bool force)
1786{
1787 Q_Q(QComboBox);
1788 QStyleOptionComboBox opt;
1789 q->initStyleOption(&opt);
1790 if (q->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, q)) {
1791 if (force || qobject_cast<QComboBoxDelegate *>(q->itemDelegate()))
1792 q->setItemDelegate(new QComboMenuDelegate(q->view(), q));
1793 } else {
1794 if (force || qobject_cast<QComboMenuDelegate *>(q->itemDelegate()))
1795 q->setItemDelegate(new QComboBoxDelegate(q->view(), q));
1796 }
1797}
1798
1799QIcon QComboBoxPrivate::itemIcon(const QModelIndex &index) const
1800{
1801 QVariant decoration = model->data(index, Qt::DecorationRole);
1802 if (decoration.type() == QVariant::Pixmap)
1803 return QIcon(qvariant_cast<QPixmap>(decoration));
1804 else
1805 return qvariant_cast<QIcon>(decoration);
1806}
1807
1808void QComboBox::setEditable(bool editable)
1809{
1810 Q_D(QComboBox);
1811 if (isEditable() == editable)
1812 return;
1813
1814 QStyleOptionComboBox opt;
1815 initStyleOption(&opt);
1816 if (editable) {
1817 if (style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this)) {
1818 d->viewContainer()->updateScrollers();
1819 view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
1820 }
1821 QLineEdit *le = new QLineEdit(this);
1822 setLineEdit(le);
1823 } else {
1824 if (style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this)) {
1825 d->viewContainer()->updateScrollers();
1826 view()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1827 }
1828 setAttribute(Qt::WA_InputMethodEnabled, false);
1829 d->lineEdit->hide();
1830 d->lineEdit->deleteLater();
1831 d->lineEdit = 0;
1832 }
1833
1834 d->updateDelegate();
1835 d->updateFocusPolicy();
1836
1837 d->viewContainer()->updateTopBottomMargin();
1838 if (!testAttribute(Qt::WA_Resized))
1839 adjustSize();
1840}
1841
1842/*!
1843 Sets the line \a edit to use instead of the current line edit widget.
1844
1845 The combo box takes ownership of the line edit.
1846*/
1847void QComboBox::setLineEdit(QLineEdit *edit)
1848{
1849 Q_D(QComboBox);
1850 if (Q_UNLIKELY(!edit)) {
1851 qWarning("QComboBox::setLineEdit: cannot set a 0 line edit");
1852 return;
1853 }
1854
1855 if (edit == d->lineEdit)
1856 return;
1857
1858 edit->setText(currentText());
1859 delete d->lineEdit;
1860
1861 d->lineEdit = edit;
1862#ifndef QT_NO_IM
1863 qt_widget_private(d->lineEdit)->inheritsInputMethodHints = 1;
1864#endif
1865 if (d->lineEdit->parent() != this)
1866 d->lineEdit->setParent(this);
1867 connect(d->lineEdit, SIGNAL(returnPressed()), this, SLOT(_q_returnPressed()));
1868 connect(d->lineEdit, SIGNAL(editingFinished()), this, SLOT(_q_editingFinished()));
1869 connect(d->lineEdit, SIGNAL(textChanged(QString)), this, SIGNAL(editTextChanged(QString)));
1870 connect(d->lineEdit, SIGNAL(textChanged(QString)), this, SIGNAL(currentTextChanged(QString)));
1871 connect(d->lineEdit, SIGNAL(cursorPositionChanged(int,int)), this, SLOT(updateMicroFocus()));
1872 connect(d->lineEdit, SIGNAL(selectionChanged()), this, SLOT(updateMicroFocus()));
1873 connect(d->lineEdit->d_func()->control, SIGNAL(updateMicroFocus()), this, SLOT(updateMicroFocus()));
1874 d->lineEdit->setFrame(false);
1875 d->lineEdit->setContextMenuPolicy(Qt::NoContextMenu);
1876 d->updateFocusPolicy();
1877 d->lineEdit->setFocusProxy(this);
1878 d->lineEdit->setAttribute(Qt::WA_MacShowFocusRect, false);
1879#if QT_DEPRECATED_SINCE(5, 13)
1880#if QT_CONFIG(completer)
1881 setAutoCompletion(d->autoCompletion);
1882
1883#ifdef QT_KEYPAD_NAVIGATION
1884 if (QApplicationPrivate::keypadNavigationEnabled()) {
1885 // Editable combo boxes will have a completer that is set to UnfilteredPopupCompletion.
1886 // This means that when the user enters edit mode they are immediately presented with a
1887 // list of possible completions.
1888 setAutoCompletion(true);
1889 if (d->completer) {
1890 d->completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion);
1891 connect(d->completer, SIGNAL(activated(QModelIndex)), this, SLOT(_q_completerActivated()));
1892 }
1893 }
1894#endif
1895#endif
1896#endif
1897
1898 setAttribute(Qt::WA_InputMethodEnabled);
1899 d->updateLayoutDirection();
1900 d->updateLineEditGeometry();
1901 if (isVisible())
1902 d->lineEdit->show();
1903
1904 update();
1905}
1906
1907/*!
1908 Returns the line edit used to edit items in the combobox, or
1909 \nullptr if there is no line edit.
1910
1911 Only editable combo boxes have a line edit.
1912*/
1913QLineEdit *QComboBox::lineEdit() const
1914{
1915 Q_D(const QComboBox);
1916 return d->lineEdit;
1917}
1918
1919#ifndef QT_NO_VALIDATOR
1920/*!
1921 \fn void QComboBox::setValidator(const QValidator *validator)
1922
1923 Sets the \a validator to use instead of the current validator.
1924
1925 \note The validator is removed when the \l editable property becomes \c false.
1926*/
1927
1928void QComboBox::setValidator(const QValidator *v)
1929{
1930 Q_D(QComboBox);
1931 if (d->lineEdit)
1932 d->lineEdit->setValidator(v);
1933}
1934
1935/*!
1936 Returns the validator that is used to constrain text input for the
1937 combobox.
1938
1939 \sa editable
1940*/
1941const QValidator *QComboBox::validator() const
1942{
1943 Q_D(const QComboBox);
1944 return d->lineEdit ? d->lineEdit->validator() : 0;
1945}
1946#endif // QT_NO_VALIDATOR
1947
1948#if QT_CONFIG(completer)
1949
1950/*!
1951 \fn void QComboBox::setCompleter(QCompleter *completer)
1952 \since 4.2
1953
1954 Sets the \a completer to use instead of the current completer.
1955 If \a completer is 0, auto completion is disabled.
1956
1957 By default, for an editable combo box, a QCompleter that
1958 performs case insensitive inline completion is automatically created.
1959
1960 \note The completer is removed when the \l editable property becomes \c false.
1961 Setting a completer on a QComboBox that is not editable will be ignored.
1962*/
1963void QComboBox::setCompleter(QCompleter *c)
1964{
1965 Q_D(QComboBox);
1966 if (!d->lineEdit) {
1967 qWarning("Setting a QCompleter on non-editable QComboBox is not allowed.");
1968 return;
1969 }
1970 d->lineEdit->setCompleter(c);
1971 if (c) {
1972 connect(c, SIGNAL(activated(QModelIndex)), this, SLOT(_q_completerActivated(QModelIndex)));
1973 c->setWidget(this);
1974 }
1975}
1976
1977/*!
1978 \since 4.2
1979
1980 Returns the completer that is used to auto complete text input for the
1981 combobox.
1982
1983 \sa editable
1984*/
1985QCompleter *QComboBox::completer() const
1986{
1987 Q_D(const QComboBox);
1988 return d->lineEdit ? d->lineEdit->completer() : 0;
1989}
1990
1991#endif // QT_CONFIG(completer)
1992
1993/*!
1994 Returns the item delegate used by the popup list view.
1995
1996 \sa setItemDelegate()
1997*/
1998QAbstractItemDelegate *QComboBox::itemDelegate() const
1999{
2000 return view()->itemDelegate();
2001}
2002
2003/*!
2004 Sets the item \a delegate for the popup list view.
2005 The combobox takes ownership of the delegate.
2006
2007 \warning You should not share the same instance of a delegate between comboboxes,
2008 widget mappers or views. Doing so can cause incorrect or unintuitive editing behavior
2009 since each view connected to a given delegate may receive the
2010 \l{QAbstractItemDelegate::}{closeEditor()} signal, and attempt to access, modify or
2011 close an editor that has already been closed.
2012
2013 \sa itemDelegate()
2014*/
2015void QComboBox::setItemDelegate(QAbstractItemDelegate *delegate)
2016{
2017 if (Q_UNLIKELY(!delegate)) {
2018 qWarning("QComboBox::setItemDelegate: cannot set a 0 delegate");
2019 return;
2020 }
2021 delete view()->itemDelegate();
2022 view()->setItemDelegate(delegate);
2023}
2024
2025/*!
2026 Returns the model used by the combobox.
2027*/
2028
2029QAbstractItemModel *QComboBox::model() const
2030{
2031 Q_D(const QComboBox);
2032 if (d->model == QAbstractItemModelPrivate::staticEmptyModel()) {
2033 QComboBox *that = const_cast<QComboBox*>(this);
2034 that->setModel(new QStandardItemModel(0, 1, that));
2035 }
2036 return d->model;
2037}
2038
2039/*!
2040 Sets the model to be \a model. \a model must not be \nullptr.
2041 If you want to clear the contents of a model, call clear().
2042
2043 \sa clear()
2044*/
2045void QComboBox::setModel(QAbstractItemModel *model)
2046{
2047 Q_D(QComboBox);
2048
2049 if (Q_UNLIKELY(!model)) {
2050 qWarning("QComboBox::setModel: cannot set a 0 model");
2051 return;
2052 }
2053
2054 if (model == d->model)
2055 return;
2056
2057#if QT_CONFIG(completer)
2058 if (d->lineEdit && d->lineEdit->completer()
2059 && d->lineEdit->completer() == d->completer)
2060 d->lineEdit->completer()->setModel(model);
2061#endif
2062 if (d->model) {
2063 disconnect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
2064 this, SLOT(_q_dataChanged(QModelIndex,QModelIndex)));
2065 disconnect(d->model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
2066 this, SLOT(_q_updateIndexBeforeChange()));
2067 disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
2068 this, SLOT(_q_rowsInserted(QModelIndex,int,int)));
2069 disconnect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
2070 this, SLOT(_q_updateIndexBeforeChange()));
2071 disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
2072 this, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
2073 disconnect(d->model, SIGNAL(destroyed()),
2074 this, SLOT(_q_modelDestroyed()));
2075 disconnect(d->model, SIGNAL(modelAboutToBeReset()),
2076 this, SLOT(_q_updateIndexBeforeChange()));
2077 disconnect(d->model, SIGNAL(modelReset()),
2078 this, SLOT(_q_modelReset()));
2079 if (d->model->QObject::parent() == this)
2080 delete d->model;
2081 }
2082
2083 d->model = model;
2084
2085 connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
2086 this, SLOT(_q_dataChanged(QModelIndex,QModelIndex)));
2087 connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
2088 this, SLOT(_q_updateIndexBeforeChange()));
2089 connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
2090 this, SLOT(_q_rowsInserted(QModelIndex,int,int)));
2091 connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
2092 this, SLOT(_q_updateIndexBeforeChange()));
2093 connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
2094 this, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
2095 connect(model, SIGNAL(destroyed()),
2096 this, SLOT(_q_modelDestroyed()));
2097 connect(model, SIGNAL(modelAboutToBeReset()),
2098 this, SLOT(_q_updateIndexBeforeChange()));
2099 connect(model, SIGNAL(modelReset()),
2100 this, SLOT(_q_modelReset()));
2101
2102 if (d->container) {
2103 d->container->itemView()->setModel(model);
2104 connect(d->container->itemView()->selectionModel(),
2105 SIGNAL(currentChanged(QModelIndex,QModelIndex)),
2106 this, SLOT(_q_emitHighlighted(QModelIndex)), Qt::UniqueConnection);
2107 }
2108
2109 setRootModelIndex(QModelIndex());
2110
2111 bool currentReset = false;
2112
2113 const int rowCount = count();
2114 for (int pos=0; pos < rowCount; pos++) {
2115 if (d->model->index(pos, d->modelColumn, d->root).flags() & Qt::ItemIsEnabled) {
2116 setCurrentIndex(pos);
2117 currentReset = true;
2118 break;
2119 }
2120 }
2121
2122 if (!currentReset)
2123 setCurrentIndex(-1);
2124
2125 d->modelChanged();
2126}
2127
2128/*!
2129 Returns the root model item index for the items in the combobox.
2130
2131 \sa setRootModelIndex()
2132*/
2133
2134QModelIndex QComboBox::rootModelIndex() const
2135{
2136 Q_D(const QComboBox);
2137 return QModelIndex(d->root);
2138}
2139
2140/*!
2141 Sets the root model item \a index for the items in the combobox.
2142
2143 \sa rootModelIndex()
2144*/
2145void QComboBox::setRootModelIndex(const QModelIndex &index)
2146{
2147 Q_D(QComboBox);
2148 if (d->root == index)
2149 return;
2150 d->root = QPersistentModelIndex(index);
2151 view()->setRootIndex(index);
2152 update();
2153}
2154
2155/*!
2156 \property QComboBox::currentIndex
2157 \brief the index of the current item in the combobox.
2158
2159 The current index can change when inserting or removing items.
2160
2161 By default, for an empty combo box or a combo box in which no current
2162 item is set, this property has a value of -1.
2163*/
2164int QComboBox::currentIndex() const
2165{
2166 Q_D(const QComboBox);
2167 return d->currentIndex.row();
2168}
2169
2170void QComboBox::setCurrentIndex(int index)
2171{
2172 Q_D(QComboBox);
2173 QModelIndex mi = d->model->index(index, d->modelColumn, d->root);
2174 d->setCurrentIndex(mi);
2175}
2176
2177void QComboBox::setCurrentText(const QString &text)
2178{
2179 if (isEditable()) {
2180 setEditText(text);
2181 } else {
2182 const int i = findText(text);
2183 if (i > -1)
2184 setCurrentIndex(i);
2185 }
2186}
2187
2188void QComboBoxPrivate::setCurrentIndex(const QModelIndex &mi)
2189{
2190 Q_Q(QComboBox);
2191
2192 QModelIndex normalized = mi.sibling(mi.row(), modelColumn); // no-op if mi.column() == modelColumn
2193 if (!normalized.isValid())
2194 normalized = mi; // Fallback to passed index.
2195
2196 bool indexChanged = (normalized != currentIndex);
2197 if (indexChanged)
2198 currentIndex = QPersistentModelIndex(normalized);
2199 if (lineEdit) {
2200 const QString newText = itemText(normalized);
2201 if (lineEdit->text() != newText) {
2202 lineEdit->setText(newText); // may cause lineEdit -> nullptr (QTBUG-54191)
2203#if QT_CONFIG(completer)
2204 if (lineEdit && lineEdit->completer())
2205 lineEdit->completer()->setCompletionPrefix(newText);
2206#endif
2207 }
2208 updateLineEditGeometry();
2209 }
2210 if (indexChanged) {
2211 q->update();
2212 _q_emitCurrentIndexChanged(currentIndex);
2213 }
2214}
2215
2216/*!
2217 \property QComboBox::currentText
2218 \brief the current text
2219
2220 If the combo box is editable, the current text is the value displayed
2221 by the line edit. Otherwise, it is the value of the current item or
2222 an empty string if the combo box is empty or no current item is set.
2223
2224 The setter setCurrentText() simply calls setEditText() if the combo box is editable.
2225 Otherwise, if there is a matching text in the list, currentIndex is set to the
2226 corresponding index.
2227
2228 \sa editable, setEditText()
2229*/
2230QString QComboBox::currentText() const
2231{
2232 Q_D(const QComboBox);
2233 if (d->lineEdit)
2234 return d->lineEdit->text();
2235 else if (d->currentIndex.isValid())
2236 return d->itemText(d->currentIndex);
2237 else
2238 return QString();
2239}
2240
2241/*!
2242 \property QComboBox::currentData
2243 \brief the data for the current item
2244 \since 5.2
2245
2246 By default, for an empty combo box or a combo box in which no current
2247 item is set, this property contains an invalid QVariant.
2248*/
2249QVariant QComboBox::currentData(int role) const
2250{
2251 Q_D(const QComboBox);
2252 return d->currentIndex.data(role);
2253}
2254
2255/*!
2256 Returns the text for the given \a index in the combobox.
2257*/
2258QString QComboBox::itemText(int index) const
2259{
2260 Q_D(const QComboBox);
2261 QModelIndex mi = d->model->index(index, d->modelColumn, d->root);
2262 return d->itemText(mi);
2263}
2264
2265/*!
2266 Returns the icon for the given \a index in the combobox.
2267*/
2268QIcon QComboBox::itemIcon(int index) const
2269{
2270 Q_D(const QComboBox);
2271 QModelIndex mi = d->model->index(index, d->modelColumn, d->root);
2272 return d->itemIcon(mi);
2273}
2274
2275/*!
2276 Returns the data for the given \a role in the given \a index in the
2277 combobox, or QVariant::Invalid if there is no data for this role.
2278*/
2279QVariant QComboBox::itemData(int index, int role) const
2280{
2281 Q_D(const QComboBox);
2282 QModelIndex mi = d->model->index(index, d->modelColumn, d->root);
2283 return d->model->data(mi, role);
2284}
2285
2286/*!
2287 \fn void QComboBox::insertItem(int index, const QString &text, const QVariant &userData)
2288
2289 Inserts the \a text and \a userData (stored in the Qt::UserRole)
2290 into the combobox at the given \a index.
2291
2292 If the index is equal to or higher than the total number of items,
2293 the new item is appended to the list of existing items. If the
2294 index is zero or negative, the new item is prepended to the list
2295 of existing items.
2296
2297 \sa insertItems()
2298*/
2299
2300/*!
2301
2302 Inserts the \a icon, \a text and \a userData (stored in the
2303 Qt::UserRole) into the combobox at the given \a index.
2304
2305 If the index is equal to or higher than the total number of items,
2306 the new item is appended to the list of existing items. If the
2307 index is zero or negative, the new item is prepended to the list
2308 of existing items.
2309
2310 \sa insertItems()
2311*/
2312void QComboBox::insertItem(int index, const QIcon &icon, const QString &text, const QVariant &userData)
2313{
2314 Q_D(QComboBox);
2315 int itemCount = count();
2316 index = qBound(0, index, itemCount);
2317 if (index >= d->maxCount)
2318 return;
2319
2320 // For the common case where we are using the built in QStandardItemModel
2321 // construct a QStandardItem, reducing the number of expensive signals from the model
2322 if (QStandardItemModel *m = qobject_cast<QStandardItemModel*>(d->model)) {
2323 QStandardItem *item = new QStandardItem(text);
2324 if (!icon.isNull()) item->setData(icon, Qt::DecorationRole);
2325 if (userData.isValid()) item->setData(userData, Qt::UserRole);
2326 m->insertRow(index, item);
2327 ++itemCount;
2328 } else {
2329 d->inserting = true;
2330 if (d->model->insertRows(index, 1, d->root)) {
2331 QModelIndex item = d->model->index(index, d->modelColumn, d->root);
2332 if (icon.isNull() && !userData.isValid()) {
2333 d->model->setData(item, text, Qt::EditRole);
2334 } else {
2335 QMap<int, QVariant> values;
2336 if (!text.isNull()) values.insert(Qt::EditRole, text);
2337 if (!icon.isNull()) values.insert(Qt::DecorationRole, icon);
2338 if (userData.isValid()) values.insert(Qt::UserRole, userData);
2339 if (!values.isEmpty()) d->model->setItemData(item, values);
2340 }
2341 d->inserting = false;
2342 d->_q_rowsInserted(d->root, index, index);
2343 ++itemCount;
2344 } else {
2345 d->inserting = false;
2346 }
2347 }
2348
2349 if (itemCount > d->maxCount)
2350 d->model->removeRows(itemCount - 1, itemCount - d->maxCount, d->root);
2351}
2352
2353/*!
2354 Inserts the strings from the \a list into the combobox as separate items,
2355 starting at the \a index specified.
2356
2357 If the index is equal to or higher than the total number of items, the new items
2358 are appended to the list of existing items. If the index is zero or negative, the
2359 new items are prepended to the list of existing items.
2360
2361 \sa insertItem()
2362 */
2363void QComboBox::insertItems(int index, const QStringList &list)
2364{
2365 Q_D(QComboBox);
2366 if (list.isEmpty())
2367 return;
2368 index = qBound(0, index, count());
2369 int insertCount = qMin(d->maxCount - index, list.count());
2370 if (insertCount <= 0)
2371 return;
2372 // For the common case where we are using the built in QStandardItemModel
2373 // construct a QStandardItem, reducing the number of expensive signals from the model
2374 if (QStandardItemModel *m = qobject_cast<QStandardItemModel*>(d->model)) {
2375 QList<QStandardItem *> items;
2376 items.reserve(insertCount);
2377 QStandardItem *hiddenRoot = m->invisibleRootItem();
2378 for (int i = 0; i < insertCount; ++i)
2379 items.append(new QStandardItem(list.at(i)));
2380 hiddenRoot->insertRows(index, items);
2381 } else {
2382 d->inserting = true;
2383 if (d->model->insertRows(index, insertCount, d->root)) {
2384 QModelIndex item;
2385 for (int i = 0; i < insertCount; ++i) {
2386 item = d->model->index(i+index, d->modelColumn, d->root);
2387 d->model->setData(item, list.at(i), Qt::EditRole);
2388 }
2389 d->inserting = false;
2390 d->_q_rowsInserted(d->root, index, index + insertCount - 1);
2391 } else {
2392 d->inserting = false;
2393 }
2394 }
2395
2396 int mc = count();
2397 if (mc > d->maxCount)
2398 d->model->removeRows(d->maxCount, mc - d->maxCount, d->root);
2399}
2400
2401/*!
2402 \since 4.4
2403
2404 Inserts a separator item into the combobox at the given \a index.
2405
2406 If the index is equal to or higher than the total number of items, the new item
2407 is appended to the list of existing items. If the index is zero or negative, the
2408 new item is prepended to the list of existing items.
2409
2410 \sa insertItem()
2411*/
2412void QComboBox::insertSeparator(int index)
2413{
2414 Q_D(QComboBox);
2415 int itemCount = count();
2416 index = qBound(0, index, itemCount);
2417 if (index >= d->maxCount)
2418 return;
2419 insertItem(index, QIcon(), QString());
2420 QComboBoxDelegate::setSeparator(d->model, d->model->index(index, 0, d->root));
2421}
2422
2423/*!
2424 Removes the item at the given \a index from the combobox.
2425 This will update the current index if the index is removed.
2426
2427 This function does nothing if \a index is out of range.
2428*/
2429void QComboBox::removeItem(int index)
2430{
2431 Q_D(QComboBox);
2432 if (index < 0 || index >= count())
2433 return;
2434 d->model->removeRows(index, 1, d->root);
2435}
2436
2437/*!
2438 Sets the \a text for the item on the given \a index in the combobox.
2439*/
2440void QComboBox::setItemText(int index, const QString &text)
2441{
2442 Q_D(const QComboBox);
2443 QModelIndex item = d->model->index(index, d->modelColumn, d->root);
2444 if (item.isValid()) {
2445 d->model->setData(item, text, Qt::EditRole);
2446 }
2447}
2448
2449/*!
2450 Sets the \a icon for the item on the given \a index in the combobox.
2451*/
2452void QComboBox::setItemIcon(int index, const QIcon &icon)
2453{
2454 Q_D(const QComboBox);
2455 QModelIndex item = d->model->index(index, d->modelColumn, d->root);
2456 if (item.isValid()) {
2457 d->model->setData(item, icon, Qt::DecorationRole);
2458 }
2459}
2460
2461/*!
2462 Sets the data \a role for the item on the given \a index in the combobox
2463 to the specified \a value.
2464*/
2465void QComboBox::setItemData(int index, const QVariant &value, int role)
2466{
2467 Q_D(const QComboBox);
2468 QModelIndex item = d->model->index(index, d->modelColumn, d->root);
2469 if (item.isValid()) {
2470 d->model->setData(item, value, role);
2471 }
2472}
2473
2474/*!
2475 Returns the list view used for the combobox popup.
2476*/
2477QAbstractItemView *QComboBox::view() const
2478{
2479 Q_D(const QComboBox);
2480 return const_cast<QComboBoxPrivate*>(d)->viewContainer()->itemView();
2481}
2482
2483/*!
2484 Sets the view to be used in the combobox popup to the given \a
2485 itemView. The combobox takes ownership of the view.
2486
2487 Note: If you want to use the convenience views (like QListWidget,
2488 QTableWidget or QTreeWidget), make sure to call setModel() on the
2489 combobox with the convenience widgets model before calling this
2490 function.
2491*/
2492void QComboBox::setView(QAbstractItemView *itemView)
2493{
2494 Q_D(QComboBox);
2495 if (Q_UNLIKELY(!itemView)) {
2496 qWarning("QComboBox::setView: cannot set a 0 view");
2497 return;
2498 }
2499
2500 if (itemView->model() != d->model)
2501 itemView->setModel(d->model);
2502 d->viewContainer()->setItemView(itemView);
2503}
2504
2505/*!
2506 \reimp
2507*/
2508QSize QComboBox::minimumSizeHint() const
2509{
2510 Q_D(const QComboBox);
2511 return d->recomputeSizeHint(d->minimumSizeHint);
2512}
2513
2514/*!
2515 \reimp
2516
2517 This implementation caches the size hint to avoid resizing when
2518 the contents change dynamically. To invalidate the cached value
2519 change the \l sizeAdjustPolicy.
2520*/
2521QSize QComboBox::sizeHint() const
2522{
2523 Q_D(const QComboBox);
2524 return d->recomputeSizeHint(d->sizeHint);
2525}
2526
2527#ifdef Q_OS_MAC
2528
2529namespace {
2530struct IndexSetter {
2531 int index;
2532 QComboBox *cb;
2533
2534 void operator()(void)
2535 {
2536 cb->setCurrentIndex(index);
2537 emit cb->activated(index);
2538 emit cb->activated(cb->itemText(index));
2539 }
2540};
2541}
2542
2543void QComboBoxPrivate::cleanupNativePopup()
2544{
2545 if (!m_platformMenu)
2546 return;
2547
2548 int count = int(m_platformMenu->tag());
2549 for (int i = 0; i < count; ++i)
2550 m_platformMenu->menuItemAt(i)->deleteLater();
2551
2552 delete m_platformMenu;
2553 m_platformMenu = 0;
2554}
2555
2556/*!
2557 * \internal
2558 *
2559 * Tries to show a native popup. Returns true if it could, false otherwise.
2560 *
2561 */
2562bool QComboBoxPrivate::showNativePopup()
2563{
2564 Q_Q(QComboBox);
2565
2566 cleanupNativePopup();
2567
2568 QPlatformTheme *theme = QGuiApplicationPrivate::instance()->platformTheme();
2569 m_platformMenu = theme->createPlatformMenu();
2570 if (!m_platformMenu)
2571 return false;
2572
2573 int itemsCount = q->count();
2574 m_platformMenu->setTag(quintptr(itemsCount));
2575
2576 QPlatformMenuItem *currentItem = 0;
2577 int currentIndex = q->currentIndex();
2578
2579 for (int i = 0; i < itemsCount; ++i) {
2580 QPlatformMenuItem *item = theme->createPlatformMenuItem();
2581 QModelIndex rowIndex = model->index(i, modelColumn, root);
2582 QVariant textVariant = model->data(rowIndex, Qt::EditRole);
2583 item->setText(textVariant.toString());
2584 QVariant iconVariant = model->data(rowIndex, Qt::DecorationRole);
2585 if (iconVariant.canConvert<QIcon>())
2586 item->setIcon(iconVariant.value<QIcon>());
2587 item->setCheckable(true);
2588 item->setChecked(i == currentIndex);
2589 if (!currentItem || i == currentIndex)
2590 currentItem = item;
2591
2592 IndexSetter setter = { i, q };
2593 QObject::connect(item, &QPlatformMenuItem::activated, setter);
2594
2595 m_platformMenu->insertMenuItem(item, 0);
2596 m_platformMenu->syncMenuItem(item);
2597 }
2598
2599 QWindow *tlw = q->window()->windowHandle();
2600 m_platformMenu->setFont(q->font());
2601 m_platformMenu->setMinimumWidth(q->rect().width());
2602 QPoint offset = QPoint(0, 7);
2603 if (q->testAttribute(Qt::WA_MacSmallSize))
2604 offset = QPoint(-1, 7);
2605 else if (q->testAttribute(Qt::WA_MacMiniSize))
2606 offset = QPoint(-2, 6);
2607
2608 const QRect targetRect = QRect(tlw->mapFromGlobal(q->mapToGlobal(offset)), QSize());
2609 m_platformMenu->showPopup(tlw, QHighDpi::toNativePixels(targetRect, tlw), currentItem);
2610
2611#ifdef Q_OS_OSX
2612 // The Cocoa popup will swallow any mouse release event.
2613 // We need to fake one here to un-press the button.
2614 QMouseEvent mouseReleased(QEvent::MouseButtonRelease, q->pos(), Qt::LeftButton,
2615 Qt::MouseButtons(Qt::LeftButton), Qt::KeyboardModifiers());
2616 QCoreApplication::sendEvent(q, &mouseReleased);
2617#endif
2618
2619 return true;
2620}
2621
2622#endif // Q_OS_MAC
2623
2624/*!
2625 Displays the list of items in the combobox. If the list is empty
2626 then no items will be shown.
2627
2628 If you reimplement this function to show a custom pop-up, make
2629 sure you call hidePopup() to reset the internal state.
2630
2631 \sa hidePopup()
2632*/
2633void QComboBox::showPopup()
2634{
2635 Q_D(QComboBox);
2636 if (count() <= 0)
2637 return;
2638
2639 QStyle * const style = this->style();
2640 QStyleOptionComboBox opt;
2641 initStyleOption(&opt);
2642 const bool usePopup = style->styleHint(QStyle::SH_ComboBox_Popup, &opt, this);
2643
2644#ifdef Q_OS_MAC
2645 if (usePopup
2646 && (!d->container
2647 || (view()->metaObject()->className() == QByteArray("QComboBoxListView")
2648 && view()->itemDelegate()->metaObject()->className() == QByteArray("QComboMenuDelegate")))
2649 && style->styleHint(QStyle::SH_ComboBox_UseNativePopup, &opt, this)
2650 && d->showNativePopup())
2651 return;
2652#endif // Q_OS_MAC
2653
2654#ifdef QT_KEYPAD_NAVIGATION
2655#if QT_CONFIG(completer)
2656 if (QApplicationPrivate::keypadNavigationEnabled() && d->completer) {
2657 // editable combo box is line edit plus completer
2658 setEditFocus(true);
2659 d->completer->complete(); // show popup
2660 return;
2661 }
2662#endif
2663#endif
2664
2665 // set current item and select it
2666 view()->selectionModel()->setCurrentIndex(d->currentIndex,
2667 QItemSelectionModel::ClearAndSelect);
2668 QComboBoxPrivateContainer* container = d->viewContainer();
2669 QRect listRect(style->subControlRect(QStyle::CC_ComboBox, &opt,
2670 QStyle::SC_ComboBoxListBoxPopup, this));
2671 QRect screen = d->popupGeometry(QDesktopWidgetPrivate::screenNumber(this));
2672
2673 QPoint below = mapToGlobal(listRect.bottomLeft());
2674 int belowHeight = screen.bottom() - below.y();
2675 QPoint above = mapToGlobal(listRect.topLeft());
2676 int aboveHeight = above.y() - screen.y();
2677 bool boundToScreen = !window()->testAttribute(Qt::WA_DontShowOnScreen);
2678
2679 {
2680 int listHeight = 0;
2681 int count = 0;
2682 QStack<QModelIndex> toCheck;
2683 toCheck.push(view()->rootIndex());
2684#if QT_CONFIG(treeview)
2685 QTreeView *treeView = qobject_cast<QTreeView*>(view());
2686 if (treeView && treeView->header() && !treeView->header()->isHidden())
2687 listHeight += treeView->header()->height();
2688#endif
2689 while (!toCheck.isEmpty()) {
2690 QModelIndex parent = toCheck.pop();
2691 for (int i = 0, end = d->model->rowCount(parent); i < end; ++i) {
2692 QModelIndex idx = d->model->index(i, d->modelColumn, parent);
2693 if (!idx.isValid())
2694 continue;
2695 listHeight += view()->visualRect(idx).height();
2696#if QT_CONFIG(treeview)
2697 if (d->model->hasChildren(idx) && treeView && treeView->isExpanded(idx))
2698 toCheck.push(idx);
2699#endif
2700 ++count;
2701 if (!usePopup && count >= d->maxVisibleItems) {
2702 toCheck.clear();
2703 break;
2704 }
2705 }
2706 }
2707 if (count > 1)
2708 listHeight += (count - 1) * container->spacing();
2709 listRect.setHeight(listHeight);
2710 }
2711
2712 {
2713 // add the spacing for the grid on the top and the bottom;
2714 int heightMargin = container->topMargin() + container->bottomMargin();
2715
2716 // add the frame of the container
2717 const QMargins cm = container->contentsMargins();
2718 heightMargin += cm.top() + cm.bottom();
2719
2720 //add the frame of the view
2721 const QMargins vm = view()->contentsMargins();
2722 heightMargin += vm.top() + vm.bottom();
2723 heightMargin += static_cast<QAbstractScrollAreaPrivate *>(QObjectPrivate::get(view()))->top;
2724 heightMargin += static_cast<QAbstractScrollAreaPrivate *>(QObjectPrivate::get(view()))->bottom;
2725
2726 listRect.setHeight(listRect.height() + heightMargin);
2727 }
2728
2729 // Add space for margin at top and bottom if the style wants it.
2730 if (usePopup)
2731 listRect.setHeight(listRect.height() + style->pixelMetric(QStyle::PM_MenuVMargin, &opt, this) * 2);
2732
2733 // Make sure the popup is wide enough to display its contents.
2734 if (usePopup) {
2735 const int diff = d->computeWidthHint() - width();
2736 if (diff > 0)
2737 listRect.setWidth(listRect.width() + diff);
2738 }
2739
2740 //we need to activate the layout to make sure the min/maximum size are set when the widget was not yet show
2741 container->layout()->activate();
2742 //takes account of the minimum/maximum size of the container
2743 listRect.setSize( listRect.size().expandedTo(container->minimumSize())
2744 .boundedTo(container->maximumSize()));
2745
2746 // make sure the widget fits on screen
2747 if (boundToScreen) {
2748 if (listRect.width() > screen.width() )
2749 listRect.setWidth(screen.width());
2750 if (mapToGlobal(listRect.bottomRight()).x() > screen.right()) {
2751 below.setX(screen.x() + screen.width() - listRect.width());
2752 above.setX(screen.x() + screen.width() - listRect.width());
2753 }
2754 if (mapToGlobal(listRect.topLeft()).x() < screen.x() ) {
2755 below.setX(screen.x());
2756 above.setX(screen.x());
2757 }
2758 }
2759
2760 if (usePopup) {
2761 // Position horizontally.
2762 listRect.moveLeft(above.x());
2763
2764 // Position vertically so the curently selected item lines up
2765 // with the combo box.
2766 const QRect currentItemRect = view()->visualRect(view()->currentIndex());
2767 const int offset = listRect.top() - currentItemRect.top();
2768 listRect.moveTop(above.y() + offset - listRect.top());
2769
2770 // Clamp the listRect height and vertical position so we don't expand outside the
2771 // available screen geometry.This may override the vertical position, but it is more
2772 // important to show as much as possible of the popup.
2773 const int height = !boundToScreen ? listRect.height() : qMin(listRect.height(), screen.height());
2774 listRect.setHeight(height);
2775
2776 if (boundToScreen) {
2777 if (listRect.top() < screen.top())
2778 listRect.moveTop(screen.top());
2779 if (listRect.bottom() > screen.bottom())
2780 listRect.moveBottom(screen.bottom());
2781 }
2782 } else if (!boundToScreen || listRect.height() <= belowHeight) {
2783 listRect.moveTopLeft(below);
2784 } else if (listRect.height() <= aboveHeight) {
2785 listRect.moveBottomLeft(above);
2786 } else if (belowHeight >= aboveHeight) {
2787 listRect.setHeight(belowHeight);
2788 listRect.moveTopLeft(below);
2789 } else {
2790 listRect.setHeight(aboveHeight);
2791 listRect.moveBottomLeft(above);
2792 }
2793
2794 if (qApp) {
2795 QGuiApplication::inputMethod()->reset();
2796 }
2797
2798 QScrollBar *sb = view()->horizontalScrollBar();
2799 Qt::ScrollBarPolicy policy = view()->horizontalScrollBarPolicy();
2800 bool needHorizontalScrollBar = (policy == Qt::ScrollBarAsNeeded || policy == Qt::ScrollBarAlwaysOn)
2801 && sb->minimum() < sb->maximum();
2802 if (needHorizontalScrollBar) {
2803 listRect.adjust(0, 0, 0, sb->height());
2804 }
2805
2806 // Hide the scrollers here, so that the listrect gets the full height of the container
2807 // If the scrollers are truly needed, the later call to container->updateScrollers()
2808 // will make them visible again.
2809 container->hideScrollers();
2810 container->setGeometry(listRect);
2811
2812#ifndef Q_OS_MAC
2813 const bool updatesEnabled = container->updatesEnabled();
2814#endif
2815
2816#if QT_CONFIG(effects)
2817 bool scrollDown = (listRect.topLeft() == below);
2818 if (QApplication::isEffectEnabled(Qt::UI_AnimateCombo)
2819 && !style->styleHint(QStyle::SH_ComboBox_Popup, &opt, this) && !window()->testAttribute(Qt::WA_DontShowOnScreen))
2820 qScrollEffect(container, scrollDown ? QEffects::DownScroll : QEffects::UpScroll, 150);
2821#endif
2822
2823// Don't disable updates on OS X. Windows are displayed immediately on this platform,
2824// which means that the window will be visible before the call to container->show() returns.
2825// If updates are disabled at this point we'll miss our chance at painting the popup
2826// menu before it's shown, causing flicker since the window then displays the standard gray
2827// background.
2828#ifndef Q_OS_MAC
2829 container->setUpdatesEnabled(false);
2830#endif
2831
2832 bool startTimer = !container->isVisible();
2833 container->raise();
2834 container->create();
2835 if (QWindow *containerWindow = qt_widget_private(container)->windowHandle(QWidgetPrivate::WindowHandleMode::TopLevel)) {
2836 QScreen *currentScreen = d->associatedScreen();
2837 if (currentScreen && !currentScreen->virtualSiblings().contains(containerWindow->screen())) {
2838 containerWindow->setScreen(currentScreen);
2839
2840 // This seems to workaround an issue in xcb+multi GPU+multiscreen
2841 // environment where the window might not always show up when screen
2842 // is changed.
2843 container->hide();
2844 }
2845 }
2846 container->show();
2847 container->updateScrollers();
2848 view()->setFocus();
2849
2850 view()->scrollTo(view()->currentIndex(),
2851 style->styleHint(QStyle::SH_ComboBox_Popup, &opt, this)
2852 ? QAbstractItemView::PositionAtCenter
2853 : QAbstractItemView::EnsureVisible);
2854
2855#ifndef Q_OS_MAC
2856 container->setUpdatesEnabled(updatesEnabled);
2857#endif
2858
2859 container->update();
2860#ifdef QT_KEYPAD_NAVIGATION
2861 if (QApplicationPrivate::keypadNavigationEnabled())
2862 view()->setEditFocus(true);
2863#endif
2864 if (startTimer) {
2865 container->popupTimer.start();
2866 container->maybeIgnoreMouseButtonRelease = true;
2867 }
2868}
2869
2870/*!
2871 Hides the list of items in the combobox if it is currently visible
2872 and resets the internal state, so that if the custom pop-up was
2873 shown inside the reimplemented showPopup(), then you also need to
2874 reimplement the hidePopup() function to hide your custom pop-up
2875 and call the base class implementation to reset the internal state
2876 whenever your custom pop-up widget is hidden.
2877
2878 \sa showPopup()
2879*/
2880void QComboBox::hidePopup()
2881{
2882 Q_D(QComboBox);
2883 if (d->container && d->container->isVisible()) {
2884#if QT_CONFIG(effects)
2885 QSignalBlocker modelBlocker(d->model);
2886 QSignalBlocker viewBlocker(d->container->itemView());
2887 QSignalBlocker containerBlocker(d->container);
2888 // Flash selected/triggered item (if any).
2889 if (style()->styleHint(QStyle::SH_Menu_FlashTriggeredItem)) {
2890 QItemSelectionModel *selectionModel = view() ? view()->selectionModel() : 0;
2891 if (selectionModel && selectionModel->hasSelection()) {
2892 QEventLoop eventLoop;
2893 const QItemSelection selection = selectionModel->selection();
2894
2895 // Deselect item and wait 60 ms.
2896 selectionModel->select(selection, QItemSelectionModel::Toggle);
2897 QTimer::singleShot(60, &eventLoop, SLOT(quit()));
2898 eventLoop.exec();
2899
2900 // Select item and wait 20 ms.
2901 selectionModel->select(selection, QItemSelectionModel::Toggle);
2902 QTimer::singleShot(20, &eventLoop, SLOT(quit()));
2903 eventLoop.exec();
2904 }
2905 }
2906
2907 // Fade out.
2908 bool needFade = style()->styleHint(QStyle::SH_Menu_FadeOutOnHide);
2909 bool didFade = false;
2910 if (needFade) {
2911#if defined(Q_OS_MAC)
2912 QPlatformNativeInterface *platformNativeInterface = QGuiApplication::platformNativeInterface();
2913 int at = platformNativeInterface->metaObject()->indexOfMethod("fadeWindow()");
2914 if (at != -1) {
2915 QMetaMethod windowFade = platformNativeInterface->metaObject()->method(at);
2916 windowFade.invoke(platformNativeInterface, Q_ARG(QWindow *, d->container->windowHandle()));
2917 didFade = true;
2918 }
2919
2920#endif // Q_OS_MAC
2921 // Other platform implementations welcome :-)
2922 }
2923 containerBlocker.unblock();
2924 viewBlocker.unblock();
2925 modelBlocker.unblock();
2926
2927 if (!didFade)
2928#endif // QT_CONFIG(effects)
2929 // Fade should implicitly hide as well ;-)
2930 d->container->hide();
2931 }
2932#ifdef QT_KEYPAD_NAVIGATION
2933 if (QApplicationPrivate::keypadNavigationEnabled() && isEditable() && hasFocus())
2934 setEditFocus(true);
2935#endif
2936 d->_q_resetButton();
2937}
2938
2939/*!
2940 Clears the combobox, removing all items.
2941
2942 Note: If you have set an external model on the combobox this model
2943 will still be cleared when calling this function.
2944*/
2945void QComboBox::clear()
2946{
2947 Q_D(QComboBox);
2948 d->model->removeRows(0, d->model->rowCount(d->root), d->root);
2949#ifndef QT_NO_ACCESSIBILITY
2950 QAccessibleValueChangeEvent event(this, QString());
2951 QAccessible::updateAccessibility(&event);
2952#endif
2953}
2954
2955/*!
2956 Clears the contents of the line edit used for editing in the combobox.
2957*/
2958void QComboBox::clearEditText()
2959{
2960 Q_D(QComboBox);
2961 if (d->lineEdit)
2962 d->lineEdit->clear();
2963#ifndef QT_NO_ACCESSIBILITY
2964 QAccessibleValueChangeEvent event(this, QString());
2965 QAccessible::updateAccessibility(&event);
2966#endif
2967}
2968
2969/*!
2970 Sets the \a text in the combobox's text edit.
2971*/
2972void QComboBox::setEditText(const QString &text)
2973{
2974 Q_D(QComboBox);
2975 if (d->lineEdit)
2976 d->lineEdit->setText(text);
2977#ifndef QT_NO_ACCESSIBILITY
2978 QAccessibleValueChangeEvent event(this, text);
2979 QAccessible::updateAccessibility(&event);
2980#endif
2981}
2982
2983/*!
2984 \reimp
2985*/
2986void QComboBox::focusInEvent(QFocusEvent *e)
2987{
2988 Q_D(QComboBox);
2989 update();
2990 if (d->lineEdit) {
2991 d->lineEdit->event(e);
2992#if QT_CONFIG(completer)
2993 if (d->lineEdit->completer())
2994 d->lineEdit->completer()->setWidget(this);
2995#endif
2996 }
2997}
2998
2999/*!
3000 \reimp
3001*/
3002void QComboBox::focusOutEvent(QFocusEvent *e)
3003{
3004 Q_D(QComboBox);
3005 update();
3006 if (d->lineEdit)
3007 d->lineEdit->event(e);
3008}
3009
3010/*! \reimp */
3011void QComboBox::changeEvent(QEvent *e)
3012{
3013 Q_D(QComboBox);
3014 switch (e->type()) {
3015 case QEvent::StyleChange:
3016 d->updateDelegate();
3017#ifdef Q_OS_MAC
3018 case QEvent::MacSizeChange:
3019#endif
3020 d->sizeHint = QSize(); // invalidate size hint
3021 d->minimumSizeHint = QSize();
3022 d->updateLayoutDirection();
3023 if (d->lineEdit)
3024 d->updateLineEditGeometry();
3025 d->setLayoutItemMargins(QStyle::SE_ComboBoxLayoutItem);
3026
3027 if (e->type() == QEvent::MacSizeChange){
3028 QPlatformTheme::Font f = QPlatformTheme::SystemFont;
3029 if (testAttribute(Qt::WA_MacSmallSize))
3030 f = QPlatformTheme::SmallFont;
3031 else if (testAttribute(Qt::WA_MacMiniSize))
3032 f = QPlatformTheme::MiniFont;
3033 if (const QFont *platformFont = QApplicationPrivate::platformTheme()->font(f)) {
3034 QFont f = font();
3035 f.setPointSizeF(platformFont->pointSizeF());
3036 setFont(