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