1 /*
2 This file is part of the KDE libraries
3
4 SPDX-FileCopyrightText: 2000, 2001 Dawit Alemayehu <adawit@kde.org>
5 SPDX-FileCopyrightText: 2000, 2001 Carsten Pfeiffer <pfeiffer@kde.org>
6 SPDX-FileCopyrightText: 2000 Stefan Schimanski <1Stein@gmx.de>
7
8 SPDX-License-Identifier: LGPL-2.1-or-later
9 */
10
11 #include "kcombobox.h"
12 #include "kcombobox_p.h"
13
14 #include <kcompletion_debug.h>
15 #include <kcompletionbox.h>
16
17 #include <QUrl>
18
init()19 void KComboBoxPrivate::init()
20 {
21 Q_Q(KComboBox);
22 }
23
_k_lineEditDeleted()24 void KComboBoxPrivate::_k_lineEditDeleted()
25 {
26 Q_Q(KComboBox);
27 // yes, we need those ugly casts due to the multiple inheritance
28 // sender() is guaranteed to be a KLineEdit (see the connect() to the
29 // destroyed() signal
30 const KCompletionBase *base = static_cast<const KCompletionBase *>(static_cast<const KLineEdit *>(q->sender()));
31
32 // is it our delegate, that is destroyed?
33 if (base == q->delegate()) {
34 q->setDelegate(nullptr);
35 }
36 }
37
KComboBox(QWidget * parent)38 KComboBox::KComboBox(QWidget *parent)
39 : KComboBox(*new KComboBoxPrivate(this), parent)
40 {
41 }
42
KComboBox(KComboBoxPrivate & dd,QWidget * parent)43 KComboBox::KComboBox(KComboBoxPrivate &dd, QWidget *parent)
44 : QComboBox(parent)
45 , d_ptr(&dd)
46 {
47 Q_D(KComboBox);
48
49 d->init();
50 }
51
KComboBox(bool rw,QWidget * parent)52 KComboBox::KComboBox(bool rw, QWidget *parent)
53 : KComboBox(*new KComboBoxPrivate(this), parent)
54 {
55 setEditable(rw);
56 }
57
~KComboBox()58 KComboBox::~KComboBox()
59 {
60 }
61
contains(const QString & text) const62 bool KComboBox::contains(const QString &text) const
63 {
64 if (text.isEmpty()) {
65 return false;
66 }
67
68 const int itemCount = count();
69 for (int i = 0; i < itemCount; ++i) {
70 if (itemText(i) == text) {
71 return true;
72 }
73 }
74 return false;
75 }
76
cursorPosition() const77 int KComboBox::cursorPosition() const
78 {
79 return (isEditable()) ? lineEdit()->cursorPosition() : -1;
80 }
81
setAutoCompletion(bool autocomplete)82 void KComboBox::setAutoCompletion(bool autocomplete)
83 {
84 Q_D(KComboBox);
85 if (d->klineEdit) {
86 if (autocomplete) {
87 d->klineEdit->setCompletionMode(KCompletion::CompletionAuto);
88 setCompletionMode(KCompletion::CompletionAuto);
89 } else {
90 d->klineEdit->setCompletionMode(KCompletion::CompletionPopup);
91 setCompletionMode(KCompletion::CompletionPopup);
92 }
93 }
94 }
95
autoCompletion() const96 bool KComboBox::autoCompletion() const
97 {
98 return completionMode() == KCompletion::CompletionAuto;
99 }
100
101 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(4, 5)
setContextMenuEnabled(bool showMenu)102 void KComboBox::setContextMenuEnabled(bool showMenu)
103 {
104 Q_D(KComboBox);
105 if (d->klineEdit) {
106 d->klineEdit->setContextMenuPolicy(showMenu ? Qt::DefaultContextMenu : Qt::NoContextMenu);
107 }
108 }
109 #endif
110
111 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(5, 0)
setUrlDropsEnabled(bool enable)112 void KComboBox::setUrlDropsEnabled(bool enable)
113 {
114 Q_D(KComboBox);
115 if (d->klineEdit) {
116 d->klineEdit->setUrlDropsEnabled(enable);
117 }
118 }
119 #endif
120
urlDropsEnabled() const121 bool KComboBox::urlDropsEnabled() const
122 {
123 Q_D(const KComboBox);
124 return d->klineEdit && d->klineEdit->urlDropsEnabled();
125 }
126
setCompletedText(const QString & text,bool marked)127 void KComboBox::setCompletedText(const QString &text, bool marked)
128 {
129 Q_D(KComboBox);
130 if (d->klineEdit) {
131 d->klineEdit->setCompletedText(text, marked);
132 }
133 }
134
setCompletedText(const QString & text)135 void KComboBox::setCompletedText(const QString &text)
136 {
137 Q_D(KComboBox);
138 if (d->klineEdit) {
139 d->klineEdit->setCompletedText(text);
140 }
141 }
142
makeCompletion(const QString & text)143 void KComboBox::makeCompletion(const QString &text)
144 {
145 Q_D(KComboBox);
146 if (d->klineEdit) {
147 d->klineEdit->makeCompletion(text);
148 }
149
150 else { // read-only combo completion
151 if (text.isNull() || !view()) {
152 return;
153 }
154
155 view()->keyboardSearch(text);
156 }
157 }
158
rotateText(KCompletionBase::KeyBindingType type)159 void KComboBox::rotateText(KCompletionBase::KeyBindingType type)
160 {
161 Q_D(KComboBox);
162 if (d->klineEdit) {
163 d->klineEdit->rotateText(type);
164 }
165 }
166
setTrapReturnKey(bool trap)167 void KComboBox::setTrapReturnKey(bool trap)
168 {
169 Q_D(KComboBox);
170 d->trapReturnKey = trap;
171
172 if (d->klineEdit) {
173 d->klineEdit->setTrapReturnKey(trap);
174 } else {
175 qCWarning(KCOMPLETION_LOG) << "KComboBox::setTrapReturnKey not supported with a non-KLineEdit.";
176 }
177 }
178
trapReturnKey() const179 bool KComboBox::trapReturnKey() const
180 {
181 Q_D(const KComboBox);
182 return d->trapReturnKey;
183 }
184
setEditUrl(const QUrl & url)185 void KComboBox::setEditUrl(const QUrl &url)
186 {
187 QComboBox::setEditText(url.toDisplayString());
188 }
189
addUrl(const QUrl & url)190 void KComboBox::addUrl(const QUrl &url)
191 {
192 QComboBox::addItem(url.toDisplayString());
193 }
194
addUrl(const QIcon & icon,const QUrl & url)195 void KComboBox::addUrl(const QIcon &icon, const QUrl &url)
196 {
197 QComboBox::addItem(icon, url.toDisplayString());
198 }
199
insertUrl(int index,const QUrl & url)200 void KComboBox::insertUrl(int index, const QUrl &url)
201 {
202 QComboBox::insertItem(index, url.toDisplayString());
203 }
204
insertUrl(int index,const QIcon & icon,const QUrl & url)205 void KComboBox::insertUrl(int index, const QIcon &icon, const QUrl &url)
206 {
207 QComboBox::insertItem(index, icon, url.toDisplayString());
208 }
209
changeUrl(int index,const QUrl & url)210 void KComboBox::changeUrl(int index, const QUrl &url)
211 {
212 QComboBox::setItemText(index, url.toDisplayString());
213 }
214
changeUrl(int index,const QIcon & icon,const QUrl & url)215 void KComboBox::changeUrl(int index, const QIcon &icon, const QUrl &url)
216 {
217 QComboBox::setItemIcon(index, icon);
218 QComboBox::setItemText(index, url.toDisplayString());
219 }
220
setCompletedItems(const QStringList & items,bool autoSuggest)221 void KComboBox::setCompletedItems(const QStringList &items, bool autoSuggest)
222 {
223 Q_D(KComboBox);
224 if (d->klineEdit) {
225 d->klineEdit->setCompletedItems(items, autoSuggest);
226 }
227 }
228
completionBox(bool create)229 KCompletionBox *KComboBox::completionBox(bool create)
230 {
231 Q_D(KComboBox);
232 if (d->klineEdit) {
233 return d->klineEdit->completionBox(create);
234 }
235 return nullptr;
236 }
237
minimumSizeHint() const238 QSize KComboBox::minimumSizeHint() const
239 {
240 Q_D(const KComboBox);
241 QSize size = QComboBox::minimumSizeHint();
242 if (isEditable() && d->klineEdit) {
243 // if it's a KLineEdit and it's editable add the clear button size
244 // to the minimum size hint, otherwise looks ugly because the
245 // clear button will cover the last 2/3 letters of the biggest entry
246 QSize bs = d->klineEdit->clearButtonUsedSize();
247 if (bs.isValid()) {
248 size.rwidth() += bs.width();
249 size.rheight() = qMax(size.height(), bs.height());
250 }
251 }
252 return size;
253 }
254
setLineEdit(QLineEdit * edit)255 void KComboBox::setLineEdit(QLineEdit *edit)
256 {
257 Q_D(KComboBox);
258 if (!isEditable() && edit && !qstrcmp(edit->metaObject()->className(), "QLineEdit")) {
259 // uic generates code that creates a read-only KComboBox and then
260 // calls combo->setEditable(true), which causes QComboBox to set up
261 // a dumb QLineEdit instead of our nice KLineEdit.
262 // As some KComboBox features rely on the KLineEdit, we reject
263 // this order here.
264 delete edit;
265 KLineEdit *kedit = new KLineEdit(this);
266
267 if (isEditable()) {
268 kedit->setClearButtonEnabled(true);
269 }
270
271 edit = kedit;
272 }
273
274 // reuse an existing completion object, if it does not belong to the previous
275 // line edit and gets destroyed with it
276 QPointer<KCompletion> completion = compObj();
277
278 QComboBox::setLineEdit(edit);
279 edit->setCompleter(nullptr); // remove Qt's builtin completer (set by setLineEdit), we have our own
280 d->klineEdit = qobject_cast<KLineEdit *>(edit);
281 setDelegate(d->klineEdit);
282
283 if (completion && d->klineEdit) {
284 d->klineEdit->setCompletionObject(completion);
285 }
286
287 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(5, 81)
288 // Connect the returnPressed signal for both Q[K]LineEdits'
289 if (edit) {
290 connect(edit, qOverload<>(&QLineEdit::returnPressed), this, qOverload<>(&KComboBox::returnPressed));
291 }
292 #endif
293
294 if (d->klineEdit) {
295 // someone calling KComboBox::setEditable(false) destroys our
296 // line edit without us noticing. And KCompletionBase::delegate would
297 // be a dangling pointer then, so prevent that. Note: only do this
298 // when it is a KLineEdit!
299 connect(edit, SIGNAL(destroyed()), SLOT(_k_lineEditDeleted()));
300
301 connect(d->klineEdit, &KLineEdit::returnKeyPressed, this, qOverload<const QString &>(&KComboBox::returnPressed));
302
303 connect(d->klineEdit, &KLineEdit::completion, this, &KComboBox::completion);
304
305 connect(d->klineEdit, &KLineEdit::substringCompletion, this, &KComboBox::substringCompletion);
306
307 connect(d->klineEdit, &KLineEdit::textRotation, this, &KComboBox::textRotation);
308
309 connect(d->klineEdit, &KLineEdit::completionModeChanged, this, &KComboBox::completionModeChanged);
310
311 connect(d->klineEdit, &KLineEdit::aboutToShowContextMenu, [this](QMenu *menu) {
312 Q_D(KComboBox);
313 d->contextMenu = menu;
314 Q_EMIT aboutToShowContextMenu(menu);
315 });
316
317 // match the declaration of the deprecated signal
318 #if QT_DEPRECATED_SINCE(5, 15)
319 QT_WARNING_PUSH
320 QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
321 QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations")
322 connect(d->klineEdit, &KLineEdit::completionBoxActivated, this, qOverload<const QString &>(&QComboBox::activated));
323 QT_WARNING_POP
324 #endif
325 connect(d->klineEdit, &KLineEdit::completionBoxActivated, this, &QComboBox::textActivated);
326
327 d->klineEdit->setTrapReturnKey(d->trapReturnKey);
328 }
329 }
330
contextMenu() const331 QMenu *KComboBox::contextMenu() const
332 {
333 return d_ptr->contextMenu;
334 }
335
setCurrentItem(const QString & item,bool insert,int index)336 void KComboBox::setCurrentItem(const QString &item, bool insert, int index)
337 {
338 int sel = -1;
339
340 const int itemCount = count();
341 for (int i = 0; i < itemCount; ++i) {
342 if (itemText(i) == item) {
343 sel = i;
344 break;
345 }
346 }
347
348 if (sel == -1 && insert) {
349 if (index >= 0) {
350 insertItem(index, item);
351 sel = index;
352 } else {
353 addItem(item);
354 sel = count() - 1;
355 }
356 }
357 setCurrentIndex(sel);
358 }
359
setEditable(bool editable)360 void KComboBox::setEditable(bool editable)
361 {
362 if (editable == isEditable()) {
363 return;
364 }
365
366 if (editable) {
367 // Create a KLineEdit instead of a QLineEdit
368 // Compared to QComboBox::setEditable, we might be missing the SH_ComboBox_Popup code though...
369 // If a style needs this, then we'll need to call QComboBox::setEditable and then setLineEdit again
370 KLineEdit *edit = new KLineEdit(this);
371 edit->setClearButtonEnabled(true);
372 setLineEdit(edit);
373 } else {
374 if (d_ptr->contextMenu) {
375 d_ptr->contextMenu->close();
376 }
377 QComboBox::setEditable(editable);
378 }
379 }
380
381 #include "moc_kcombobox.cpp"
382