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"