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