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 
0007     SPDX-License-Identifier: LGPL-2.1-or-later
0008 */
0009 
0010 #ifndef KCOMBOBOX_H
0011 #define KCOMBOBOX_H
0012 
0013 #include <kcompletion.h>
0014 
0015 #include <kcompletion_export.h>
0016 #include <kcompletionbase.h>
0017 
0018 #include <QComboBox>
0019 #include <memory>
0020 
0021 class KCompletionBox;
0022 class KComboBoxPrivate;
0023 
0024 class QLineEdit;
0025 class QMenu;
0026 
0027 /**
0028  * @class KComboBox kcombobox.h KComboBox
0029  *
0030  * @short A combo box with completion support.
0031  *
0032  * This widget inherits from QComboBox and implements the following
0033  * additional features:
0034  *   @li a completion object that provides both automatic
0035  * and manual text completion as well as text rotation
0036  *   @li configurable key bindings to activate these features
0037  *   @li a popup menu item that can be used to allow the user to change
0038  * the text completion mode on the fly.
0039  *
0040  * To support these additional features, KComboBox emits a few additional signals
0041  * such as completion(const QString&) and textRotation(KeyBindingType).
0042  *
0043  * The completion signal can be connected to a slot that will assist the user in
0044  * filling out the remaining text while the rotation signal can be used to traverse
0045  * through all possible matches whenever text completion results in multiple matches.
0046  * Additionally, the returnPressed(const QString &) signal is emitted when the user
0047  * presses the Return or Enter key.
0048  *
0049  * KCombobox by default creates a completion object when you invoke the
0050  * completionObject(bool) member function for the first time or
0051  * explicitly use setCompletionObject(KCompletion*, bool) to assign your
0052  * own completion object. Additionally, to make this widget more functional,
0053  * KComboBox will by default handle text rotation and completion events
0054  * internally whenever a completion object is created through either one of the
0055  * methods mentioned above. If you do not need this functionality, simply use
0056  * KCompletionBase::setHandleSignals(bool) or alternatively set the boolean
0057  * parameter in the @c setCompletionObject() call to @c false.
0058  *
0059  * Beware: The completion object can be deleted on you, especially if a call
0060  * such as setEditable(false) is made. Store the pointer at your own risk,
0061  * and consider using QPointer<KCompletion>.
0062  *
0063  * The default key bindings for completion and rotation are determined from the
0064  * global settings in KStandardShortcut. These values, however, can be overridden
0065  * locally by invoking KCompletionBase::setKeyBinding(). The values can
0066  * easily be reverted back to the default settings by calling
0067  * useGlobalSettings(). An alternate method would be to default individual
0068  * key bindings by using setKeyBinding() with the default second argument.
0069  *
0070  * A non-editable combo box only has one completion mode, @c CompletionAuto.
0071  * Unlike an editable combo box, the CompletionAuto mode works by matching
0072  * any typed key with the first letter of entries in the combo box. Please note
0073  * that if you call setEditable(false) to change an editable combo box to a
0074  * non-editable one, the text completion object associated with the combo box will
0075  * no longer exist unless you created the completion object yourself and assigned
0076  * it to this widget or you called setAutoDeleteCompletionObject(false). In other
0077  * words do not do the following:
0078  *
0079  * \code
0080  * KComboBox* combo = new KComboBox(true, this);
0081  * KCompletion* comp = combo->completionObject();
0082  * combo->setEditable(false);
0083  * comp->clear(); // CRASH: completion object does not exist anymore.
0084  * \endcode
0085  *
0086  *
0087  * A read-only KComboBox will have the same background color as a
0088  * disabled KComboBox, but its foreground color will be the one used for
0089  * the editable mode. This differs from QComboBox's implementation
0090  * and is done to give visual distinction between the three different modes:
0091  * disabled, read-only, and editable.
0092  *
0093  * \b Usage
0094  *
0095  * To enable the basic completion feature:
0096  *
0097  * \code
0098  * KComboBox *combo = new KComboBox(true, this);
0099  * KCompletion *comp = combo->completionObject();
0100  * // Connect to the Return pressed signal - optional
0101  * connect(combo, &KComboBox::returnPressed, comp, [this](const QString &text) { addItem(text); });
0102  *
0103  * // Provide the to be completed strings. Note that those are separate from the combo's
0104  * // contents.
0105  * comp->insertItems(someQStringList);
0106  * \endcode
0107  *
0108  * To use your own completion object:
0109  *
0110  * \code
0111  * KComboBox *combo = new KComboBox(this);
0112  * KUrlCompletion *comp = new KUrlCompletion();
0113  * // You can either delete the allocated completion object manually when you
0114  * // don't need it anymore, or call setAutoDeleteCompletionObject(true) and it
0115  * // will be deleted automatically
0116  * comp->setAutoDeleteCompletionObject(true);
0117  * combo->setCompletionObject(comp);
0118  * // Connect to the return pressed signal - optional
0119  * connect(combo, &KComboBox::returnPressed, comp, [this](const QString &text) { addItem(text); });
0120  * \endcode
0121  *
0122  * Miscellaneous function calls:
0123  *
0124  * \code
0125  * // Tell the widget not to handle completion and rotation
0126  * combo->setHandleSignals(false);
0127  * // Set your own completion key for manual completions.
0128  * combo->setKeyBinding(KCompletionBase::TextCompletion, Qt::End);
0129  * \endcode
0130  *
0131  * \image html kcombobox.png "KComboBox widgets, one non-editable, one editable with KUrlCompletion"
0132  *
0133  * @author Dawit Alemayehu <adawit@kde.org>
0134  */
0135 class KCOMPLETION_EXPORT KComboBox : public QComboBox, public KCompletionBase // krazy:exclude=qclasses
0136 {
0137     Q_OBJECT
0138     Q_PROPERTY(bool autoCompletion READ autoCompletion WRITE setAutoCompletion)
0139 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(5, 0)
0140     Q_PROPERTY(bool urlDropsEnabled READ urlDropsEnabled WRITE setUrlDropsEnabled)
0141 #endif
0142     Q_PROPERTY(bool trapReturnKey READ trapReturnKey WRITE setTrapReturnKey)
0143 
0144 public:
0145     /**
0146      * Constructs a read-only (or rather select-only) combo box.
0147      *
0148      * @param parent The parent object of this widget
0149      */
0150     explicit KComboBox(QWidget *parent = nullptr);
0151 
0152     /**
0153      * Constructs an editable or read-only combo box.
0154      *
0155      * @param rw When @c true, widget will be editable.
0156      * @param parent The parent object of this widget.
0157      */
0158     explicit KComboBox(bool rw, QWidget *parent = nullptr);
0159 
0160     /**
0161      * Destructor.
0162      */
0163     ~KComboBox() override;
0164 
0165 #if KCOMPLETION_ENABLE_DEPRECATED_SINCE(4, 5)
0166     /**
0167      * Deprecated to reflect Qt api changes
0168      * @deprecated since 4.5
0169      */
0170     KCOMPLETION_DEPRECATED_VERSION(4, 5, "Use KComboBox::insertUrl(int, const QUrl&)")
0171     void insertURL(const QUrl &url, int index = -1)
0172     {
0173         insertUrl(index < 0 ? count() : index, url);
0174     }
0175     KCOMPLETION_DEPRECATED_VERSION(4, 5, "Use KComboBox::insertUrl(int, const QIcon&, const QUrl&)")
0176     void insertURL(const QPixmap &pixmap, const QUrl &url, int index = -1)
0177     {
0178         insertUrl(index < 0 ? count() : index, QIcon(pixmap), url);
0179     }
0180     KCOMPLETION_DEPRECATED_VERSION(4, 5, "Use KComboBox::changeUrl(int, const QUrl&)")
0181     void changeURL(const QUrl &url, int index)
0182     {
0183         changeUrl(index, url);
0184     }
0185     KCOMPLETION_DEPRECATED_VERSION(4, 5, "Use KComboBox::changeUrl(int, const QIcon&, const QUrl&)")
0186     void changeURL(const QPixmap &pixmap, const QUrl &url, int index)
0187     {
0188         changeUrl(index, QIcon(pixmap), url);
0189     }
0190 #endif
0191 
0192     /**
0193      * Sets @p url into the edit field of the combo box.
0194      *
0195      * It uses QUrl::toDisplayString() so that the url is properly decoded for
0196      * displaying.
0197      */
0198     void setEditUrl(const QUrl &url);
0199 
0200     /**
0201      * Appends @p url to the combo box.
0202      *
0203      * QUrl::toDisplayString() is used so that the url is properly decoded
0204      * for displaying.
0205      */
0206     void addUrl(const QUrl &url);
0207 
0208     /**
0209      * Appends @p url with the @p icon to the combo box.
0210      *
0211      * QUrl::toDisplayString() is used so that the url is properly decoded
0212      * for displaying.
0213      */
0214     void addUrl(const QIcon &icon, const QUrl &url);
0215 
0216     /**
0217      * Inserts @p url at position @p index into the combo box.
0218      *
0219      * QUrl::toDisplayString() is used so that the url is properly decoded
0220      * for displaying.
0221      */
0222     void insertUrl(int index, const QUrl &url);
0223 
0224     /**
0225      * Inserts @p url with the @p icon at position @p index into
0226      * the combo box.
0227      *
0228      * QUrl::toDisplayString() is used so that the url is
0229      * properly decoded for displaying.
0230      */
0231     void insertUrl(int index, const QIcon &icon, const QUrl &url);
0232 
0233     /**
0234      * Replaces the item at position @p index with @p url.
0235      *
0236      * QUrl::toDisplayString() is used so that the url is properly decoded
0237      * for displaying.
0238      */
0239     void changeUrl(int index, const QUrl &url);
0240 
0241     /**
0242      * Replaces the item at position @p index with @p url and @p icon.
0243      *
0244      * QUrl::toDisplayString() is used so that the url is properly decoded
0245      * for displaying.
0246      */
0247     void changeUrl(int index, const QIcon &icon, const QUrl &url);
0248 
0249     /**
0250      * Returns the current cursor position.
0251      *
0252      * This method always returns a -1 if the combo box is @em not
0253      * editable (read-only).
0254      *
0255      * @return Current cursor position.
0256      */
0257     int cursorPosition() const;
0258 
0259     /**
0260      * Reimplemented from QComboBox.
0261      *
0262      * If @c true, the completion mode will be set to automatic.
0263      * Otherwise, it is defaulted to the global setting. This
0264      * method has been replaced by the more comprehensive
0265      * setCompletionMode().
0266      *
0267      * @param autocomplete Flag to enable/disable automatic completion mode.
0268      */
0269     virtual void setAutoCompletion(bool autocomplete);
0270 
0271     /**
0272      * Reimplemented from QComboBox.
0273      *
0274      * Returns @c true if the current completion mode is set
0275      * to automatic. See its more comprehensive replacement
0276      * completionMode().
0277      *
0278      * @return @c true when completion mode is automatic.
0279      */
0280     bool autoCompletion() const;
0281 
0282 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(4, 5)
0283     /**
0284      * Enables or disables the popup (context) menu.
0285      *
0286      * This method only works if this widget is editable, and
0287      * allows you to enable/disable the context menu. It does nothing if invoked
0288      * for a non-editable combo box.
0289      *
0290      * By default, the context menu is created if this widget is editable.
0291      * Call this function with the argument set to false to disable the popup
0292      * menu.
0293      *
0294      * @param showMenu If @c true, show the context menu.
0295      * @deprecated since 4.5, use setContextMenuPolicy instead
0296      */
0297     KCOMPLETION_DEPRECATED_VERSION(4, 5, "Use QWidget::setContextMenuPolicy(...)")
0298     virtual void setContextMenuEnabled(bool showMenu);
0299 #endif
0300 
0301 #if KCOMPLETION_ENABLE_DEPRECATED_SINCE(5, 0)
0302     /**
0303      * Enables/Disables handling of URL drops.
0304      *
0305      * If enabled and the user drops an URL, the decoded URL will
0306      * be inserted. Otherwise the default behavior of QComboBox is used,
0307      * which inserts the encoded URL.
0308      *
0309      * @param enable If @c true, insert decoded URLs
0310      * @deprecated since 5.0. Use lineEdit()->installEventFilter with a LineEditUrlDropEventFilter
0311      */
0312     KCOMPLETION_DEPRECATED_VERSION(5, 0, "Use KComboBox::lineEdit()->installEventFilter(...) with a LineEditUrlDropEventFilter")
0313     void setUrlDropsEnabled(bool enable);
0314 #endif
0315 
0316     /**
0317      * Returns @c true when decoded URL drops are enabled
0318      */
0319     bool urlDropsEnabled() const;
0320 
0321     /**
0322      * Convenience method which iterates over all items and checks if
0323      * any of them is equal to @p text.
0324      *
0325      * If @p text is an empty string, @c false
0326      * is returned.
0327      *
0328      * @return @c true if an item with the string @p text is in the combo box.
0329      */
0330     bool contains(const QString &text) const;
0331 
0332     /**
0333      * By default, KComboBox recognizes Key_Return and Key_Enter and emits the
0334      * returnPressed(const QString &) signal, but it also lets the event pass,
0335      * for example causing a dialog's default button to be called.
0336      *
0337      * Call this method with @p trap set to true to make KComboBox stop these
0338      * events. The signals will still be emitted of course.
0339      *
0340      * @note This only affects editable combo boxes.
0341      *
0342      * @see setTrapReturnKey()
0343      */
0344     void setTrapReturnKey(bool trap);
0345 
0346     /**
0347      * @return @c true if Key_Return or Key_Enter input events will be stopped or
0348      * @c false if they will be propagated.
0349      *
0350      * @see setTrapReturnKey()
0351      */
0352     bool trapReturnKey() const;
0353 
0354     /**
0355      * This method will create a completion box by calling
0356      * KLineEdit::completionBox, if none is there yet.
0357      *
0358      * @param create Set this to false if you don't want the box to be created
0359      *               i.e. to test if it is available.
0360      * @returns the completion box that is used in completion mode
0361      * CompletionPopup and CompletionPopupAuto.
0362      */
0363     KCompletionBox *completionBox(bool create = true);
0364 
0365     /**
0366      * Reimplemented for internal reasons. API remains unaffected.
0367      * Note that QComboBox::setLineEdit is not virtual in Qt4, do not
0368      * use a KComboBox in a QComboBox pointer.
0369      *
0370      * NOTE: Only editable combo boxes can have a line editor. As such
0371      * any attempt to assign a line edit to a non-editable combo box will
0372      * simply be ignored.
0373      */
0374     virtual void setLineEdit(QLineEdit *);
0375 
0376     /**
0377      * Reimplemented so that setEditable(true) creates a KLineEdit
0378      * instead of QLineEdit.
0379      *
0380      * Note that QComboBox::setEditable is not virtual, so do not
0381      * use a KComboBox in a QComboBox pointer.
0382      */
0383     void setEditable(bool editable);
0384 
0385     /**
0386      * Pointer to KLineEdit's context menu, or nullptr if it does not exist at
0387      * the given moment.
0388      *
0389      * @since 5.78
0390      */
0391     QMenu *contextMenu() const;
0392 
0393 Q_SIGNALS:
0394 #if KCOMPLETION_ENABLE_DEPRECATED_SINCE(5, 81)
0395     /**
0396      * Emitted when the user presses the Enter key.
0397      *
0398      * Note that this signal is only emitted when the widget is editable.
0399      *
0400      * @deprecated since 5.81, use the KComboBox::returnPressed(const QString &) signal
0401      */
0402     KCOMPLETION_DEPRECATED_VERSION(5, 81, "Use the KComboBox::returnPressed(const QString &) signal instead")
0403     void returnPressed(); // clazy:exclude=overloaded-signal
0404 #endif
0405 
0406     /**
0407      * Emitted when the user presses the Return or Enter key.
0408      *
0409      * The argument is the current text being edited.
0410      *
0411      * @note This signal is only emitted when the widget is editable.
0412      *
0413     */
0414     void returnPressed(const QString &text); // clazy:exclude=overloaded-signal
0415 
0416     /**
0417      * Emitted when the completion key is pressed.
0418      *
0419      * The argument is the current text being edited.
0420      *
0421      * Note that this signal is @em not available when the widget is non-editable
0422      * or the completion mode is set to @c CompletionNone.
0423      */
0424     void completion(const QString &);
0425 
0426     /**
0427      * Emitted when the shortcut for substring completion is pressed.
0428      */
0429     void substringCompletion(const QString &);
0430 
0431     /**
0432      * Emitted when the text rotation key bindings are pressed.
0433      *
0434      * The argument indicates which key binding was pressed. In this case this
0435      * can be either one of four values: @c PrevCompletionMatch,
0436      * @c NextCompletionMatch, @c RotateUp or @c RotateDown.
0437      *
0438      * Note that this signal is @em not emitted if the completion
0439      * mode is set to CompletionNone.
0440      *
0441      * @see KCompletionBase::setKeyBinding() for details
0442      */
0443     void textRotation(KCompletionBase::KeyBindingType);
0444 
0445     /**
0446      * Emitted whenever the completion mode is changed by the user
0447      * through the context menu.
0448      */
0449     void completionModeChanged(KCompletion::CompletionMode);
0450 
0451     /**
0452      * Emitted before the context menu is displayed.
0453      *
0454      * The signal allows you to add your own entries into the context menu.
0455      * Note that you <em>must not</em> store the pointer to the QPopupMenu since it is
0456      * created and deleted on demand. Otherwise, you can crash your app.
0457      *
0458      * @param contextMenu the context menu about to be displayed
0459      */
0460     void aboutToShowContextMenu(QMenu *contextMenu);
0461 
0462 public Q_SLOTS:
0463 
0464     /**
0465      * Iterates through all possible matches of the completed text
0466      * or the history list.
0467      *
0468      * Depending on the value of the argument, this function either
0469      * iterates through the history list of this widget or all the
0470      * possible matches in whenever multiple matches result from a
0471      * text completion request. Note that the all-possible-match
0472      * iteration will not work if there are no previous matches, i.e.
0473      * no text has been completed and the *nix shell history list
0474      * rotation is only available if the insertion policy for this
0475      * widget is set either @c QComobBox::AtTop or @c QComboBox::AtBottom.
0476      * For other insertion modes whatever has been typed by the user
0477      * when the rotation event was initiated will be lost.
0478      *
0479      * @param type The key binding invoked.
0480      */
0481     void rotateText(KCompletionBase::KeyBindingType type);
0482 
0483     /**
0484      * Sets the completed text in the line edit appropriately.
0485      *
0486      * This function is an implementation for
0487      * KCompletionBase::setCompletedText.
0488      */
0489     void setCompletedText(const QString &) override;
0490 
0491     /**
0492      * Sets @p items into the completion box if completionMode() is
0493      * CompletionPopup. The popup will be shown immediately.
0494      */
0495     void setCompletedItems(const QStringList &items, bool autoSuggest = true) override;
0496 
0497     /**
0498      * Selects the first item that matches @p item.
0499      *
0500      * If there is no such item, it is inserted at position @p index
0501      * if @p insert is true. Otherwise, no item is selected.
0502      */
0503     void setCurrentItem(const QString &item, bool insert = false, int index = -1);
0504 
0505 protected Q_SLOTS:
0506 
0507     /**
0508      * Completes text according to the completion mode.
0509      *
0510      * @note This method is not invoked if the completion mode is
0511      * set to @c CompletionNone. Also if the mode is set to @c CompletionShell
0512      * and multiple matches are found, this method will complete the
0513      * text to the first match with a beep to indicate that there are
0514      * more matches. Then any successive completion key event iterates
0515      * through the remaining matches. This way the rotation functionality
0516      * is left to iterate through the list as usual.
0517      */
0518     virtual void makeCompletion(const QString &);
0519 
0520 protected:
0521     /**
0522      * This function sets the line edit text and
0523      * highlights the text appropriately if the boolean
0524      * value is set to true.
0525      *
0526      * @param text The text to be set in the line edit
0527      * @param marked Whether the text inserted should be highlighted
0528      */
0529     virtual void setCompletedText(const QString &text, bool marked);
0530 
0531     // TODO KF6: make public like in base classes, so consumers do not need to cast to base classes
0532     // when they have a KComboBox (or subclasses) object and want to access this property
0533     QSize minimumSizeHint() const override;
0534 
0535 protected:
0536     KCOMPLETION_NO_EXPORT KComboBox(KComboBoxPrivate &dd, QWidget *parent);
0537 
0538 private:
0539     friend class KHistoryComboBox;
0540     Q_DECLARE_PRIVATE(KComboBox)
0541     std::unique_ptr<KComboBoxPrivate> const d_ptr;
0542     // KF6 TODO: change private d_ptr to protected d_ptr, remove friend
0543 };
0544 
0545 #endif