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