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::Background,
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->setMargin(0);
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/*!
899 \fn void QComboBox::currentIndexChanged(const QString &text)
900 \since 4.1
901
902 This signal is sent whenever the currentIndex in the combobox
903 changes either through user interaction or programmatically. The
904 item's \a text is passed.
905*/
906
907/*!
908 \fn void QComboBox::currentTextChanged(const QString &text)
909 \since 5.0
910
911 This signal is sent whenever currentText changes. The new value
912 is passed as \a text.
913*/
914
915/*!
916 Constructs a combobox with the given \a parent, using the default
917 model QStandardItemModel.
918*/
919QComboBox::QComboBox(QWidget *parent)
920 : QWidget(*new QComboBoxPrivate(), parent, 0)
921{
922 Q_D(QComboBox);
923 d->init();
924}
925
926/*!
927 \internal
928*/
929QComboBox::QComboBox(QComboBoxPrivate &dd, QWidget *parent)
930 : QWidget(dd, parent, 0)
931{
932 Q_D(QComboBox);
933 d->init();
934}
935
936
937/*!
938 \class QComboBox
939 \brief The QComboBox widget is a combined button and popup list.
940
941 \ingroup basicwidgets
942 \inmodule QtWidgets
943
944 \image windows-combobox.png
945
946 A QComboBox provides a means of presenting a list of options to the user
947 in a way that takes up the minimum amount of screen space.
948
949 A combobox is a selection widget that displays the current item,
950 and can pop up a list of selectable items. A combobox may be editable,
951 allowing the user to modify each item in the list.
952
953 Comboboxes can contain pixmaps as well as strings; the
954 insertItem() and setItemText() functions are suitably overloaded.
955 For editable comboboxes, the function clearEditText() is provided,
956 to clear the displayed string without changing the combobox's
957 contents.
958
959 There are two signals emitted if the current item of a combobox
960 changes, currentIndexChanged() and activated().
961 currentIndexChanged() is always emitted regardless if the change
962 was done programmatically or by user interaction, while
963 activated() is only emitted when the change is caused by user
964 interaction. The highlighted() signal is emitted when the user
965 highlights an item in the combobox popup list. All three signals
966 exist in two versions, one with a QString argument and one with an
967 \c int argument. If the user selects or highlights a pixmap, only
968 the \c int signals are emitted. Whenever the text of an editable
969 combobox is changed the editTextChanged() signal is emitted.
970
971 When the user enters a new string in an editable combobox, the
972 widget may or may not insert it, and it can insert it in several
973 locations. The default policy is \l InsertAtBottom but you can change
974 this using setInsertPolicy().
975
976 It is possible to constrain the input to an editable combobox
977 using QValidator; see setValidator(). By default, any input is
978 accepted.
979
980 A combobox can be populated using the insert functions,
981 insertItem() and insertItems() for example. Items can be
982 changed with setItemText(). An item can be removed with
983 removeItem() and all items can be removed with clear(). The text
984 of the current item is returned by currentText(), and the text of
985 a numbered item is returned with text(). The current item can be
986 set with setCurrentIndex(). The number of items in the combobox is
987 returned by count(); the maximum number of items can be set with
988 setMaxCount(). You can allow editing using setEditable(). For
989 editable comboboxes you can set auto-completion using
990 setCompleter() and whether or not the user can add duplicates
991 is set with setDuplicatesEnabled().
992
993 QComboBox uses the \l{Model/View Programming}{model/view
994 framework} for its popup list and to store its items. By default
995 a QStandardItemModel stores the items and a QListView subclass
996 displays the popuplist. You can access the model and view directly
997 (with model() and view()), but QComboBox also provides functions
998 to set and get item data (e.g., setItemData() and itemText()). You
999 can also set a new model and view (with setModel() and setView()).
1000 For the text and icon in the combobox label, the data in the model
1001 that has the Qt::DisplayRole and Qt::DecorationRole is used. Note
1002 that you cannot alter the \l{QAbstractItemView::}{SelectionMode}
1003 of the view(), e.g., by using
1004 \l{QAbstractItemView::}{setSelectionMode()}.
1005
1006 \sa QLineEdit, QSpinBox, QRadioButton, QButtonGroup,
1007 {fowler}{GUI Design Handbook: Combo Box, Drop-Down List Box}
1008*/
1009
1010void QComboBoxPrivate::init()
1011{
1012 Q_Q(QComboBox);
1013#ifdef Q_OS_OSX
1014 // On OS X, only line edits and list views always get tab focus. It's only
1015 // when we enable full keyboard access that other controls can get tab focus.
1016 // When it's not editable, a combobox looks like a button, and it behaves as
1017 // such in this respect.
1018 if (!q->isEditable())
1019 q->setFocusPolicy(Qt::TabFocus);
1020 else
1021#endif
1022 q->setFocusPolicy(Qt::WheelFocus);
1023
1024 q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed,
1025 QSizePolicy::ComboBox));
1026 setLayoutItemMargins(QStyle::SE_ComboBoxLayoutItem);
1027 q->setModel(new QStandardItemModel(0, 1, q));
1028 if (!q->isEditable())
1029 q->setAttribute(Qt::WA_InputMethodEnabled, false);
1030 else
1031 q->setAttribute(Qt::WA_InputMethodEnabled);
1032}
1033
1034QComboBoxPrivateContainer* QComboBoxPrivate::viewContainer()
1035{
1036 if (container)
1037 return container;
1038
1039 Q_Q(QComboBox);
1040 container = new QComboBoxPrivateContainer(new QComboBoxListView(q), q);
1041 container->itemView()->setModel(model);
1042 container->itemView()->setTextElideMode(Qt::ElideMiddle);
1043 updateDelegate(true);
1044 updateLayoutDirection();
1045 updateViewContainerPaletteAndOpacity();
1046 QObject::connect(container, SIGNAL(itemSelected(QModelIndex)),
1047 q, SLOT(_q_itemSelected(QModelIndex)));
1048 QObject::connect(container->itemView()->selectionModel(),
1049 SIGNAL(currentChanged(QModelIndex,QModelIndex)),
1050 q, SLOT(_q_emitHighlighted(QModelIndex)));
1051 QObject::connect(container, SIGNAL(resetButton()), q, SLOT(_q_resetButton()));
1052 return container;
1053}
1054
1055
1056void QComboBoxPrivate::_q_resetButton()
1057{
1058 updateArrow(QStyle::State_None);
1059}
1060
1061void QComboBoxPrivate::_q_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
1062{
1063 Q_Q(QComboBox);
1064 if (inserting || topLeft.parent() != root)
1065 return;
1066
1067 if (sizeAdjustPolicy == QComboBox::AdjustToContents) {
1068 sizeHint = QSize();
1069 adjustComboBoxSize();
1070 q->updateGeometry();
1071 }
1072
1073 if (currentIndex.row() >= topLeft.row() && currentIndex.row() <= bottomRight.row()) {
1074 const QString text = q->itemText(currentIndex.row());
1075 if (lineEdit) {
1076 lineEdit->setText(text);
1077 updateLineEditGeometry();
1078 } else {
1079 emit q->currentTextChanged(text);
1080 }
1081 q->update();
1082#ifndef QT_NO_ACCESSIBILITY
1083 QAccessibleValueChangeEvent event(q, text);
1084 QAccessible::updateAccessibility(&event);
1085#endif
1086 }
1087}
1088
1089void QComboBoxPrivate::_q_rowsInserted(const QModelIndex &parent, int start, int end)
1090{
1091 Q_Q(QComboBox);
1092 if (inserting || parent != root)
1093 return;
1094
1095 if (sizeAdjustPolicy == QComboBox::AdjustToContents) {
1096 sizeHint = QSize();
1097 adjustComboBoxSize();
1098 q->updateGeometry();
1099 }
1100
1101 // set current index if combo was previously empty
1102 if (start == 0 && (end - start + 1) == q->count() && !currentIndex.isValid()) {
1103 q->setCurrentIndex(0);
1104 // need to emit changed if model updated index "silently"
1105 } else if (currentIndex.row() != indexBeforeChange) {
1106 q->update();
1107 _q_emitCurrentIndexChanged(currentIndex);
1108 }
1109}
1110
1111void QComboBoxPrivate::_q_updateIndexBeforeChange()
1112{
1113 indexBeforeChange = currentIndex.row();
1114}
1115
1116void QComboBoxPrivate::_q_rowsRemoved(const QModelIndex &parent, int /*start*/, int /*end*/)
1117{
1118 Q_Q(QComboBox);
1119 if (parent != root)
1120 return;
1121
1122 if (sizeAdjustPolicy == QComboBox::AdjustToContents) {
1123 sizeHint = QSize();
1124 adjustComboBoxSize();
1125 q->updateGeometry();
1126 }
1127
1128 // model has changed the currentIndex
1129 if (currentIndex.row() != indexBeforeChange) {
1130 if (!currentIndex.isValid() && q->count()) {
1131 q->setCurrentIndex(qMin(q->count() - 1, qMax(indexBeforeChange, 0)));
1132 return;
1133 }
1134 if (lineEdit) {
1135 lineEdit->setText(q->itemText(currentIndex.row()));
1136 updateLineEditGeometry();
1137 }
1138 q->update();
1139 _q_emitCurrentIndexChanged(currentIndex);
1140 }
1141}
1142
1143
1144void QComboBoxPrivate::updateViewContainerPaletteAndOpacity()
1145{
1146 if (!container)
1147 return;
1148 Q_Q(QComboBox);
1149 QStyleOptionComboBox opt;
1150 q->initStyleOption(&opt);
1151#if QT_CONFIG(menu)
1152 if (q->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, q)) {
1153 QMenu menu;
1154 menu.ensurePolished();
1155 container->setPalette(menu.palette());
1156 container->setWindowOpacity(menu.windowOpacity());
1157 } else
1158#endif
1159 {
1160 container->setPalette(q->palette());
1161 container->setWindowOpacity(1.0);
1162 }
1163 if (lineEdit)
1164 lineEdit->setPalette(q->palette());
1165}
1166
1167void QComboBoxPrivate::updateFocusPolicy()
1168{
1169#ifdef Q_OS_OSX
1170 Q_Q(QComboBox);
1171
1172 // See comment in QComboBoxPrivate::init()
1173 if (q->isEditable())
1174 q->setFocusPolicy(Qt::WheelFocus);
1175 else
1176 q->setFocusPolicy(Qt::TabFocus);
1177#endif
1178}
1179
1180/*!
1181 Initialize \a option with the values from this QComboBox. This method
1182 is useful for subclasses when they need a QStyleOptionComboBox, but don't want
1183 to fill in all the information themselves.
1184
1185 \sa QStyleOption::initFrom()
1186*/
1187void QComboBox::initStyleOption(QStyleOptionComboBox *option) const
1188{
1189 if (!option)
1190 return;
1191
1192 Q_D(const QComboBox);
1193 option->initFrom(this);
1194 option->editable = isEditable();
1195 option->frame = d->frame;
1196 if (hasFocus() && !option->editable)
1197 option->state |= QStyle::State_Selected;
1198 option->subControls = QStyle::SC_All;
1199 if (d->arrowState == QStyle::State_Sunken) {
1200 option->activeSubControls = QStyle::SC_ComboBoxArrow;
1201 option->state |= d->arrowState;
1202 } else {
1203 option->activeSubControls = d->hoverControl;
1204 }
1205 if (d->currentIndex.isValid()) {
1206 option->currentText = currentText();
1207 option->currentIcon = d->itemIcon(d->currentIndex);
1208 }
1209 option->iconSize = iconSize();
1210 if (d->container && d->container->isVisible())
1211 option->state |= QStyle::State_On;
1212}
1213
1214void QComboBoxPrivate::updateLineEditGeometry()
1215{
1216 if (!lineEdit)
1217 return;
1218
1219 Q_Q(QComboBox);
1220 QStyleOptionComboBox opt;
1221 q->initStyleOption(&opt);
1222 QRect editRect = q->style()->subControlRect(QStyle::CC_ComboBox, &opt,
1223 QStyle::SC_ComboBoxEditField, q);
1224 if (!q->itemIcon(q->currentIndex()).isNull()) {
1225 QRect comboRect(editRect);
1226 editRect.setWidth(editRect.width() - q->iconSize().width() - 4);
1227 editRect = QStyle::alignedRect(q->layoutDirection(), Qt::AlignRight,
1228 editRect.size(), comboRect);
1229 }
1230 lineEdit->setGeometry(editRect);
1231}
1232
1233Qt::MatchFlags QComboBoxPrivate::matchFlags() const
1234{
1235 // Base how duplicates are determined on the autocompletion case sensitivity
1236 Qt::MatchFlags flags = Qt::MatchFixedString;
1237#if QT_CONFIG(completer)
1238 if (!lineEdit->completer() || lineEdit->completer()->caseSensitivity() == Qt::CaseSensitive)
1239#endif
1240 flags |= Qt::MatchCaseSensitive;
1241 return flags;
1242}
1243
1244
1245void QComboBoxPrivate::_q_editingFinished()
1246{
1247 Q_Q(QComboBox);
1248 if (!lineEdit)
1249 return;
1250 const auto leText = lineEdit->text();
1251 if (!leText.isEmpty() && itemText(currentIndex) != leText) {
1252#if QT_CONFIG(completer)
1253 const auto *leCompleter = lineEdit->completer();
1254 const auto *popup = leCompleter ? QCompleterPrivate::get(leCompleter)->popup : nullptr;
1255 if (popup && popup->isVisible()) {
1256 // QLineEdit::editingFinished() will be emitted before the code flow returns
1257 // to QCompleter::eventFilter(), where QCompleter::activated() may be emitted.
1258 // We know that the completer popup will still be visible at this point, and
1259 // that any selection should be valid.
1260 const QItemSelectionModel *selModel = popup->selectionModel();
1261 const QModelIndex curIndex = popup->currentIndex();
1262 const bool completerIsActive = selModel && selModel->selectedIndexes().contains(curIndex);
1263
1264 if (completerIsActive)
1265 return;
1266 }
1267#endif
1268 const int index = q_func()->findText(leText, matchFlags());
1269 if (index != -1) {
1270 q->setCurrentIndex(index);
1271 emitActivated(currentIndex);
1272 }
1273 }
1274
1275}
1276
1277void QComboBoxPrivate::_q_returnPressed()
1278{
1279 Q_Q(QComboBox);
1280
1281 // The insertion code below does not apply when the policy is QComboBox::NoInsert.
1282 // In case a completer is installed, item activation via the completer is handled
1283 // in _q_completerActivated(). Otherwise _q_editingFinished() updates the current
1284 // index as appropriate.
1285 if (insertPolicy == QComboBox::NoInsert)
1286 return;
1287
1288 if (lineEdit && !lineEdit->text().isEmpty()) {
1289 if (q->count() >= maxCount && !(this->insertPolicy == QComboBox::InsertAtCurrent))
1290 return;
1291 lineEdit->deselect();
1292 lineEdit->end(false);
1293 QString text = lineEdit->text();
1294 // check for duplicates (if not enabled) and quit
1295 int index = -1;
1296 if (!duplicatesEnabled) {
1297 index = q->findText(text, matchFlags());
1298 if (index != -1) {
1299 q->setCurrentIndex(index);
1300 emitActivated(currentIndex);
1301 return;
1302 }
1303 }
1304 switch (insertPolicy) {
1305 case QComboBox::InsertAtTop:
1306 index = 0;
1307 break;
1308 case QComboBox::InsertAtBottom:
1309 index = q->count();
1310 break;
1311 case QComboBox::InsertAtCurrent:
1312 case QComboBox::InsertAfterCurrent:
1313 case QComboBox::InsertBeforeCurrent:
1314 if (!q->count() || !currentIndex.isValid())
1315 index = 0;
1316 else if (insertPolicy == QComboBox::InsertAtCurrent)
1317 q->setItemText(q->currentIndex(), text);
1318 else if (insertPolicy == QComboBox::InsertAfterCurrent)
1319 index = q->currentIndex() + 1;
1320 else if (insertPolicy == QComboBox::InsertBeforeCurrent)
1321 index = q->currentIndex();
1322 break;
1323 case QComboBox::InsertAlphabetically:
1324 index = 0;
1325 for (int i=0; i< q->count(); i++, index++ ) {
1326 if (text.toLower() < q->itemText(i).toLower())
1327 break;
1328 }
1329 break;
1330 default:
1331 break;
1332 }
1333 if (index >= 0) {
1334 q->insertItem(index, text);
1335 q->setCurrentIndex(index);
1336 emitActivated(currentIndex);
1337 }
1338 }
1339}
1340
1341void QComboBoxPrivate::_q_itemSelected(const QModelIndex &item)
1342{
1343 Q_Q(QComboBox);
1344 if (item != currentIndex) {
1345 setCurrentIndex(item);
1346 } else if (lineEdit) {
1347 lineEdit->selectAll();
1348 lineEdit->setText(q->itemText(currentIndex.row()));
1349 }
1350 emitActivated(currentIndex);
1351}
1352
1353void QComboBoxPrivate::emitActivated(const QModelIndex &index)
1354{
1355 Q_Q(QComboBox);
1356 if (!index.isValid())
1357 return;
1358 QString text(itemText(index));
1359 emit q->activated(index.row());
1360 emit q->activated(text);
1361}
1362
1363void QComboBoxPrivate::_q_emitHighlighted(const QModelIndex &index)
1364{
1365 Q_Q(QComboBox);
1366 if (!index.isValid())
1367 return;
1368 QString text(itemText(index));
1369 emit q->highlighted(index.row());
1370 emit q->highlighted(text);
1371}
1372
1373void QComboBoxPrivate::_q_emitCurrentIndexChanged(const QModelIndex &index)
1374{
1375 Q_Q(QComboBox);
1376 const QString text = itemText(index);
1377 emit q->currentIndexChanged(index.row());
1378 emit q->currentIndexChanged(text);
1379 // signal lineEdit.textChanged already connected to signal currentTextChanged, so don't emit double here
1380 if (!lineEdit)
1381 emit q->currentTextChanged(text);
1382#ifndef QT_NO_ACCESSIBILITY
1383 QAccessibleValueChangeEvent event(q, text);
1384 QAccessible::updateAccessibility(&event);
1385#endif
1386}
1387
1388QString QComboBoxPrivate::itemText(const QModelIndex &index) const
1389{
1390 return index.isValid() ? model->data(index, itemRole()).toString() : QString();
1391}
1392
1393int QComboBoxPrivate::itemRole() const
1394{
1395 return q_func()->isEditable() ? Qt::EditRole : Qt::DisplayRole;
1396}
1397
1398/*!
1399 Destroys the combobox.
1400*/
1401QComboBox::~QComboBox()
1402{
1403 // ### check delegateparent and delete delegate if us?
1404 Q_D(QComboBox);
1405
1406 QT_TRY {
1407 disconnect(d->model, SIGNAL(destroyed()),
1408 this, SLOT(_q_modelDestroyed()));
1409 } QT_CATCH(...) {
1410 ; // objects can't throw in destructor
1411 }
1412}
1413
1414/*!
1415 \property QComboBox::maxVisibleItems
1416 \brief the maximum allowed size on screen of the combo box, measured in items
1417
1418 By default, this property has a value of 10.
1419
1420 \note This property is ignored for non-editable comboboxes in styles that returns
1421 true for QStyle::SH_ComboBox_Popup such as the Mac style or the Gtk+ Style.
1422*/
1423int QComboBox::maxVisibleItems() const
1424{
1425 Q_D(const QComboBox);
1426 return d->maxVisibleItems;
1427}
1428
1429void QComboBox::setMaxVisibleItems(int maxItems)
1430{
1431 Q_D(QComboBox);
1432 if (Q_UNLIKELY(maxItems < 0)) {
1433 qWarning("QComboBox::setMaxVisibleItems: "
1434 "Invalid max visible items (%d) must be >= 0", maxItems);
1435 return;
1436 }
1437 d->maxVisibleItems = maxItems;
1438}
1439
1440/*!
1441 \property QComboBox::count
1442 \brief the number of items in the combobox
1443
1444 By default, for an empty combo box, this property has a value of 0.
1445*/
1446int QComboBox::count() const
1447{
1448 Q_D(const QComboBox);
1449 return d->model->rowCount(d->root);
1450}
1451
1452/*!
1453 \property QComboBox::maxCount
1454 \brief the maximum number of items allowed in the combobox
1455
1456 \note If you set the maximum number to be less then the current
1457 amount of items in the combobox, the extra items will be
1458 truncated. This also applies if you have set an external model on
1459 the combobox.
1460
1461 By default, this property's value is derived from the highest
1462 signed integer available (typically 2147483647).
1463*/
1464void QComboBox::setMaxCount(int max)
1465{
1466 Q_D(QComboBox);
1467 if (Q_UNLIKELY(max < 0)) {
1468 qWarning("QComboBox::setMaxCount: Invalid count (%d) must be >= 0", max);
1469 return;
1470 }
1471
1472 const int rowCount = count();
1473 if (rowCount > max)
1474 d->model->removeRows(max, rowCount - max, d->root);
1475
1476 d->maxCount = max;
1477}
1478
1479int QComboBox::maxCount() const
1480{
1481 Q_D(const QComboBox);
1482 return d->maxCount;
1483}
1484
1485#if QT_CONFIG(completer)
1486
1487/*!
1488 \property QComboBox::autoCompletion
1489 \brief whether the combobox provides auto-completion for editable items
1490 \since 4.1
1491 \obsolete
1492
1493 Use setCompleter() instead.
1494
1495 By default, this property is \c true.
1496
1497 \sa editable
1498*/
1499
1500/*!
1501 \obsolete
1502
1503 Use setCompleter() instead.
1504*/
1505bool QComboBox::autoCompletion() const
1506{
1507 Q_D(const QComboBox);
1508 return d->autoCompletion;
1509}
1510
1511/*!
1512 \obsolete
1513
1514 Use setCompleter() instead.
1515*/
1516void QComboBox::setAutoCompletion(bool enable)
1517{
1518 Q_D(QComboBox);
1519
1520#ifdef QT_KEYPAD_NAVIGATION
1521 if (Q_UNLIKELY(QApplication::keypadNavigationEnabled() && !enable && isEditable()))
1522 qWarning("QComboBox::setAutoCompletion: auto completion is mandatory when combo box editable");
1523#endif
1524
1525 d->autoCompletion = enable;
1526 if (!d->lineEdit)
1527 return;
1528 if (enable) {
1529 if (d->lineEdit->completer())
1530 return;
1531 d->completer = new QCompleter(d->model, d->lineEdit);
1532 connect(d->completer, SIGNAL(activated(QModelIndex)), this, SLOT(_q_completerActivated(QModelIndex)));
1533 d->completer->setCaseSensitivity(d->autoCompletionCaseSensitivity);
1534 d->completer->setCompletionMode(QCompleter::InlineCompletion);
1535 d->completer->setCompletionColumn(d->modelColumn);
1536 d->lineEdit->setCompleter(d->completer);
1537 d->completer->setWidget(this);
1538 } else {
1539 d->lineEdit->setCompleter(0);
1540 }
1541}
1542
1543/*!
1544 \property QComboBox::autoCompletionCaseSensitivity
1545 \brief whether string comparisons are case-sensitive or case-insensitive for auto-completion
1546 \obsolete
1547
1548 By default, this property is Qt::CaseInsensitive.
1549
1550 Use setCompleter() instead. Case sensitivity of the auto completion can be
1551 changed using QCompleter::setCaseSensitivity().
1552
1553 \sa autoCompletion
1554*/
1555
1556/*!
1557 \obsolete
1558
1559 Use setCompleter() and QCompleter::setCaseSensitivity() instead.
1560*/
1561Qt::CaseSensitivity QComboBox::autoCompletionCaseSensitivity() const
1562{
1563 Q_D(const QComboBox);
1564 return d->autoCompletionCaseSensitivity;
1565}
1566
1567/*!
1568 \obsolete
1569
1570 Use setCompleter() and QCompleter::setCaseSensitivity() instead.
1571*/
1572void QComboBox::setAutoCompletionCaseSensitivity(Qt::CaseSensitivity sensitivity)
1573{
1574 Q_D(QComboBox);
1575 d->autoCompletionCaseSensitivity = sensitivity;
1576 if (d->lineEdit && d->lineEdit->completer())
1577 d->lineEdit->completer()->setCaseSensitivity(sensitivity);
1578}
1579
1580#endif // QT_CONFIG(completer)
1581
1582/*!
1583 \property QComboBox::duplicatesEnabled
1584 \brief whether the user can enter duplicate items into the combobox
1585
1586 Note that it is always possible to programmatically insert duplicate items into the
1587 combobox.
1588
1589 By default, this property is \c false (duplicates are not allowed).
1590*/
1591bool QComboBox::duplicatesEnabled() const
1592{
1593 Q_D(const QComboBox);
1594 return d->duplicatesEnabled;
1595}
1596
1597void QComboBox::setDuplicatesEnabled(bool enable)
1598{
1599 Q_D(QComboBox);
1600 d->duplicatesEnabled = enable;
1601}
1602
1603/*! \fn int QComboBox::findText(const QString &text, Qt::MatchFlags flags = Qt::MatchExactly|Qt::MatchCaseSensitive) const
1604
1605 Returns the index of the item containing the given \a text; otherwise
1606 returns -1.
1607
1608 The \a flags specify how the items in the combobox are searched.
1609*/
1610
1611/*!
1612 Returns the index of the item containing the given \a data for the
1613 given \a role; otherwise returns -1.
1614
1615 The \a flags specify how the items in the combobox are searched.
1616*/
1617int QComboBox::findData(const QVariant &data, int role, Qt::MatchFlags flags) const
1618{
1619 Q_D(const QComboBox);
1620 QModelIndex start = d->model->index(0, d->modelColumn, d->root);
1621 const QModelIndexList result = d->model->match(start, role, data, 1, flags);
1622 if (result.isEmpty())
1623 return -1;
1624 return result.first().row();
1625}
1626
1627/*!
1628 \property QComboBox::insertPolicy
1629 \brief the policy used to determine where user-inserted items should
1630 appear in the combobox
1631
1632 The default value is \l InsertAtBottom, indicating that new items will appear
1633 at the bottom of the list of items.
1634
1635 \sa InsertPolicy
1636*/
1637
1638QComboBox::InsertPolicy QComboBox::insertPolicy() const
1639{
1640 Q_D(const QComboBox);
1641 return d->insertPolicy;
1642}
1643
1644void QComboBox::setInsertPolicy(InsertPolicy policy)
1645{
1646 Q_D(QComboBox);
1647 d->insertPolicy = policy;
1648}
1649
1650/*!
1651 \property QComboBox::sizeAdjustPolicy
1652 \brief the policy describing how the size of the combobox changes
1653 when the content changes
1654
1655 The default value is \l AdjustToContentsOnFirstShow.
1656
1657 \sa SizeAdjustPolicy
1658*/
1659
1660QComboBox::SizeAdjustPolicy QComboBox::sizeAdjustPolicy() const
1661{
1662 Q_D(const QComboBox);
1663 return d->sizeAdjustPolicy;
1664}
1665
1666void QComboBox::setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy policy)
1667{
1668 Q_D(QComboBox);
1669 if (policy == d->sizeAdjustPolicy)
1670 return;
1671
1672 d->sizeAdjustPolicy = policy;
1673 d->sizeHint = QSize();
1674 d->adjustComboBoxSize();
1675 updateGeometry();
1676}
1677
1678/*!
1679 \property QComboBox::minimumContentsLength
1680 \brief the minimum number of characters that should fit into the combobox.
1681
1682 The default value is 0.
1683
1684 If this property is set to a positive value, the
1685 minimumSizeHint() and sizeHint() take it into account.
1686
1687 \sa sizeAdjustPolicy
1688*/
1689int QComboBox::minimumContentsLength() const
1690{
1691 Q_D(const QComboBox);
1692 return d->minimumContentsLength;
1693}
1694
1695void QComboBox::setMinimumContentsLength(int characters)
1696{
1697 Q_D(QComboBox);
1698 if (characters == d->minimumContentsLength || characters < 0)
1699 return;
1700
1701 d->minimumContentsLength = characters;
1702
1703 if (d->sizeAdjustPolicy == AdjustToContents
1704 || d->sizeAdjustPolicy == AdjustToMinimumContentsLength
1705 || d->sizeAdjustPolicy == AdjustToMinimumContentsLengthWithIcon) {
1706 d->sizeHint = QSize();
1707 d->adjustComboBoxSize();
1708 updateGeometry();
1709 }
1710}
1711
1712/*!
1713 \property QComboBox::iconSize
1714 \brief the size of the icons shown in the combobox.
1715
1716 Unless explicitly set this returns the default value of the
1717 current style. This size is the maximum size that icons can have;
1718 icons of smaller size are not scaled up.
1719*/
1720
1721QSize QComboBox::iconSize() const
1722{
1723 Q_D(const QComboBox);
1724 if (d->iconSize.isValid())
1725 return d->iconSize;
1726
1727 int iconWidth = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this);
1728 return QSize(iconWidth, iconWidth);
1729}
1730
1731void QComboBox::setIconSize(const QSize &size)
1732{
1733 Q_D(QComboBox);
1734 if (size == d->iconSize)
1735 return;
1736
1737 view()->setIconSize(size);
1738 d->iconSize = size;
1739 d->sizeHint = QSize();
1740 updateGeometry();
1741}
1742
1743/*!
1744 \property QComboBox::editable
1745 \brief whether the combo box can be edited by the user
1746
1747 By default, this property is \c false. The effect of editing depends
1748 on the insert policy.
1749
1750 \note When disabling the \a editable state, the validator and
1751 completer are removed.
1752
1753 \sa InsertPolicy
1754*/
1755bool QComboBox::isEditable() const
1756{
1757 Q_D(const QComboBox);
1758 return d->lineEdit != 0;
1759}
1760
1761/*! \internal
1762 update the default delegate
1763 depending on the style's SH_ComboBox_Popup hint, we use a different default delegate.
1764
1765 but we do not change the delegate is the combobox use a custom delegate,
1766 unless \a force is set to true.
1767 */
1768void QComboBoxPrivate::updateDelegate(bool force)
1769{
1770 Q_Q(QComboBox);
1771 QStyleOptionComboBox opt;
1772 q->initStyleOption(&opt);
1773 if (q->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, q)) {
1774 if (force || qobject_cast<QComboBoxDelegate *>(q->itemDelegate()))
1775 q->setItemDelegate(new QComboMenuDelegate(q->view(), q));
1776 } else {
1777 if (force || qobject_cast<QComboMenuDelegate *>(q->itemDelegate()))
1778 q->setItemDelegate(new QComboBoxDelegate(q->view(), q));
1779 }
1780}
1781
1782QIcon QComboBoxPrivate::itemIcon(const QModelIndex &index) const
1783{
1784 QVariant decoration = model->data(index, Qt::DecorationRole);
1785 if (decoration.type() == QVariant::Pixmap)
1786 return QIcon(qvariant_cast<QPixmap>(decoration));
1787 else
1788 return qvariant_cast<QIcon>(decoration);
1789}
1790
1791void QComboBox::setEditable(bool editable)
1792{
1793 Q_D(QComboBox);
1794 if (isEditable() == editable)
1795 return;
1796
1797 QStyleOptionComboBox opt;
1798 initStyleOption(&opt);
1799 if (editable) {
1800 if (style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this)) {
1801 d->viewContainer()->updateScrollers();
1802 view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
1803 }
1804 QLineEdit *le = new QLineEdit(this);
1805 setLineEdit(le);
1806 } else {
1807 if (style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this)) {
1808 d->viewContainer()->updateScrollers();
1809 view()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1810 }
1811 setAttribute(Qt::WA_InputMethodEnabled, false);
1812 d->lineEdit->hide();
1813 d->lineEdit->deleteLater();
1814 d->lineEdit = 0;
1815 }
1816
1817 d->updateDelegate();
1818 d->updateFocusPolicy();
1819
1820 d->viewContainer()->updateTopBottomMargin();
1821 if (!testAttribute(Qt::WA_Resized))
1822 adjustSize();
1823}
1824
1825/*!
1826 Sets the line \a edit to use instead of the current line edit widget.
1827
1828 The combo box takes ownership of the line edit.
1829*/
1830void QComboBox::setLineEdit(QLineEdit *edit)
1831{
1832 Q_D(QComboBox);
1833 if (Q_UNLIKELY(!edit)) {
1834 qWarning("QComboBox::setLineEdit: cannot set a 0 line edit");
1835 return;
1836 }
1837
1838 if (edit == d->lineEdit)
1839 return;
1840
1841 edit->setText(currentText());
1842 delete d->lineEdit;
1843
1844 d->lineEdit = edit;
1845#ifndef QT_NO_IM
1846 qt_widget_private(d->lineEdit)->inheritsInputMethodHints = 1;
1847#endif
1848 if (d->lineEdit->parent() != this)
1849 d->lineEdit->setParent(this);
1850 connect(d->lineEdit, SIGNAL(returnPressed()), this, SLOT(_q_returnPressed()));
1851 connect(d->lineEdit, SIGNAL(editingFinished()), this, SLOT(_q_editingFinished()));
1852 connect(d->lineEdit, SIGNAL(textChanged(QString)), this, SIGNAL(editTextChanged(QString)));
1853 connect(d->lineEdit, SIGNAL(textChanged(QString)), this, SIGNAL(currentTextChanged(QString)));
1854 connect(d->lineEdit, SIGNAL(cursorPositionChanged(int,int)), this, SLOT(updateMicroFocus()));
1855 connect(d->lineEdit, SIGNAL(selectionChanged()), this, SLOT(updateMicroFocus()));
1856 connect(d->lineEdit->d_func()->control, SIGNAL(updateMicroFocus()), this, SLOT(updateMicroFocus()));
1857 d->lineEdit->setFrame(false);
1858 d->lineEdit->setContextMenuPolicy(Qt::NoContextMenu);
1859 d->updateFocusPolicy();
1860 d->lineEdit->setFocusProxy(this);
1861 d->lineEdit->setAttribute(Qt::WA_MacShowFocusRect, false);
1862#if QT_CONFIG(completer)
1863 setAutoCompletion(d->autoCompletion);
1864#endif
1865
1866#ifdef QT_KEYPAD_NAVIGATION
1867#if QT_CONFIG(completer)
1868 if (QApplication::keypadNavigationEnabled()) {
1869 // Editable combo boxes will have a completer that is set to UnfilteredPopupCompletion.
1870 // This means that when the user enters edit mode they are immediately presented with a
1871 // list of possible completions.
1872 setAutoCompletion(true);
1873 if (d->completer) {
1874 d->completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion);
1875 connect(d->completer, SIGNAL(activated(QModelIndex)), this, SLOT(_q_completerActivated()));
1876 }
1877 }
1878#endif
1879#endif
1880
1881 setAttribute(Qt::WA_InputMethodEnabled);
1882 d->updateLayoutDirection();
1883 d->updateLineEditGeometry();
1884 if (isVisible())
1885 d->lineEdit->show();
1886
1887 update();
1888}
1889
1890/*!
1891 Returns the line edit used to edit items in the combobox, or 0 if there
1892 is no line edit.
1893
1894 Only editable combo boxes have a line edit.
1895*/
1896QLineEdit *QComboBox::lineEdit() const
1897{
1898 Q_D(const QComboBox);
1899 return d->lineEdit;
1900}
1901
1902#ifndef QT_NO_VALIDATOR
1903/*!
1904 \fn void QComboBox::setValidator(const QValidator *validator)
1905
1906 Sets the \a validator to use instead of the current validator.
1907
1908 \note The validator is removed when the \l editable property becomes \c false.
1909*/
1910
1911void QComboBox::setValidator(const QValidator *v)
1912{
1913 Q_D(QComboBox);
1914 if (d->lineEdit)
1915 d->lineEdit->setValidator(v);
1916}
1917
1918/*!
1919 Returns the validator that is used to constrain text input for the
1920 combobox.
1921
1922 \sa editable
1923*/
1924const QValidator *QComboBox::validator() const
1925{
1926 Q_D(const QComboBox);
1927 return d->lineEdit ? d->lineEdit->validator() : 0;
1928}
1929#endif // QT_NO_VALIDATOR
1930
1931#if QT_CONFIG(completer)
1932
1933/*!
1934 \fn void QComboBox::setCompleter(QCompleter *completer)
1935 \since 4.2
1936
1937 Sets the \a completer to use instead of the current completer.
1938 If \a completer is 0, auto completion is disabled.
1939
1940 By default, for an editable combo box, a QCompleter that
1941 performs case insensitive inline completion is automatically created.
1942
1943 \note The completer is removed when the \l editable property becomes \c false.
1944 Setting a completer on a QComboBox that is not editable will be ignored.
1945*/
1946void QComboBox::setCompleter(QCompleter *c)
1947{
1948 Q_D(QComboBox);
1949 if (!d->lineEdit) {
1950 qWarning("Setting a QCompleter on non-editable QComboBox is not allowed.");
1951 return;
1952 }
1953 d->lineEdit->setCompleter(c);
1954 if (c) {
1955 connect(c, SIGNAL(activated(QModelIndex)), this, SLOT(_q_completerActivated(QModelIndex)));
1956 c->setWidget(this);
1957 }
1958}
1959
1960/*!
1961 \since 4.2
1962
1963 Returns the completer that is used to auto complete text input for the
1964 combobox.
1965
1966 \sa editable
1967*/
1968QCompleter *QComboBox::completer() const
1969{
1970 Q_D(const QComboBox);
1971 return d->lineEdit ? d->lineEdit->completer() : 0;
1972}
1973
1974#endif // QT_CONFIG(completer)
1975
1976/*!
1977 Returns the item delegate used by the popup list view.
1978
1979 \sa setItemDelegate()
1980*/
1981QAbstractItemDelegate *QComboBox::itemDelegate() const
1982{
1983 return view()->itemDelegate();
1984}
1985
1986/*!
1987 Sets the item \a delegate for the popup list view.
1988 The combobox takes ownership of the delegate.
1989
1990 \warning You should not share the same instance of a delegate between comboboxes,
1991 widget mappers or views. Doing so can cause incorrect or unintuitive editing behavior
1992 since each view connected to a given delegate may receive the
1993 \l{QAbstractItemDelegate::}{closeEditor()} signal, and attempt to access, modify or
1994 close an editor that has already been closed.
1995
1996 \sa itemDelegate()
1997*/
1998void QComboBox::setItemDelegate(QAbstractItemDelegate *delegate)
1999{
2000 if (Q_UNLIKELY(!delegate)) {
2001 qWarning("QComboBox::setItemDelegate: cannot set a 0 delegate");
2002 return;
2003 }
2004 delete view()->itemDelegate();
2005 view()->setItemDelegate(delegate);
2006}
2007
2008/*!
2009 Returns the model used by the combobox.
2010*/
2011
2012QAbstractItemModel *QComboBox::model() const
2013{
2014 Q_D(const QComboBox);
2015 if (d->model == QAbstractItemModelPrivate::staticEmptyModel()) {
2016 QComboBox *that = const_cast<QComboBox*>(this);
2017 that->setModel(new QStandardItemModel(0, 1, that));
2018 }
2019 return d->model;
2020}
2021
2022/*!
2023 Sets the model to be \a model. \a model must not be 0.
2024 If you want to clear the contents of a model, call clear().
2025
2026 \sa clear()
2027*/
2028void QComboBox::setModel(QAbstractItemModel *model)
2029{
2030 Q_D(QComboBox);
2031
2032 if (Q_UNLIKELY(!model)) {
2033 qWarning("QComboBox::setModel: cannot set a 0 model");
2034 return;
2035 }
2036
2037 if (model == d->model)
2038 return;
2039
2040#if QT_CONFIG(completer)
2041 if (d->lineEdit && d->lineEdit->completer()
2042 && d->lineEdit->completer() == d->completer)
2043 d->lineEdit->completer()->setModel(model);
2044#endif
2045 if (d->model) {
2046 disconnect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
2047 this, SLOT(_q_dataChanged(QModelIndex,QModelIndex)));
2048 disconnect(d->model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
2049 this, SLOT(_q_updateIndexBeforeChange()));
2050 disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
2051 this, SLOT(_q_rowsInserted(QModelIndex,int,int)));
2052 disconnect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
2053 this, SLOT(_q_updateIndexBeforeChange()));
2054 disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
2055 this, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
2056 disconnect(d->model, SIGNAL(destroyed()),
2057 this, SLOT(_q_modelDestroyed()));
2058 disconnect(d->model, SIGNAL(modelAboutToBeReset()),
2059 this, SLOT(_q_updateIndexBeforeChange()));
2060 disconnect(d->model, SIGNAL(modelReset()),
2061 this, SLOT(_q_modelReset()));
2062 if (d->model->QObject::parent() == this)
2063 delete d->model;
2064 }
2065
2066 d->model = model;
2067
2068 connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
2069 this, SLOT(_q_dataChanged(QModelIndex,QModelIndex)));
2070 connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
2071 this, SLOT(_q_updateIndexBeforeChange()));
2072 connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
2073 this, SLOT(_q_rowsInserted(QModelIndex,int,int)));
2074 connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
2075 this, SLOT(_q_updateIndexBeforeChange()));
2076 connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
2077 this, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
2078 connect(model, SIGNAL(destroyed()),
2079 this, SLOT(_q_modelDestroyed()));
2080 connect(model, SIGNAL(modelAboutToBeReset()),
2081 this, SLOT(_q_updateIndexBeforeChange()));
2082 connect(model, SIGNAL(modelReset()),
2083 this, SLOT(_q_modelReset()));
2084
2085 if (d->container) {
2086 d->container->itemView()->setModel(model);
2087 connect(d->container->itemView()->selectionModel(),
2088 SIGNAL(currentChanged(QModelIndex,QModelIndex)),
2089 this, SLOT(_q_emitHighlighted(QModelIndex)), Qt::UniqueConnection);
2090 }
2091
2092 setRootModelIndex(QModelIndex());
2093
2094 bool currentReset = false;
2095
2096 const int rowCount = count();
2097 for (int pos=0; pos < rowCount; pos++) {
2098 if (d->model->index(pos, d->modelColumn, d->root).flags() & Qt::ItemIsEnabled) {
2099 setCurrentIndex(pos);
2100 currentReset = true;
2101 break;
2102 }
2103 }
2104
2105 if (!currentReset)
2106 setCurrentIndex(-1);
2107
2108 d->modelChanged();
2109}
2110
2111/*!
2112 Returns the root model item index for the items in the combobox.
2113
2114 \sa setRootModelIndex()
2115*/
2116
2117QModelIndex QComboBox::rootModelIndex() const
2118{
2119 Q_D(const QComboBox);
2120 return QModelIndex(d->root);
2121}
2122
2123/*!
2124 Sets the root model item \a index for the items in the combobox.
2125
2126 \sa rootModelIndex()
2127*/
2128void QComboBox::setRootModelIndex(const QModelIndex &index)
2129{
2130 Q_D(QComboBox);
2131 if (d->root == index)
2132 return;
2133 d->root = QPersistentModelIndex(index);
2134 view()->setRootIndex(index);
2135 update();
2136}
2137
2138/*!
2139 \property QComboBox::currentIndex
2140 \brief the index of the current item in the combobox.
2141
2142 The current index can change when inserting or removing items.
2143
2144 By default, for an empty combo box or a combo box in which no current
2145 item is set, this property has a value of -1.
2146*/
2147int QComboBox::currentIndex() const
2148{
2149 Q_D(const QComboBox);
2150 return d->currentIndex.row();
2151}
2152
2153void QComboBox::setCurrentIndex(int index)
2154{
2155 Q_D(QComboBox);
2156 QModelIndex mi = d->model->index(index, d->modelColumn, d->root);
2157 d->setCurrentIndex(mi);
2158}
2159
2160void QComboBox::setCurrentText(const QString &text)
2161{
2162 if (isEditable()) {
2163 setEditText(text);
2164 } else {
2165 const int i = findText(text);
2166 if (i > -1)
2167 setCurrentIndex(i);
2168 }
2169}
2170
2171void QComboBoxPrivate::setCurrentIndex(const QModelIndex &mi)
2172{
2173 Q_Q(QComboBox);
2174
2175 QModelIndex normalized = mi.sibling(mi.row(), modelColumn); // no-op if mi.column() == modelColumn
2176 if (!normalized.isValid())
2177 normalized = mi; // Fallback to passed index.
2178
2179 bool indexChanged = (normalized != currentIndex);
2180 if (indexChanged)
2181 currentIndex = QPersistentModelIndex(normalized);
2182 if (lineEdit) {
2183 const QString newText = itemText(normalized);
2184 if (lineEdit->text() != newText) {
2185 lineEdit->setText(newText); // may cause lineEdit -> nullptr (QTBUG-54191)
2186#if QT_CONFIG(completer)
2187 if (lineEdit && lineEdit->completer())
2188 lineEdit->completer()->setCompletionPrefix(newText);
2189#endif
2190 }
2191 updateLineEditGeometry();
2192 }
2193 if (indexChanged) {
2194 q->update();
2195 _q_emitCurrentIndexChanged(currentIndex);
2196 }
2197}
2198
2199/*!
2200 \property QComboBox::currentText
2201 \brief the current text
2202
2203 If the combo box is editable, the current text is the value displayed
2204 by the line edit. Otherwise, it is the value of the current item or
2205 an empty string if the combo box is empty or no current item is set.
2206
2207 The setter setCurrentText() simply calls setEditText() if the combo box is editable.
2208 Otherwise, if there is a matching text in the list, currentIndex is set to the
2209 corresponding index.
2210
2211 \sa editable, setEditText()
2212*/
2213QString QComboBox::currentText() const
2214{
2215 Q_D(const QComboBox);
2216 if (d->lineEdit)
2217 return d->lineEdit->text();
2218 else if (d->currentIndex.isValid())
2219 return d->itemText(d->currentIndex);
2220 else
2221 return QString();
2222}
2223
2224/*!
2225 \property QComboBox::currentData
2226 \brief the data for the current item
2227 \since 5.2
2228
2229 By default, for an empty combo box or a combo box in which no current
2230 item is set, this property contains an invalid QVariant.
2231*/
2232QVariant QComboBox::currentData(int role) const
2233{
2234 Q_D(const QComboBox);
2235 return d->currentIndex.data(role);
2236}
2237
2238/*!
2239 Returns the text for the given \a index in the combobox.
2240*/
2241QString QComboBox::itemText(int index) const
2242{
2243 Q_D(const QComboBox);
2244 QModelIndex mi = d->model->index(index, d->modelColumn, d->root);
2245 return d->itemText(mi);
2246}
2247
2248/*!
2249 Returns the icon for the given \a index in the combobox.
2250*/
2251QIcon QComboBox::itemIcon(int index) const
2252{
2253 Q_D(const QComboBox);
2254 QModelIndex mi = d->model->index(index, d->modelColumn, d->root);
2255 return d->itemIcon(mi);
2256}
2257
2258/*!
2259 Returns the data for the given \a role in the given \a index in the
2260 combobox, or QVariant::Invalid if there is no data for this role.
2261*/
2262QVariant QComboBox::itemData(int index, int role) const
2263{
2264 Q_D(const QComboBox);
2265 QModelIndex mi = d->model->index(index, d->modelColumn, d->root);
2266 return d->model->data(mi, role);
2267}
2268
2269/*!
2270 \fn void QComboBox::insertItem(int index, const QString &text, const QVariant &userData)
2271
2272 Inserts the \a text and \a userData (stored in the Qt::UserRole)
2273 into the combobox at the given \a index.
2274
2275 If the index is equal to or higher than the total number of items,
2276 the new item is appended to the list of existing items. If the
2277 index is zero or negative, the new item is prepended to the list
2278 of existing items.
2279
2280 \sa insertItems()
2281*/
2282
2283/*!
2284
2285 Inserts the \a icon, \a text and \a userData (stored in the
2286 Qt::UserRole) into the combobox at the given \a index.
2287
2288 If the index is equal to or higher than the total number of items,
2289 the new item is appended to the list of existing items. If the
2290 index is zero or negative, the new item is prepended to the list
2291 of existing items.
2292
2293 \sa insertItems()
2294*/
2295void QComboBox::insertItem(int index, const QIcon &icon, const QString &text, const QVariant &userData)
2296{
2297 Q_D(QComboBox);
2298 int itemCount = count();
2299 index = qBound(0, index, itemCount);
2300 if (index >= d->maxCount)
2301 return;
2302
2303 // For the common case where we are using the built in QStandardItemModel
2304 // construct a QStandardItem, reducing the number of expensive signals from the model
2305 if (QStandardItemModel *m = qobject_cast<QStandardItemModel*>(d->model)) {
2306 QStandardItem *item = new QStandardItem(text);
2307 if (!icon.isNull()) item->setData(icon, Qt::DecorationRole);
2308 if (userData.isValid()) item->setData(userData, Qt::UserRole);
2309 m->insertRow(index, item);
2310 ++itemCount;
2311 } else {
2312 d->inserting = true;
2313 if (d->model->insertRows(index, 1, d->root)) {
2314 QModelIndex item = d->model->index(index, d->modelColumn, d->root);
2315 if (icon.isNull() && !userData.isValid()) {
2316 d->model->setData(item, text, Qt::EditRole);
2317 } else {
2318 QMap<int, QVariant> values;
2319 if (!text.isNull()) values.insert(Qt::EditRole, text);
2320 if (!icon.isNull()) values.insert(Qt::DecorationRole, icon);
2321 if (userData.isValid()) values.insert(Qt::UserRole, userData);
2322 if (!values.isEmpty()) d->model->setItemData(item, values);
2323 }
2324 d->inserting = false;
2325 d->_q_rowsInserted(d->root, index, index);
2326 ++itemCount;
2327 } else {
2328 d->inserting = false;
2329 }
2330 }
2331
2332 if (itemCount > d->maxCount)
2333 d->model->removeRows(itemCount - 1, itemCount - d->maxCount, d->root);
2334}
2335
2336/*!
2337 Inserts the strings from the \a list into the combobox as separate items,
2338 starting at the \a index specified.
2339
2340 If the index is equal to or higher than the total number of items, the new items
2341 are appended to the list of existing items. If the index is zero or negative, the
2342 new items are prepended to the list of existing items.
2343
2344 \sa insertItem()
2345 */
2346void QComboBox::insertItems(int index, const QStringList &list)
2347{
2348 Q_D(QComboBox);
2349 if (list.isEmpty())
2350 return;
2351 index = qBound(0, index, count());
2352 int insertCount = qMin(d->maxCount - index, list.count());
2353 if (insertCount <= 0)
2354 return;
2355 // For the common case where we are using the built in QStandardItemModel
2356 // construct a QStandardItem, reducing the number of expensive signals from the model
2357 if (QStandardItemModel *m = qobject_cast<QStandardItemModel*>(d->model)) {
2358 QList<QStandardItem *> items;
2359 items.reserve(insertCount);
2360 QStandardItem *hiddenRoot = m->invisibleRootItem();
2361 for (int i = 0; i < insertCount; ++i)
2362 items.append(new QStandardItem(list.at(i)));
2363 hiddenRoot->insertRows(index, items);
2364 } else {
2365 d->inserting = true;
2366 if (d->model->insertRows(index, insertCount, d->root)) {
2367 QModelIndex item;
2368 for (int i = 0; i < insertCount; ++i) {
2369 item = d->model->index(i+index, d->modelColumn, d->root);
2370 d->model->setData(item, list.at(i), Qt::EditRole);
2371 }
2372 d->inserting = false;
2373 d->_q_rowsInserted(d->root, index, index + insertCount - 1);
2374 } else {
2375 d->inserting = false;
2376 }
2377 }
2378
2379 int mc = count();
2380 if (mc > d->maxCount)
2381 d->model->removeRows(d->maxCount, mc - d->maxCount, d->root);
2382}
2383
2384/*!
2385 \since 4.4
2386
2387 Inserts a separator item into the combobox at the given \a index.
2388
2389 If the index is equal to or higher than the total number of items, the new item
2390 is appended to the list of existing items. If the index is zero or negative, the
2391 new item is prepended to the list of existing items.
2392
2393 \sa insertItem()
2394*/
2395void QComboBox::insertSeparator(int index)
2396{
2397 Q_D(QComboBox);
2398 int itemCount = count();
2399 index = qBound(0, index, itemCount);
2400 if (index >= d->maxCount)
2401 return;
2402 insertItem(index, QIcon(), QString());
2403 QComboBoxDelegate::setSeparator(d->model, d->model->index(index, 0, d->root));
2404}
2405
2406/*!
2407 Removes the item at the given \a index from the combobox.
2408 This will update the current index if the index is removed.
2409
2410 This function does nothing if \a index is out of range.
2411*/
2412void QComboBox::removeItem(int index)
2413{
2414 Q_D(QComboBox);
2415 if (index < 0 || index >= count())
2416 return;
2417 d->model->removeRows(index, 1, d->root);
2418}
2419
2420/*!
2421 Sets the \a text for the item on the given \a index in the combobox.
2422*/
2423void QComboBox::setItemText(int index, const QString &text)
2424{
2425 Q_D(const QComboBox);
2426 QModelIndex item = d->model->index(index, d->modelColumn, d->root);
2427 if (item.isValid()) {
2428 d->model->setData(item, text, Qt::EditRole);
2429 }
2430}
2431
2432/*!
2433 Sets the \a icon for the item on the given \a index in the combobox.
2434*/
2435void QComboBox::setItemIcon(int index, const QIcon &icon)
2436{
2437 Q_D(const QComboBox);
2438 QModelIndex item = d->model->index(index, d->modelColumn, d->root);
2439 if (item.isValid()) {
2440 d->model->setData(item, icon, Qt::DecorationRole);
2441 }
2442}
2443
2444/*!
2445 Sets the data \a role for the item on the given \a index in the combobox
2446 to the specified \a value.
2447*/
2448void QComboBox::setItemData(int index, const QVariant &value, int role)
2449{
2450 Q_D(const QComboBox);
2451 QModelIndex item = d->model->index(index, d->modelColumn, d->root);
2452 if (item.isValid()) {
2453 d->model->setData(item, value, role);
2454 }
2455}
2456
2457/*!
2458 Returns the list view used for the combobox popup.
2459*/
2460QAbstractItemView *QComboBox::view() const
2461{
2462 Q_D(const QComboBox);
2463 return const_cast<QComboBoxPrivate*>(d)->viewContainer()->itemView();
2464}
2465
2466/*!
2467 Sets the view to be used in the combobox popup to the given \a
2468 itemView. The combobox takes ownership of the view.
2469
2470 Note: If you want to use the convenience views (like QListWidget,
2471 QTableWidget or QTreeWidget), make sure to call setModel() on the
2472 combobox with the convenience widgets model before calling this
2473 function.
2474*/
2475void QComboBox::setView(QAbstractItemView *itemView)
2476{
2477 Q_D(QComboBox);
2478 if (Q_UNLIKELY(!itemView)) {
2479 qWarning("QComboBox::setView: cannot set a 0 view");
2480 return;
2481 }
2482
2483 if (itemView->model() != d->model)
2484 itemView->setModel(d->model);
2485 d->viewContainer()->setItemView(itemView);
2486}
2487
2488/*!
2489 \reimp
2490*/
2491QSize QComboBox::minimumSizeHint() const
2492{
2493 Q_D(const QComboBox);
2494 return d->recomputeSizeHint(d->minimumSizeHint);
2495}
2496
2497/*!
2498 \reimp
2499
2500 This implementation caches the size hint to avoid resizing when
2501 the contents change dynamically. To invalidate the cached value
2502 change the \l sizeAdjustPolicy.
2503*/
2504QSize QComboBox::sizeHint() const
2505{
2506 Q_D(const QComboBox);
2507 return d->recomputeSizeHint(d->sizeHint);
2508}
2509
2510#ifdef Q_OS_MAC
2511
2512namespace {
2513struct IndexSetter {
2514 int index;
2515 QComboBox *cb;
2516
2517 void operator()(void)
2518 {
2519 cb->setCurrentIndex(index);
2520 emit cb->activated(index);
2521 emit cb->activated(cb->itemText(index));
2522 }
2523};
2524}
2525
2526void QComboBoxPrivate::cleanupNativePopup()
2527{
2528 if (!m_platformMenu)
2529 return;
2530
2531 int count = int(m_platformMenu->tag());
2532 for (int i = 0; i < count; ++i)
2533 m_platformMenu->menuItemAt(i)->deleteLater();
2534
2535 delete m_platformMenu;
2536 m_platformMenu = 0;
2537}
2538
2539/*!
2540 * \internal
2541 *
2542 * Tries to show a native popup. Returns true if it could, false otherwise.
2543 *
2544 */
2545bool QComboBoxPrivate::showNativePopup()
2546{
2547 Q_Q(QComboBox);
2548
2549 cleanupNativePopup();
2550
2551 QPlatformTheme *theme = QGuiApplicationPrivate::instance()->platformTheme();
2552 m_platformMenu = theme->createPlatformMenu();
2553 if (!m_platformMenu)
2554 return false;
2555
2556 int itemsCount = q->count();
2557 m_platformMenu->setTag(quintptr(itemsCount));
2558
2559 QPlatformMenuItem *currentItem = 0;
2560 int currentIndex = q->currentIndex();
2561
2562 for (int i = 0; i < itemsCount; ++i) {
2563 QPlatformMenuItem *item = theme->createPlatformMenuItem();
2564 QModelIndex rowIndex = model->index(i, modelColumn, root);
2565 QVariant textVariant = model->data(rowIndex, Qt::EditRole);
2566 item->setText(textVariant.toString());
2567 QVariant iconVariant = model->data(rowIndex, Qt::DecorationRole);
2568 if (iconVariant.canConvert<QIcon>())
2569 item->setIcon(iconVariant.value<QIcon>());
2570 item->setCheckable(true);
2571 item->setChecked(i == currentIndex);
2572 if (!currentItem || i == currentIndex)
2573 currentItem = item;
2574
2575 IndexSetter setter = { i, q };
2576 QObject::connect(item, &QPlatformMenuItem::activated, setter);
2577
2578 m_platformMenu->insertMenuItem(item, 0);
2579 m_platformMenu->syncMenuItem(item);
2580 }
2581
2582 QWindow *tlw = q->window()->windowHandle();
2583 m_platformMenu->setFont(q->font());
2584 m_platformMenu->setMinimumWidth(q->rect().width());
2585 QPoint offset = QPoint(0, 7);
2586 if (q->testAttribute(Qt::WA_MacSmallSize))
2587 offset = QPoint(-1, 7);
2588 else if (q->testAttribute(Qt::WA_MacMiniSize))
2589 offset = QPoint(-2, 6);
2590
2591 const QRect targetRect = QRect(tlw->mapFromGlobal(q->mapToGlobal(offset)), QSize());
2592 m_platformMenu->showPopup(tlw, QHighDpi::toNativePixels(targetRect, tlw), currentItem);
2593
2594#ifdef Q_OS_OSX
2595 // The Cocoa popup will swallow any mouse release event.
2596 // We need to fake one here to un-press the button.
2597 QMouseEvent mouseReleased(QEvent::MouseButtonRelease, q->pos(), Qt::LeftButton,
2598 Qt::MouseButtons(Qt::LeftButton), Qt::KeyboardModifiers());
2599 qApp->sendEvent(q, &mouseReleased);
2600#endif
2601
2602 return true;
2603}
2604
2605#endif // Q_OS_MAC
2606
2607/*!
2608 Displays the list of items in the combobox. If the list is empty
2609 then the no items will be shown.
2610
2611 If you reimplement this function to show a custom pop-up, make
2612 sure you call hidePopup() to reset the internal state.
2613
2614 \sa hidePopup()
2615*/
2616void QComboBox::showPopup()
2617{
2618 Q_D(QComboBox);
2619 if (count() <= 0)
2620 return;
2621
2622 QStyle * const style = this->style();
2623 QStyleOptionComboBox opt;
2624 initStyleOption(&opt);
2625 const bool usePopup = style->styleHint(QStyle::SH_ComboBox_Popup, &opt, this);
2626
2627#ifdef Q_OS_MAC
2628 if (usePopup
2629 && (!d->container
2630 || (view()->metaObject()->className() == QByteArray("QComboBoxListView")
2631 && view()->itemDelegate()->metaObject()->className() == QByteArray("QComboMenuDelegate")))
2632 && style->styleHint(QStyle::SH_ComboBox_UseNativePopup, &opt, this)
2633 && d->showNativePopup())
2634 return;
2635#endif // Q_OS_MAC
2636
2637#ifdef QT_KEYPAD_NAVIGATION
2638#if QT_CONFIG(completer)
2639 if (QApplication::keypadNavigationEnabled() && d->completer) {
2640 // editable combo box is line edit plus completer
2641 setEditFocus(true);
2642 d->completer->complete(); // show popup
2643 return;
2644 }
2645#endif
2646#endif
2647
2648 // set current item and select it
2649 view()->selectionModel()->setCurrentIndex(d->currentIndex,
2650 QItemSelectionModel::ClearAndSelect);
2651 QComboBoxPrivateContainer* container = d->viewContainer();
2652 QRect listRect(style->subControlRect(QStyle::CC_ComboBox, &opt,
2653 QStyle::SC_ComboBoxListBoxPopup, this));
2654 QRect screen = d->popupGeometry(QDesktopWidgetPrivate::screenNumber(this));
2655
2656 QPoint below = mapToGlobal(listRect.bottomLeft());
2657 int belowHeight = screen.bottom() - below.y();
2658 QPoint above = mapToGlobal(listRect.topLeft());
2659 int aboveHeight = above.y() - screen.y();
2660 bool boundToScreen = !window()->testAttribute(Qt::WA_DontShowOnScreen);
2661
2662 {
2663 int listHeight = 0;
2664 int count = 0;
2665 QStack<QModelIndex> toCheck;
2666 toCheck.push(view()->rootIndex());
2667#if QT_CONFIG(treeview)
2668 QTreeView *treeView = qobject_cast<QTreeView*>(view());
2669 if (treeView && treeView->header() && !treeView->header()->isHidden())
2670 listHeight += treeView->header()->height();
2671#endif
2672 while (!toCheck.isEmpty()) {
2673 QModelIndex parent = toCheck.pop();
2674 for (int i = 0, end = d->model->rowCount(parent); i < end; ++i) {
2675 QModelIndex idx = d->model->index(i, d->modelColumn, parent);
2676 if (!idx.isValid())
2677 continue;
2678 listHeight += view()->visualRect(idx).height();
2679#if QT_CONFIG(treeview)
2680 if (d->model->hasChildren(idx) && treeView && treeView->isExpanded(idx))
2681 toCheck.push(idx);
2682#endif
2683 ++count;
2684 if (!usePopup && count >= d->maxVisibleItems) {
2685 toCheck.clear();
2686 break;
2687 }
2688 }
2689 }
2690 if (count > 1)
2691 listHeight += (count - 1) * container->spacing();
2692 listRect.setHeight(listHeight);
2693 }
2694
2695 {
2696 // add the spacing for the grid on the top and the bottom;
2697 int heightMargin = container->topMargin() + container->bottomMargin();
2698
2699 // add the frame of the container
2700 int marginTop, marginBottom;
2701 container->getContentsMargins(0, &marginTop, 0, &marginBottom);
2702 heightMargin += marginTop + marginBottom;
2703
2704 //add the frame of the view
2705 view()->getContentsMargins(0, &marginTop, 0, &marginBottom);
2706 marginTop += static_cast<QAbstractScrollAreaPrivate *>(QObjectPrivate::get(view()))->top;
2707 marginBottom += static_cast<QAbstractScrollAreaPrivate *>(QObjectPrivate::get(view()))->bottom;
2708 heightMargin += marginTop + marginBottom;
2709
2710 listRect.setHeight(listRect.height() + heightMargin);
2711 }
2712
2713 // Add space for margin at top and bottom if the style wants it.
2714 if (usePopup)
2715 listRect.setHeight(listRect.height() + style->pixelMetric(QStyle::PM_MenuVMargin, &opt, this) * 2);
2716
2717 // Make sure the popup is wide enough to display its contents.
2718 if (usePopup) {
2719 const int diff = d->computeWidthHint() - width();
2720 if (diff > 0)
2721 listRect.setWidth(listRect.width() + diff);
2722 }
2723
2724 //we need to activate the layout to make sure the min/maximum size are set when the widget was not yet show
2725 container->layout()->activate();
2726 //takes account of the minimum/maximum size of the container
2727 listRect.setSize( listRect.size().expandedTo(container->minimumSize())
2728 .boundedTo(container->maximumSize()));
2729
2730 // make sure the widget fits on screen
2731 if (boundToScreen) {
2732 if (listRect.width() > screen.width() )
2733 listRect.setWidth(screen.width());
2734 if (mapToGlobal(listRect.bottomRight()).x() > screen.right()) {
2735 below.setX(screen.x() + screen.width() - listRect.width());
2736 above.setX(screen.x() + screen.width() - listRect.width());
2737 }
2738 if (mapToGlobal(listRect.topLeft()).x() < screen.x() ) {
2739 below.setX(screen.x());
2740 above.setX(screen.x());
2741 }
2742 }
2743
2744 if (usePopup) {
2745 // Position horizontally.
2746 listRect.moveLeft(above.x());
2747
2748 // Position vertically so the curently selected item lines up
2749 // with the combo box.
2750 const QRect currentItemRect = view()->visualRect(view()->currentIndex());
2751 const int offset = listRect.top() - currentItemRect.top();
2752 listRect.moveTop(above.y() + offset - listRect.top());
2753
2754 // Clamp the listRect height and vertical position so we don't expand outside the
2755 // available screen geometry.This may override the vertical position, but it is more
2756 // important to show as much as possible of the popup.
2757 const int height = !boundToScreen ? listRect.height() : qMin(listRect.height(), screen.height());
2758 listRect.setHeight(height);
2759
2760 if (boundToScreen) {
2761 if (listRect.top() < screen.top())
2762 listRect.moveTop(screen.top());
2763 if (listRect.bottom() > screen.bottom())
2764 listRect.moveBottom(screen.bottom());
2765 }
2766 } else if (!boundToScreen || listRect.height() <= belowHeight) {
2767 listRect.moveTopLeft(below);
2768 } else if (listRect.height() <= aboveHeight) {
2769 listRect.moveBottomLeft(above);
2770 } else if (belowHeight >= aboveHeight) {
2771 listRect.setHeight(belowHeight);
2772 listRect.moveTopLeft(below);
2773 } else {
2774 listRect.setHeight(aboveHeight);
2775 listRect.moveBottomLeft(above);
2776 }
2777
2778 if (qApp) {
2779 QGuiApplication::inputMethod()->reset();
2780 }
2781
2782 QScrollBar *sb = view()->horizontalScrollBar();
2783 Qt::ScrollBarPolicy policy = view()->horizontalScrollBarPolicy();
2784 bool needHorizontalScrollBar = (policy == Qt::ScrollBarAsNeeded || policy == Qt::ScrollBarAlwaysOn)
2785 && sb->minimum() < sb->maximum();
2786 if (needHorizontalScrollBar) {
2787 listRect.adjust(0, 0, 0, sb->height());
2788 }
2789
2790 // Hide the scrollers here, so that the listrect gets the full height of the container
2791 // If the scrollers are truly needed, the later call to container->updateScrollers()
2792 // will make them visible again.
2793 container->hideScrollers();
2794 container->setGeometry(listRect);
2795
2796#ifndef Q_OS_MAC
2797 const bool updatesEnabled = container->updatesEnabled();
2798#endif
2799
2800#if QT_CONFIG(effects)
2801 bool scrollDown = (listRect.topLeft() == below);
2802 if (QApplication::isEffectEnabled(Qt::UI_AnimateCombo)
2803 && !style->styleHint(QStyle::SH_ComboBox_Popup, &opt, this) && !window()->testAttribute(Qt::WA_DontShowOnScreen))
2804 qScrollEffect(container, scrollDown ? QEffects::DownScroll : QEffects::UpScroll, 150);
2805#endif
2806
2807// Don't disable updates on OS X. Windows are displayed immediately on this platform,
2808// which means that the window will be visible before the call to container->show() returns.
2809// If updates are disabled at this point we'll miss our chance at painting the popup
2810// menu before it's shown, causing flicker since the window then displays the standard gray
2811// background.
2812#ifndef Q_OS_MAC
2813 container->setUpdatesEnabled(false);
2814#endif
2815
2816 bool startTimer = !container->isVisible();
2817 container->raise();
2818 container->create();
2819 QWindow *containerWindow = container->window()->windowHandle();
2820 if (containerWindow) {
2821 QWindow *win = window()->windowHandle();
2822 if (win) {
2823 QScreen *currentScreen = win->screen();
2824 if (currentScreen && !currentScreen->virtualSiblings().contains(containerWindow->screen())) {
2825 containerWindow->setScreen(currentScreen);
2826
2827 // This seems to workaround an issue in xcb+multi GPU+multiscreen
2828 // environment where the window might not always show up when screen
2829 // is changed.
2830 container->hide();
2831 }
2832 }
2833 }
2834 container->show();
2835 container->updateScrollers();
2836 view()->setFocus();
2837
2838 view()->scrollTo(view()->currentIndex(),
2839 style->styleHint(QStyle::SH_ComboBox_Popup, &opt, this)
2840 ? QAbstractItemView::PositionAtCenter
2841 : QAbstractItemView::EnsureVisible);
2842
2843#ifndef Q_OS_MAC
2844 container->setUpdatesEnabled(updatesEnabled);
2845#endif
2846
2847 container->update();
2848#ifdef QT_KEYPAD_NAVIGATION
2849 if (QApplication::keypadNavigationEnabled())
2850 view()->setEditFocus(true);
2851#endif
2852 if (startTimer) {
2853 container->popupTimer.start();
2854 container->maybeIgnoreMouseButtonRelease = true;
2855 }
2856}
2857
2858/*!
2859 Hides the list of items in the combobox if it is currently visible
2860 and resets the internal state, so that if the custom pop-up was
2861 shown inside the reimplemented showPopup(), then you also need to
2862 reimplement the hidePopup() function to hide your custom pop-up
2863 and call the base class implementation to reset the internal state
2864 whenever your custom pop-up widget is hidden.
2865
2866 \sa showPopup()
2867*/
2868void QComboBox::hidePopup()
2869{
2870 Q_D(QComboBox);
2871 if (d->container && d->container->isVisible()) {
2872#if QT_CONFIG(effects)
2873 QSignalBlocker modelBlocker(d->model);
2874 QSignalBlocker viewBlocker(d->container->itemView());
2875 QSignalBlocker containerBlocker(d->container);
2876 // Flash selected/triggered item (if any).
2877 if (style()->styleHint(QStyle::SH_Menu_FlashTriggeredItem)) {
2878 QItemSelectionModel *selectionModel = view() ? view()->selectionModel() : 0;
2879 if (selectionModel && selectionModel->hasSelection()) {
2880 QEventLoop eventLoop;
2881 const QItemSelection selection = selectionModel->selection();
2882
2883 // Deselect item and wait 60 ms.
2884 selectionModel->select(selection, QItemSelectionModel::Toggle);
2885 QTimer::singleShot(60, &eventLoop, SLOT(quit()));
2886 eventLoop.exec();
2887
2888 // Select item and wait 20 ms.
2889 selectionModel->select(selection, QItemSelectionModel::Toggle);
2890 QTimer::singleShot(20, &eventLoop, SLOT(quit()));
2891 eventLoop.exec();
2892 }
2893 }
2894
2895 // Fade out.
2896 bool needFade = style()->styleHint(QStyle::SH_Menu_FadeOutOnHide);
2897 bool didFade = false;
2898 if (needFade) {
2899#if defined(Q_OS_MAC)
2900 QPlatformNativeInterface *platformNativeInterface = qApp->platformNativeInterface();
2901 int at = platformNativeInterface->metaObject()->indexOfMethod("fadeWindow()");
2902 if (at != -1) {
2903 QMetaMethod windowFade = platformNativeInterface->metaObject()->method(at);
2904 windowFade.invoke(platformNativeInterface, Q_ARG(QWindow *, d->container->windowHandle()));
2905 didFade = true;
2906 }
2907
2908#endif // Q_OS_MAC
2909 // Other platform implementations welcome :-)
2910 }
2911 containerBlocker.unblock();
2912 viewBlocker.unblock();
2913 modelBlocker.unblock();
2914
2915 if (!didFade)
2916#endif // QT_CONFIG(effects)
2917 // Fade should implicitly hide as well ;-)
2918 d->container->hide();
2919 }
2920#ifdef QT_KEYPAD_NAVIGATION
2921 if (QApplication::keypadNavigationEnabled() && isEditable() && hasFocus())
2922 setEditFocus(true);
2923#endif
2924 d->_q_resetButton();
2925}
2926
2927/*!
2928 Clears the combobox, removing all items.
2929
2930 Note: If you have set an external model on the combobox this model
2931 will still be cleared when calling this function.
2932*/
2933void QComboBox::clear()
2934{
2935 Q_D(QComboBox);
2936 d->model->removeRows(0, d->model->rowCount(d->root), d->root);
2937#ifndef QT_NO_ACCESSIBILITY
2938 QAccessibleValueChangeEvent event(this, QString());
2939 QAccessible::updateAccessibility(&event);
2940#endif
2941}
2942
2943/*!
2944 Clears the contents of the line edit used for editing in the combobox.
2945*/
2946void QComboBox::clearEditText()
2947{
2948 Q_D(QComboBox);
2949 if (d->lineEdit)
2950 d->lineEdit->clear();
2951#ifndef QT_NO_ACCESSIBILITY
2952 QAccessibleValueChangeEvent event(this, QString());
2953 QAccessible::updateAccessibility(&event);
2954#endif
2955}
2956
2957/*!
2958 Sets the \a text in the combobox's text edit.
2959*/
2960void QComboBox::setEditText(const QString &text)
2961{
2962 Q_D(QComboBox);
2963 if (d->lineEdit)
2964 d->lineEdit->setText(text);
2965#ifndef QT_NO_ACCESSIBILITY
2966 QAccessibleValueChangeEvent event(this, text);
2967 QAccessible::updateAccessibility(&event);
2968#endif
2969}
2970
2971/*!
2972 \reimp
2973*/
2974void QComboBox::focusInEvent(QFocusEvent *e)
2975{
2976 Q_D(QComboBox);
2977 update();
2978 if (d->lineEdit) {
2979 d->lineEdit->event(e);
2980#if QT_CONFIG(completer)
2981 if (d->lineEdit->completer())
2982 d->lineEdit->completer()->setWidget(this);
2983#endif
2984 }
2985}
2986
2987/*!
2988 \reimp
2989*/
2990void QComboBox::focusOutEvent(QFocusEvent *e)
2991{
2992 Q_D(QComboBox);
2993 update();
2994 if (d->lineEdit)
2995 d->lineEdit->event(e);
2996}
2997
2998/*! \reimp */
2999void QComboBox::changeEvent(QEvent *e)
3000{
3001 Q_D(QComboBox);
3002 switch (e->type()) {
3003 case QEvent::StyleChange:
3004 d->updateDelegate();
3005#ifdef Q_OS_MAC
3006 case QEvent::MacSizeChange:
3007#endif
3008 d->sizeHint = QSize(); // invalidate size hint
3009 d->minimumSizeHint = QSize();
3010 d->updateLayoutDirection();
3011 if (d->lineEdit)
3012 d->updateLineEditGeometry();
3013 d->setLayoutItemMargins(QStyle::SE_ComboBoxLayoutItem);
3014
3015 if (e->type() == QEvent::MacSizeChange){
3016 QPlatformTheme::Font f = QPlatformTheme::SystemFont;
3017 if (testAttribute(Qt::WA_MacSmallSize))
3018 f = QPlatformTheme::SmallFont;
3019 else if (testAttribute(Qt::WA_MacMiniSize))
3020 f = QPlatformTheme::MiniFont;
3021 if (const QFont *platformFont = QApplicationPrivate::platformTheme()->font(f)) {
3022 QFont f = font();
3023 f.setPointSizeF(platformFont->pointSizeF());
3024 setFont(f);
3025 }
3026 }
3027 // ### need to update scrollers etc. as well here
3028 break;
3029 case QEvent::EnabledChange:
3030 if (!isEnabled())
3031 hidePopup();
3032 break;
3033 case QEvent::PaletteChange: {
3034 d->updateViewContainerPaletteAndOpacity();
3035 break;
3036 }
3037 case QEvent::FontChange:
3038 d->