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