File indexing completed on 2024-05-12 15:33:59
0001 /* 0002 This file is part of the KDE libraries 0003 0004 SPDX-FileCopyrightText: 2000, 2001 Dawit Alemayehu <adawit@kde.org> 0005 SPDX-FileCopyrightText: 2000, 2001 Carsten Pfeiffer <pfeiffer@kde.org> 0006 SPDX-FileCopyrightText: 2000 Stefan Schimanski <1Stein@gmx.de> 0007 0008 SPDX-License-Identifier: LGPL-2.1-or-later 0009 */ 0010 0011 #include "kcombobox.h" 0012 #include "kcombobox_p.h" 0013 0014 #include <kcompletion_debug.h> 0015 #include <kcompletionbox.h> 0016 0017 #include <QUrl> 0018 0019 void KComboBoxPrivate::init() 0020 { 0021 Q_Q(KComboBox); 0022 } 0023 0024 void KComboBoxPrivate::slotLineEditDeleted(QLineEdit *sender) 0025 { 0026 Q_Q(KComboBox); 0027 // yes, we need those ugly casts due to the multiple inheritance 0028 // "sender" is guaranteed to be a KLineEdit (see the connect() to the 0029 // destroyed() signal 0030 const KCompletionBase *base = static_cast<const KCompletionBase *>(static_cast<const KLineEdit *>(sender)); 0031 0032 // is it our delegate, that is destroyed? 0033 if (base == q->delegate()) { 0034 q->setDelegate(nullptr); 0035 } 0036 } 0037 0038 KComboBox::KComboBox(QWidget *parent) 0039 : KComboBox(*new KComboBoxPrivate(this), parent) 0040 { 0041 } 0042 0043 KComboBox::KComboBox(KComboBoxPrivate &dd, QWidget *parent) 0044 : QComboBox(parent) 0045 , d_ptr(&dd) 0046 { 0047 Q_D(KComboBox); 0048 0049 d->init(); 0050 } 0051 0052 KComboBox::KComboBox(bool rw, QWidget *parent) 0053 : KComboBox(*new KComboBoxPrivate(this), parent) 0054 { 0055 setEditable(rw); 0056 } 0057 0058 KComboBox::~KComboBox() 0059 { 0060 Q_D(KComboBox); 0061 disconnect(d->m_klineEditConnection); 0062 } 0063 0064 bool KComboBox::contains(const QString &text) const 0065 { 0066 if (text.isEmpty()) { 0067 return false; 0068 } 0069 0070 const int itemCount = count(); 0071 for (int i = 0; i < itemCount; ++i) { 0072 if (itemText(i) == text) { 0073 return true; 0074 } 0075 } 0076 return false; 0077 } 0078 0079 int KComboBox::cursorPosition() const 0080 { 0081 return (isEditable()) ? lineEdit()->cursorPosition() : -1; 0082 } 0083 0084 void KComboBox::setAutoCompletion(bool autocomplete) 0085 { 0086 Q_D(KComboBox); 0087 if (d->klineEdit) { 0088 if (autocomplete) { 0089 d->klineEdit->setCompletionMode(KCompletion::CompletionAuto); 0090 setCompletionMode(KCompletion::CompletionAuto); 0091 } else { 0092 d->klineEdit->setCompletionMode(KCompletion::CompletionPopup); 0093 setCompletionMode(KCompletion::CompletionPopup); 0094 } 0095 } 0096 } 0097 0098 bool KComboBox::autoCompletion() const 0099 { 0100 return completionMode() == KCompletion::CompletionAuto; 0101 } 0102 0103 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(4, 5) 0104 void KComboBox::setContextMenuEnabled(bool showMenu) 0105 { 0106 Q_D(KComboBox); 0107 if (d->klineEdit) { 0108 d->klineEdit->setContextMenuPolicy(showMenu ? Qt::DefaultContextMenu : Qt::NoContextMenu); 0109 } 0110 } 0111 #endif 0112 0113 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(5, 0) 0114 void KComboBox::setUrlDropsEnabled(bool enable) 0115 { 0116 Q_D(KComboBox); 0117 if (d->klineEdit) { 0118 d->klineEdit->setUrlDropsEnabled(enable); 0119 } 0120 } 0121 #endif 0122 0123 bool KComboBox::urlDropsEnabled() const 0124 { 0125 Q_D(const KComboBox); 0126 return d->klineEdit && d->klineEdit->urlDropsEnabled(); 0127 } 0128 0129 void KComboBox::setCompletedText(const QString &text, bool marked) 0130 { 0131 Q_D(KComboBox); 0132 if (d->klineEdit) { 0133 d->klineEdit->setCompletedText(text, marked); 0134 } 0135 } 0136 0137 void KComboBox::setCompletedText(const QString &text) 0138 { 0139 Q_D(KComboBox); 0140 if (d->klineEdit) { 0141 d->klineEdit->setCompletedText(text); 0142 } 0143 } 0144 0145 void KComboBox::makeCompletion(const QString &text) 0146 { 0147 Q_D(KComboBox); 0148 if (d->klineEdit) { 0149 d->klineEdit->makeCompletion(text); 0150 } 0151 0152 else { // read-only combo completion 0153 if (text.isNull() || !view()) { 0154 return; 0155 } 0156 0157 view()->keyboardSearch(text); 0158 } 0159 } 0160 0161 void KComboBox::rotateText(KCompletionBase::KeyBindingType type) 0162 { 0163 Q_D(KComboBox); 0164 if (d->klineEdit) { 0165 d->klineEdit->rotateText(type); 0166 } 0167 } 0168 0169 void KComboBox::setTrapReturnKey(bool trap) 0170 { 0171 Q_D(KComboBox); 0172 d->trapReturnKey = trap; 0173 0174 if (d->klineEdit) { 0175 d->klineEdit->setTrapReturnKey(trap); 0176 } else { 0177 qCWarning(KCOMPLETION_LOG) << "KComboBox::setTrapReturnKey not supported with a non-KLineEdit."; 0178 } 0179 } 0180 0181 bool KComboBox::trapReturnKey() const 0182 { 0183 Q_D(const KComboBox); 0184 return d->trapReturnKey; 0185 } 0186 0187 void KComboBox::setEditUrl(const QUrl &url) 0188 { 0189 QComboBox::setEditText(url.toDisplayString()); 0190 } 0191 0192 void KComboBox::addUrl(const QUrl &url) 0193 { 0194 QComboBox::addItem(url.toDisplayString()); 0195 } 0196 0197 void KComboBox::addUrl(const QIcon &icon, const QUrl &url) 0198 { 0199 QComboBox::addItem(icon, url.toDisplayString()); 0200 } 0201 0202 void KComboBox::insertUrl(int index, const QUrl &url) 0203 { 0204 QComboBox::insertItem(index, url.toDisplayString()); 0205 } 0206 0207 void KComboBox::insertUrl(int index, const QIcon &icon, const QUrl &url) 0208 { 0209 QComboBox::insertItem(index, icon, url.toDisplayString()); 0210 } 0211 0212 void KComboBox::changeUrl(int index, const QUrl &url) 0213 { 0214 QComboBox::setItemText(index, url.toDisplayString()); 0215 } 0216 0217 void KComboBox::changeUrl(int index, const QIcon &icon, const QUrl &url) 0218 { 0219 QComboBox::setItemIcon(index, icon); 0220 QComboBox::setItemText(index, url.toDisplayString()); 0221 } 0222 0223 void KComboBox::setCompletedItems(const QStringList &items, bool autoSuggest) 0224 { 0225 Q_D(KComboBox); 0226 if (d->klineEdit) { 0227 d->klineEdit->setCompletedItems(items, autoSuggest); 0228 } 0229 } 0230 0231 KCompletionBox *KComboBox::completionBox(bool create) 0232 { 0233 Q_D(KComboBox); 0234 if (d->klineEdit) { 0235 return d->klineEdit->completionBox(create); 0236 } 0237 return nullptr; 0238 } 0239 0240 QSize KComboBox::minimumSizeHint() const 0241 { 0242 Q_D(const KComboBox); 0243 QSize size = QComboBox::minimumSizeHint(); 0244 if (isEditable() && d->klineEdit) { 0245 // if it's a KLineEdit and it's editable add the clear button size 0246 // to the minimum size hint, otherwise looks ugly because the 0247 // clear button will cover the last 2/3 letters of the biggest entry 0248 QSize bs = d->klineEdit->clearButtonUsedSize(); 0249 if (bs.isValid()) { 0250 size.rwidth() += bs.width(); 0251 size.rheight() = qMax(size.height(), bs.height()); 0252 } 0253 } 0254 return size; 0255 } 0256 0257 void KComboBox::setLineEdit(QLineEdit *edit) 0258 { 0259 Q_D(KComboBox); 0260 if (!isEditable() && edit && !qstrcmp(edit->metaObject()->className(), "QLineEdit")) { 0261 // uic generates code that creates a read-only KComboBox and then 0262 // calls combo->setEditable(true), which causes QComboBox to set up 0263 // a dumb QLineEdit instead of our nice KLineEdit. 0264 // As some KComboBox features rely on the KLineEdit, we reject 0265 // this order here. 0266 delete edit; 0267 KLineEdit *kedit = new KLineEdit(this); 0268 0269 if (isEditable()) { 0270 kedit->setClearButtonEnabled(true); 0271 } 0272 0273 edit = kedit; 0274 } 0275 0276 // reuse an existing completion object, if it does not belong to the previous 0277 // line edit and gets destroyed with it 0278 QPointer<KCompletion> completion = compObj(); 0279 0280 QComboBox::setLineEdit(edit); 0281 edit->setCompleter(nullptr); // remove Qt's builtin completer (set by setLineEdit), we have our own 0282 d->klineEdit = qobject_cast<KLineEdit *>(edit); 0283 setDelegate(d->klineEdit); 0284 0285 if (completion && d->klineEdit) { 0286 d->klineEdit->setCompletionObject(completion); 0287 } 0288 0289 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(5, 81) 0290 // Connect the returnPressed signal for both Q[K]LineEdits' 0291 if (edit) { 0292 connect(edit, qOverload<>(&QLineEdit::returnPressed), this, qOverload<>(&KComboBox::returnPressed)); 0293 } 0294 #endif 0295 0296 if (d->klineEdit) { 0297 // someone calling KComboBox::setEditable(false) destroys our 0298 // line edit without us noticing. And KCompletionBase::delegate would 0299 // be a dangling pointer then, so prevent that. Note: only do this 0300 // when it is a KLineEdit! 0301 d->m_klineEditConnection = connect(edit, &QObject::destroyed, this, [d, edit]() { 0302 d->slotLineEditDeleted(edit); 0303 }); 0304 0305 connect(d->klineEdit, &KLineEdit::returnKeyPressed, this, qOverload<const QString &>(&KComboBox::returnPressed)); 0306 0307 connect(d->klineEdit, &KLineEdit::completion, this, &KComboBox::completion); 0308 0309 connect(d->klineEdit, &KLineEdit::substringCompletion, this, &KComboBox::substringCompletion); 0310 0311 connect(d->klineEdit, &KLineEdit::textRotation, this, &KComboBox::textRotation); 0312 0313 connect(d->klineEdit, &KLineEdit::completionModeChanged, this, &KComboBox::completionModeChanged); 0314 0315 connect(d->klineEdit, &KLineEdit::aboutToShowContextMenu, [this](QMenu *menu) { 0316 Q_D(KComboBox); 0317 d->contextMenu = menu; 0318 Q_EMIT aboutToShowContextMenu(menu); 0319 }); 0320 0321 // match the declaration of the deprecated signal 0322 #if QT_DEPRECATED_SINCE(5, 15) 0323 QT_WARNING_PUSH 0324 QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations") 0325 QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations") 0326 connect(d->klineEdit, &KLineEdit::completionBoxActivated, this, qOverload<const QString &>(&QComboBox::activated)); 0327 QT_WARNING_POP 0328 #endif 0329 connect(d->klineEdit, &KLineEdit::completionBoxActivated, this, &QComboBox::textActivated); 0330 0331 d->klineEdit->setTrapReturnKey(d->trapReturnKey); 0332 } 0333 } 0334 0335 QMenu *KComboBox::contextMenu() const 0336 { 0337 return d_ptr->contextMenu; 0338 } 0339 0340 void KComboBox::setCurrentItem(const QString &item, bool insert, int index) 0341 { 0342 int sel = -1; 0343 0344 const int itemCount = count(); 0345 for (int i = 0; i < itemCount; ++i) { 0346 if (itemText(i) == item) { 0347 sel = i; 0348 break; 0349 } 0350 } 0351 0352 if (sel == -1 && insert) { 0353 if (index >= 0) { 0354 insertItem(index, item); 0355 sel = index; 0356 } else { 0357 addItem(item); 0358 sel = count() - 1; 0359 } 0360 } 0361 setCurrentIndex(sel); 0362 } 0363 0364 void KComboBox::setEditable(bool editable) 0365 { 0366 if (editable == isEditable()) { 0367 return; 0368 } 0369 0370 if (editable) { 0371 // Create a KLineEdit instead of a QLineEdit 0372 // Compared to QComboBox::setEditable, we might be missing the SH_ComboBox_Popup code though... 0373 // If a style needs this, then we'll need to call QComboBox::setEditable and then setLineEdit again 0374 KLineEdit *edit = new KLineEdit(this); 0375 edit->setClearButtonEnabled(true); 0376 setLineEdit(edit); 0377 } else { 0378 if (d_ptr->contextMenu) { 0379 d_ptr->contextMenu->close(); 0380 } 0381 QComboBox::setEditable(editable); 0382 } 0383 } 0384 0385 #include "moc_kcombobox.cpp"