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