|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the QtGui module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #ifndef QCOMBOBOX_P_H |
|
43 #define QCOMBOBOX_P_H |
|
44 |
|
45 // |
|
46 // W A R N I N G |
|
47 // ------------- |
|
48 // |
|
49 // This file is not part of the Qt API. It exists purely as an |
|
50 // implementation detail. This header file may change from version to |
|
51 // version without notice, or even be removed. |
|
52 // |
|
53 // We mean it. |
|
54 // |
|
55 |
|
56 #include "QtGui/qcombobox.h" |
|
57 |
|
58 #ifndef QT_NO_COMBOBOX |
|
59 #include "QtGui/qabstractslider.h" |
|
60 #include "QtGui/qapplication.h" |
|
61 #include "QtGui/qitemdelegate.h" |
|
62 #include "QtGui/qstandarditemmodel.h" |
|
63 #include "QtGui/qlineedit.h" |
|
64 #include "QtGui/qlistview.h" |
|
65 #include "QtGui/qpainter.h" |
|
66 #include "QtGui/qstyle.h" |
|
67 #include "QtGui/qstyleoption.h" |
|
68 #include "QtCore/qhash.h" |
|
69 #include "QtCore/qpair.h" |
|
70 #include "QtCore/qtimer.h" |
|
71 #include "private/qwidget_p.h" |
|
72 #include "QtCore/qpointer.h" |
|
73 #include "QtGui/qcompleter.h" |
|
74 #include "QtGui/qevent.h" |
|
75 #include "QtCore/qdebug.h" |
|
76 |
|
77 #include <limits.h> |
|
78 |
|
79 QT_BEGIN_NAMESPACE |
|
80 |
|
81 class QAction; |
|
82 |
|
83 class QComboBoxListView : public QListView |
|
84 { |
|
85 Q_OBJECT |
|
86 public: |
|
87 QComboBoxListView(QComboBox *cmb = 0) : combo(cmb) {} |
|
88 |
|
89 protected: |
|
90 void resizeEvent(QResizeEvent *event) |
|
91 { |
|
92 resizeContents(viewport()->width(), contentsSize().height()); |
|
93 QListView::resizeEvent(event); |
|
94 } |
|
95 |
|
96 QStyleOptionViewItem viewOptions() const |
|
97 { |
|
98 QStyleOptionViewItem option = QListView::viewOptions(); |
|
99 option.showDecorationSelected = true; |
|
100 if (combo) |
|
101 option.font = combo->font(); |
|
102 return option; |
|
103 } |
|
104 |
|
105 void paintEvent(QPaintEvent *e) |
|
106 { |
|
107 if (combo) { |
|
108 QStyleOptionComboBox opt; |
|
109 opt.initFrom(combo); |
|
110 opt.editable = combo->isEditable(); |
|
111 if (combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo)) { |
|
112 //we paint the empty menu area to avoid having blank space that can happen when scrolling |
|
113 QStyleOptionMenuItem menuOpt; |
|
114 menuOpt.initFrom(this); |
|
115 menuOpt.palette = palette(); |
|
116 menuOpt.state = QStyle::State_None; |
|
117 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable; |
|
118 menuOpt.menuRect = e->rect(); |
|
119 menuOpt.maxIconWidth = 0; |
|
120 menuOpt.tabWidth = 0; |
|
121 QPainter p(viewport()); |
|
122 combo->style()->drawControl(QStyle::CE_MenuEmptyArea, &menuOpt, &p, this); |
|
123 } |
|
124 } |
|
125 QListView::paintEvent(e); |
|
126 } |
|
127 |
|
128 private: |
|
129 QComboBox *combo; |
|
130 }; |
|
131 |
|
132 |
|
133 class QStandardItemModel; |
|
134 |
|
135 class Q_AUTOTEST_EXPORT QComboBoxPrivateScroller : public QWidget |
|
136 { |
|
137 Q_OBJECT |
|
138 |
|
139 public: |
|
140 QComboBoxPrivateScroller(QAbstractSlider::SliderAction action, QWidget *parent) |
|
141 : QWidget(parent), sliderAction(action) |
|
142 { |
|
143 setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); |
|
144 setAttribute(Qt::WA_NoMousePropagation); |
|
145 } |
|
146 QSize sizeHint() const { |
|
147 return QSize(20, style()->pixelMetric(QStyle::PM_MenuScrollerHeight)); |
|
148 } |
|
149 |
|
150 protected: |
|
151 inline void stopTimer() { |
|
152 timer.stop(); |
|
153 } |
|
154 |
|
155 inline void startTimer() { |
|
156 timer.start(100, this); |
|
157 fast = false; |
|
158 } |
|
159 |
|
160 void enterEvent(QEvent *) { |
|
161 startTimer(); |
|
162 } |
|
163 |
|
164 void leaveEvent(QEvent *) { |
|
165 stopTimer(); |
|
166 } |
|
167 void timerEvent(QTimerEvent *e) { |
|
168 if (e->timerId() == timer.timerId()) { |
|
169 emit doScroll(sliderAction); |
|
170 if (fast) { |
|
171 emit doScroll(sliderAction); |
|
172 emit doScroll(sliderAction); |
|
173 } |
|
174 } |
|
175 } |
|
176 void hideEvent(QHideEvent *) { |
|
177 stopTimer(); |
|
178 } |
|
179 |
|
180 void mouseMoveEvent(QMouseEvent *e) |
|
181 { |
|
182 // Enable fast scrolling if the cursor is directly above or below the popup. |
|
183 const int mouseX = e->pos().x(); |
|
184 const int mouseY = e->pos().y(); |
|
185 const bool horizontallyInside = pos().x() < mouseX && mouseX < rect().right() + 1; |
|
186 const bool verticallyOutside = (sliderAction == QAbstractSlider::SliderSingleStepAdd) ? |
|
187 rect().bottom() + 1 < mouseY : mouseY < pos().y(); |
|
188 |
|
189 fast = horizontallyInside && verticallyOutside; |
|
190 } |
|
191 |
|
192 void paintEvent(QPaintEvent *) { |
|
193 QPainter p(this); |
|
194 QStyleOptionMenuItem menuOpt; |
|
195 menuOpt.init(this); |
|
196 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable; |
|
197 menuOpt.menuRect = rect(); |
|
198 menuOpt.maxIconWidth = 0; |
|
199 menuOpt.tabWidth = 0; |
|
200 menuOpt.menuItemType = QStyleOptionMenuItem::Scroller; |
|
201 if (sliderAction == QAbstractSlider::SliderSingleStepAdd) |
|
202 menuOpt.state |= QStyle::State_DownArrow; |
|
203 p.eraseRect(rect()); |
|
204 style()->drawControl(QStyle::CE_MenuScroller, &menuOpt, &p); |
|
205 } |
|
206 |
|
207 Q_SIGNALS: |
|
208 void doScroll(int action); |
|
209 |
|
210 private: |
|
211 QAbstractSlider::SliderAction sliderAction; |
|
212 QBasicTimer timer; |
|
213 bool fast; |
|
214 }; |
|
215 |
|
216 class Q_AUTOTEST_EXPORT QComboBoxPrivateContainer : public QFrame |
|
217 { |
|
218 Q_OBJECT |
|
219 |
|
220 public: |
|
221 QComboBoxPrivateContainer(QAbstractItemView *itemView, QComboBox *parent); |
|
222 QAbstractItemView *itemView() const; |
|
223 void setItemView(QAbstractItemView *itemView); |
|
224 int spacing() const; |
|
225 void updateTopBottomMargin(); |
|
226 |
|
227 QTimer blockMouseReleaseTimer; |
|
228 QBasicTimer adjustSizeTimer; |
|
229 QPoint initialClickPosition; |
|
230 |
|
231 public Q_SLOTS: |
|
232 void scrollItemView(int action); |
|
233 void updateScrollers(); |
|
234 void setCurrentIndex(const QModelIndex &index); |
|
235 void viewDestroyed(); |
|
236 |
|
237 protected: |
|
238 void changeEvent(QEvent *e); |
|
239 bool eventFilter(QObject *o, QEvent *e); |
|
240 void mousePressEvent(QMouseEvent *e); |
|
241 void mouseReleaseEvent(QMouseEvent *e); |
|
242 void showEvent(QShowEvent *e); |
|
243 void hideEvent(QHideEvent *e); |
|
244 void timerEvent(QTimerEvent *timerEvent); |
|
245 void leaveEvent(QEvent *e); |
|
246 void resizeEvent(QResizeEvent *e); |
|
247 QStyleOptionComboBox comboStyleOption() const; |
|
248 |
|
249 Q_SIGNALS: |
|
250 void itemSelected(const QModelIndex &); |
|
251 void resetButton(); |
|
252 |
|
253 private: |
|
254 QComboBox *combo; |
|
255 QAbstractItemView *view; |
|
256 QComboBoxPrivateScroller *top; |
|
257 QComboBoxPrivateScroller *bottom; |
|
258 #ifdef QT_SOFTKEYS_ENABLED |
|
259 QAction *selectAction; |
|
260 QAction *cancelAction; |
|
261 #endif |
|
262 }; |
|
263 |
|
264 class QComboMenuDelegate : public QAbstractItemDelegate |
|
265 { Q_OBJECT |
|
266 public: |
|
267 QComboMenuDelegate(QObject *parent, QComboBox *cmb) : QAbstractItemDelegate(parent), mCombo(cmb) {} |
|
268 |
|
269 protected: |
|
270 void paint(QPainter *painter, |
|
271 const QStyleOptionViewItem &option, |
|
272 const QModelIndex &index) const { |
|
273 QStyleOptionMenuItem opt = getStyleOption(option, index); |
|
274 painter->fillRect(option.rect, opt.palette.background()); |
|
275 mCombo->style()->drawControl(QStyle::CE_MenuItem, &opt, painter, mCombo); |
|
276 } |
|
277 QSize sizeHint(const QStyleOptionViewItem &option, |
|
278 const QModelIndex &index) const { |
|
279 QStyleOptionMenuItem opt = getStyleOption(option, index); |
|
280 return mCombo->style()->sizeFromContents( |
|
281 QStyle::CT_MenuItem, &opt, option.rect.size(), mCombo); |
|
282 } |
|
283 |
|
284 private: |
|
285 QStyleOptionMenuItem getStyleOption(const QStyleOptionViewItem &option, |
|
286 const QModelIndex &index) const; |
|
287 QComboBox *mCombo; |
|
288 }; |
|
289 |
|
290 // Note that this class is intentionally not using QStyledItemDelegate |
|
291 // Vista does not use the new theme for combo boxes and there might |
|
292 // be other side effects from using the new class |
|
293 class QComboBoxDelegate : public QItemDelegate |
|
294 { Q_OBJECT |
|
295 public: |
|
296 QComboBoxDelegate(QObject *parent, QComboBox *cmb) : QItemDelegate(parent), mCombo(cmb) {} |
|
297 |
|
298 static bool isSeparator(const QModelIndex &index) { |
|
299 return index.data(Qt::AccessibleDescriptionRole).toString() == QLatin1String("separator"); |
|
300 } |
|
301 static void setSeparator(QAbstractItemModel *model, const QModelIndex &index) { |
|
302 model->setData(index, QString::fromLatin1("separator"), Qt::AccessibleDescriptionRole); |
|
303 if (QStandardItemModel *m = qobject_cast<QStandardItemModel*>(model)) |
|
304 if (QStandardItem *item = m->itemFromIndex(index)) |
|
305 item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); |
|
306 } |
|
307 |
|
308 protected: |
|
309 void paint(QPainter *painter, |
|
310 const QStyleOptionViewItem &option, |
|
311 const QModelIndex &index) const { |
|
312 if (isSeparator(index)) { |
|
313 QRect rect = option.rect; |
|
314 if (const QStyleOptionViewItemV3 *v3 = qstyleoption_cast<const QStyleOptionViewItemV3*>(&option)) |
|
315 if (const QAbstractItemView *view = qobject_cast<const QAbstractItemView*>(v3->widget)) |
|
316 rect.setWidth(view->viewport()->width()); |
|
317 QStyleOption opt; |
|
318 opt.rect = rect; |
|
319 mCombo->style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &opt, painter, mCombo); |
|
320 } else { |
|
321 QItemDelegate::paint(painter, option, index); |
|
322 } |
|
323 } |
|
324 |
|
325 QSize sizeHint(const QStyleOptionViewItem &option, |
|
326 const QModelIndex &index) const { |
|
327 if (isSeparator(index)) { |
|
328 int pm = mCombo->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, mCombo); |
|
329 return QSize(pm, pm); |
|
330 } |
|
331 return QItemDelegate::sizeHint(option, index); |
|
332 } |
|
333 private: |
|
334 QComboBox *mCombo; |
|
335 }; |
|
336 |
|
337 class QComboBoxPrivate : public QWidgetPrivate |
|
338 { |
|
339 Q_DECLARE_PUBLIC(QComboBox) |
|
340 public: |
|
341 QComboBoxPrivate(); |
|
342 ~QComboBoxPrivate() {} |
|
343 void init(); |
|
344 QComboBoxPrivateContainer* viewContainer(); |
|
345 void updateLineEditGeometry(); |
|
346 void _q_returnPressed(); |
|
347 void _q_complete(); |
|
348 void _q_itemSelected(const QModelIndex &item); |
|
349 bool contains(const QString &text, int role); |
|
350 void emitActivated(const QModelIndex&); |
|
351 void _q_emitHighlighted(const QModelIndex&); |
|
352 void _q_emitCurrentIndexChanged(const QModelIndex &index); |
|
353 void _q_modelDestroyed(); |
|
354 void _q_modelReset(); |
|
355 #ifdef QT_KEYPAD_NAVIGATION |
|
356 void _q_completerActivated(); |
|
357 #endif |
|
358 void _q_resetButton(); |
|
359 void _q_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); |
|
360 void _q_rowsAboutToBeInserted(const QModelIndex & parent, int start, int end); |
|
361 void _q_rowsInserted(const QModelIndex & parent, int start, int end); |
|
362 void _q_rowsAboutToBeRemoved(const QModelIndex & parent, int start, int end); |
|
363 void _q_rowsRemoved(const QModelIndex & parent, int start, int end); |
|
364 void updateArrow(QStyle::StateFlag state); |
|
365 bool updateHoverControl(const QPoint &pos); |
|
366 QRect popupGeometry(int screen = -1) const; |
|
367 QStyle::SubControl newHoverControl(const QPoint &pos); |
|
368 int computeWidthHint() const; |
|
369 QSize recomputeSizeHint(QSize &sh) const; |
|
370 void adjustComboBoxSize(); |
|
371 QString itemText(const QModelIndex &index) const; |
|
372 QIcon itemIcon(const QModelIndex &index) const; |
|
373 int itemRole() const; |
|
374 void updateLayoutDirection(); |
|
375 void setCurrentIndex(const QModelIndex &index); |
|
376 void updateDelegate(bool force = false); |
|
377 void keyboardSearchString(const QString &text); |
|
378 void modelChanged(); |
|
379 void updateViewContainerPaletteAndOpacity(); |
|
380 |
|
381 QAbstractItemModel *model; |
|
382 QLineEdit *lineEdit; |
|
383 QComboBoxPrivateContainer *container; |
|
384 QComboBox::InsertPolicy insertPolicy; |
|
385 QComboBox::SizeAdjustPolicy sizeAdjustPolicy; |
|
386 int minimumContentsLength; |
|
387 QSize iconSize; |
|
388 uint shownOnce : 1; |
|
389 uint autoCompletion : 1; |
|
390 uint duplicatesEnabled : 1; |
|
391 uint frame : 1; |
|
392 uint padding : 26; |
|
393 int maxVisibleItems; |
|
394 int maxCount; |
|
395 int modelColumn; |
|
396 bool inserting; |
|
397 mutable QSize minimumSizeHint; |
|
398 mutable QSize sizeHint; |
|
399 QStyle::StateFlag arrowState; |
|
400 QStyle::SubControl hoverControl; |
|
401 QRect hoverRect; |
|
402 QPersistentModelIndex currentIndex; |
|
403 QPersistentModelIndex root; |
|
404 Qt::CaseSensitivity autoCompletionCaseSensitivity; |
|
405 int indexBeforeChange; |
|
406 #ifndef QT_NO_COMPLETER |
|
407 QPointer<QCompleter> completer; |
|
408 #endif |
|
409 static QPalette viewContainerPalette(QComboBox *cmb) |
|
410 { return cmb->d_func()->viewContainer()->palette(); } |
|
411 }; |
|
412 |
|
413 QT_END_NAMESPACE |
|
414 |
|
415 #endif // QT_NO_COMBOBOX |
|
416 |
|
417 #endif // QCOMBOBOX_P_H |