File indexing completed on 2024-05-12 11:47:07

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"