File indexing completed on 2024-04-28 03:53:08
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 bool KComboBox::urlDropsEnabled() const 0104 { 0105 Q_D(const KComboBox); 0106 return d->klineEdit && d->klineEdit->urlDropsEnabled(); 0107 } 0108 0109 void KComboBox::setCompletedText(const QString &text, bool marked) 0110 { 0111 Q_D(KComboBox); 0112 if (d->klineEdit) { 0113 d->klineEdit->setCompletedText(text, marked); 0114 } 0115 } 0116 0117 void KComboBox::setCompletedText(const QString &text) 0118 { 0119 Q_D(KComboBox); 0120 if (d->klineEdit) { 0121 d->klineEdit->setCompletedText(text); 0122 } 0123 } 0124 0125 void KComboBox::makeCompletion(const QString &text) 0126 { 0127 Q_D(KComboBox); 0128 if (d->klineEdit) { 0129 d->klineEdit->makeCompletion(text); 0130 } 0131 0132 else { // read-only combo completion 0133 if (text.isNull() || !view()) { 0134 return; 0135 } 0136 0137 view()->keyboardSearch(text); 0138 } 0139 } 0140 0141 void KComboBox::rotateText(KCompletionBase::KeyBindingType type) 0142 { 0143 Q_D(KComboBox); 0144 if (d->klineEdit) { 0145 d->klineEdit->rotateText(type); 0146 } 0147 } 0148 0149 void KComboBox::setTrapReturnKey(bool trap) 0150 { 0151 Q_D(KComboBox); 0152 d->trapReturnKey = trap; 0153 0154 if (d->klineEdit) { 0155 d->klineEdit->setTrapReturnKey(trap); 0156 } else { 0157 qCWarning(KCOMPLETION_LOG) << "KComboBox::setTrapReturnKey not supported with a non-KLineEdit."; 0158 } 0159 } 0160 0161 bool KComboBox::trapReturnKey() const 0162 { 0163 Q_D(const KComboBox); 0164 return d->trapReturnKey; 0165 } 0166 0167 void KComboBox::setEditUrl(const QUrl &url) 0168 { 0169 QComboBox::setEditText(url.toDisplayString()); 0170 } 0171 0172 void KComboBox::addUrl(const QUrl &url) 0173 { 0174 QComboBox::addItem(url.toDisplayString()); 0175 } 0176 0177 void KComboBox::addUrl(const QIcon &icon, const QUrl &url) 0178 { 0179 QComboBox::addItem(icon, url.toDisplayString()); 0180 } 0181 0182 void KComboBox::insertUrl(int index, const QUrl &url) 0183 { 0184 QComboBox::insertItem(index, url.toDisplayString()); 0185 } 0186 0187 void KComboBox::insertUrl(int index, const QIcon &icon, const QUrl &url) 0188 { 0189 QComboBox::insertItem(index, icon, url.toDisplayString()); 0190 } 0191 0192 void KComboBox::changeUrl(int index, const QUrl &url) 0193 { 0194 QComboBox::setItemText(index, url.toDisplayString()); 0195 } 0196 0197 void KComboBox::changeUrl(int index, const QIcon &icon, const QUrl &url) 0198 { 0199 QComboBox::setItemIcon(index, icon); 0200 QComboBox::setItemText(index, url.toDisplayString()); 0201 } 0202 0203 void KComboBox::setCompletedItems(const QStringList &items, bool autoSuggest) 0204 { 0205 Q_D(KComboBox); 0206 if (d->klineEdit) { 0207 d->klineEdit->setCompletedItems(items, autoSuggest); 0208 } 0209 } 0210 0211 KCompletionBox *KComboBox::completionBox(bool create) 0212 { 0213 Q_D(KComboBox); 0214 if (d->klineEdit) { 0215 return d->klineEdit->completionBox(create); 0216 } 0217 return nullptr; 0218 } 0219 0220 QSize KComboBox::minimumSizeHint() const 0221 { 0222 Q_D(const KComboBox); 0223 QSize size = QComboBox::minimumSizeHint(); 0224 if (isEditable() && d->klineEdit) { 0225 // if it's a KLineEdit and it's editable add the clear button size 0226 // to the minimum size hint, otherwise looks ugly because the 0227 // clear button will cover the last 2/3 letters of the biggest entry 0228 QSize bs = d->klineEdit->clearButtonUsedSize(); 0229 if (bs.isValid()) { 0230 size.rwidth() += bs.width(); 0231 size.rheight() = qMax(size.height(), bs.height()); 0232 } 0233 } 0234 return size; 0235 } 0236 0237 void KComboBox::setLineEdit(QLineEdit *edit) 0238 { 0239 Q_D(KComboBox); 0240 if (!isEditable() && edit && !qstrcmp(edit->metaObject()->className(), "QLineEdit")) { 0241 // uic generates code that creates a read-only KComboBox and then 0242 // calls combo->setEditable(true), which causes QComboBox to set up 0243 // a dumb QLineEdit instead of our nice KLineEdit. 0244 // As some KComboBox features rely on the KLineEdit, we reject 0245 // this order here. 0246 delete edit; 0247 KLineEdit *kedit = new KLineEdit(this); 0248 0249 if (isEditable()) { 0250 kedit->setClearButtonEnabled(true); 0251 } 0252 0253 edit = kedit; 0254 } 0255 0256 // reuse an existing completion object, if it does not belong to the previous 0257 // line edit and gets destroyed with it 0258 QPointer<KCompletion> completion = compObj(); 0259 0260 QComboBox::setLineEdit(edit); 0261 edit->setCompleter(nullptr); // remove Qt's builtin completer (set by setLineEdit), we have our own 0262 d->klineEdit = qobject_cast<KLineEdit *>(edit); 0263 setDelegate(d->klineEdit); 0264 0265 if (completion && d->klineEdit) { 0266 d->klineEdit->setCompletionObject(completion); 0267 } 0268 0269 if (d->klineEdit) { 0270 // someone calling KComboBox::setEditable(false) destroys our 0271 // line edit without us noticing. And KCompletionBase::delegate would 0272 // be a dangling pointer then, so prevent that. Note: only do this 0273 // when it is a KLineEdit! 0274 d->m_klineEditConnection = connect(edit, &QObject::destroyed, this, [d, edit]() { 0275 d->slotLineEditDeleted(edit); 0276 }); 0277 0278 connect(d->klineEdit, &KLineEdit::returnKeyPressed, this, qOverload<const QString &>(&KComboBox::returnPressed)); 0279 0280 connect(d->klineEdit, &KLineEdit::completion, this, &KComboBox::completion); 0281 0282 connect(d->klineEdit, &KLineEdit::substringCompletion, this, &KComboBox::substringCompletion); 0283 0284 connect(d->klineEdit, &KLineEdit::textRotation, this, &KComboBox::textRotation); 0285 0286 connect(d->klineEdit, &KLineEdit::completionModeChanged, this, &KComboBox::completionModeChanged); 0287 0288 connect(d->klineEdit, &KLineEdit::aboutToShowContextMenu, [this](QMenu *menu) { 0289 Q_D(KComboBox); 0290 d->contextMenu = menu; 0291 Q_EMIT aboutToShowContextMenu(menu); 0292 }); 0293 0294 connect(d->klineEdit, &KLineEdit::completionBoxActivated, this, &QComboBox::textActivated); 0295 0296 d->klineEdit->setTrapReturnKey(d->trapReturnKey); 0297 } 0298 } 0299 0300 QMenu *KComboBox::contextMenu() const 0301 { 0302 return d_ptr->contextMenu; 0303 } 0304 0305 void KComboBox::setCurrentItem(const QString &item, bool insert, int index) 0306 { 0307 int sel = -1; 0308 0309 const int itemCount = count(); 0310 for (int i = 0; i < itemCount; ++i) { 0311 if (itemText(i) == item) { 0312 sel = i; 0313 break; 0314 } 0315 } 0316 0317 if (sel == -1 && insert) { 0318 if (index >= 0) { 0319 insertItem(index, item); 0320 sel = index; 0321 } else { 0322 addItem(item); 0323 sel = count() - 1; 0324 } 0325 } 0326 setCurrentIndex(sel); 0327 } 0328 0329 void KComboBox::setEditable(bool editable) 0330 { 0331 if (editable == isEditable()) { 0332 return; 0333 } 0334 0335 if (editable) { 0336 // Create a KLineEdit instead of a QLineEdit 0337 // Compared to QComboBox::setEditable, we might be missing the SH_ComboBox_Popup code though... 0338 // If a style needs this, then we'll need to call QComboBox::setEditable and then setLineEdit again 0339 KLineEdit *edit = new KLineEdit(this); 0340 edit->setClearButtonEnabled(true); 0341 setLineEdit(edit); 0342 } else { 0343 if (d_ptr->contextMenu) { 0344 d_ptr->contextMenu->close(); 0345 } 0346 QComboBox::setEditable(editable); 0347 } 0348 } 0349 0350 #include "moc_kcombobox.cpp"