1 /*
2     This file is part of the KDE libraries
3     SPDX-FileCopyrightText: 1999 Reginald Stadlbauer <reggie@kde.org>
4     SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
5     SPDX-FileCopyrightText: 2000 Nicolas Hadacek <haadcek@kde.org>
6     SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org>
7     SPDX-FileCopyrightText: 2000 Michael Koch <koch@kde.org>
8     SPDX-FileCopyrightText: 2001 Holger Freyther <freyther@kde.org>
9     SPDX-FileCopyrightText: 2002 Ellis Whitehead <ellis@kde.org>
10     SPDX-FileCopyrightText: 2003 Andras Mantia <amantia@kde.org>
11     SPDX-FileCopyrightText: 2005-2006 Hamish Rodda <rodda@kde.org>
12     SPDX-FileCopyrightText: 2006 Albert Astals Cid <aacid@kde.org>
13     SPDX-FileCopyrightText: 2006 Clarence Dang <dang@kde.org>
14     SPDX-FileCopyrightText: 2006 Michel Hermier <michel.hermier@gmail.com>
15     SPDX-FileCopyrightText: 2007 Nick Shaforostoff <shafff@ukr.net>
16 
17     SPDX-License-Identifier: LGPL-2.0-only
18 */
19 
20 #ifndef KSELECTACTION_H
21 #define KSELECTACTION_H
22 
23 #include <QToolButton>
24 #include <QWidgetAction>
25 #include <memory>
26 
27 #include <kwidgetsaddons_export.h>
28 
29 class KSelectActionPrivate;
30 
31 /**
32  *  @class KSelectAction kselectaction.h KSelectAction
33  *
34  *  @short Action for selecting one of several items
35  *
36  *  Action for selecting one of several items.
37  *
38  *  This action shows up a submenu with a list of items.
39  *  One of them can be checked. If the user clicks on an item
40  *  this item will automatically be checked,
41  *  the formerly checked item becomes unchecked.
42  *  There can be only one item checked at a time.
43  */
44 class KWIDGETSADDONS_EXPORT KSelectAction : public QWidgetAction
45 {
46     Q_OBJECT
47     Q_PROPERTY(QAction *currentAction READ currentAction WRITE setCurrentAction)
48     Q_PROPERTY(bool editable READ isEditable WRITE setEditable)
49     Q_PROPERTY(int comboWidth READ comboWidth WRITE setComboWidth)
50     Q_PROPERTY(QString currentText READ currentText)
51     Q_PROPERTY(ToolBarMode toolBarMode READ toolBarMode WRITE setToolBarMode)
52     Q_PROPERTY(QToolButton::ToolButtonPopupMode toolButtonPopupMode READ toolButtonPopupMode WRITE setToolButtonPopupMode)
53     Q_PROPERTY(int currentItem READ currentItem WRITE setCurrentItem)
54     Q_PROPERTY(QStringList items READ items WRITE setItems)
55 
56 public:
57     /**
58      * Constructs a selection action with the specified parent.
59      *
60      * @param parent The action's parent object.
61      */
62     explicit KSelectAction(QObject *parent);
63 
64     /**
65      * Constructs a selection action with text; a shortcut may be specified by
66      * the ampersand character (e.g.\ "&Option" creates a shortcut with key \e O )
67      *
68      * This is the most common KSelectAction used when you do not have a
69      * corresponding icon (note that it won't appear in the current version
70      * of the "Edit ToolBar" dialog, because an action needs an icon to be
71      * plugged in a toolbar...).
72      *
73      * @param text The text that will be displayed.
74      * @param parent The action's parent object.
75      */
76     KSelectAction(const QString &text, QObject *parent);
77 
78     /**
79      * Constructs a selection action with text and an icon; a shortcut may be specified by
80      * the ampersand character (e.g.\ "&Option" creates a shortcut with key \e O )
81      *
82      * This is the other common KSelectAction used.  Use it when you
83      * \e do have a corresponding icon.
84      *
85      * @param icon The icon to display.
86      * @param text The text that will be displayed.
87      * @param parent The action's parent object.
88      */
89     KSelectAction(const QIcon &icon, const QString &text, QObject *parent);
90 
91     /**
92      * Destructor
93      */
94     ~KSelectAction() override;
95 
96     enum ToolBarMode {
97         /// Creates a button which pops up a menu when interacted with, as defined by toolButtonPopupMode().
98         MenuMode,
99         /// Creates a combo box which contains the actions.
100         /// This is the default.
101         ComboBoxMode,
102     };
103     Q_ENUM(ToolBarMode)
104 
105     /**
106      * Returns which type of widget (combo box or button with drop-down menu) will be inserted
107      * in a toolbar.
108      */
109     ToolBarMode toolBarMode() const;
110 
111     /**
112      * Set the type of widget to be inserted in a toolbar to \a mode.
113      */
114     void setToolBarMode(ToolBarMode mode);
115 
116     /**
117      * Returns the style for the list of actions, when this action is plugged
118      * into a KToolBar. The default value is QToolButton::InstantPopup
119      *
120      * \sa QToolButton::setPopupMode()
121      */
122     QToolButton::ToolButtonPopupMode toolButtonPopupMode() const;
123 
124     /**
125      * Set how this list of actions should behave when in popup mode and plugged into a toolbar.
126      */
127     void setToolButtonPopupMode(QToolButton::ToolButtonPopupMode mode);
128 
129     /**
130      * The action group used to create exclusivity between the actions associated with this action.
131      */
132     QActionGroup *selectableActionGroup() const;
133 
134     /**
135      * Returns the current QAction.
136      * @see setCurrentAction
137      */
138     QAction *currentAction() const;
139 
140     /**
141      * Returns the index of the current item.
142      *
143      * @sa currentText(), currentAction()
144      */
145     int currentItem() const;
146 
147     /**
148      * Returns the text of the currently selected item.
149      *
150      * @sa currentItem(), currentAction()
151      */
152     QString currentText() const;
153 
154     /**
155      * Returns the list of selectable actions
156      */
157     QList<QAction *> actions() const;
158 
159     /**
160      * Returns the action at \a index, if one exists.
161      */
162     QAction *action(int index) const;
163 
164     /**
165      * Searches for an action with the specified \a text, using a search whose
166      * case sensitivity is defined by \a cs.
167      */
168     QAction *action(const QString &text, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
169 
170     /**
171      * Sets the currently checked item.
172      *
173      * @param action the QAction to become the currently checked item.
174      *
175      * \return \e true if a corresponding action was found and successfully checked.
176      */
177     bool setCurrentAction(QAction *action);
178 
179     /**
180      * Convenience function to set the currently checked action to be the action
181      * at index \p index.
182      *
183      * If there is no action at that index, the currently checked action (if any) will
184      * be deselected.
185      *
186      * \return \e true if a corresponding action was found and thus set to the current action, otherwise \e false
187      */
188     bool setCurrentItem(int index);
189 
190     /**
191      * Overloaded member function, provided for convenience, to set the currently
192      * checked action to be the action which has \p text as its text().
193      *
194      * If there is no action at that index, the currently checked action (if any) will
195      * be deselected.
196      *
197      * \return \e true if a corresponding action was found, otherwise \e false
198      */
199     bool setCurrentAction(const QString &text, Qt::CaseSensitivity cs = Qt::CaseSensitive);
200 
201     /**
202      * Add \a action to the list of selectable actions.
203      */
204     void addAction(QAction *action);
205 
206     /**
207      * Overloaded member function, provided for convenience, which creates an action
208      * from \p text and inserts it into the list of selectable actions.
209      *
210      * The newly created action is checkable and not user configurable.
211      */
212     QAction *addAction(const QString &text);
213 
214     /**
215      * Overloaded member function, provided for convenience, which creates an action
216      * from \p text and \p icon and inserts it into the list of selectable actions.
217      *
218      * The newly created action is checkable and not user configurable.
219      */
220     QAction *addAction(const QIcon &icon, const QString &text);
221 
222     /**
223      * Remove the specified \a action from this action selector.
224      *
225      * You take ownership here, so save or delete it in order to not leak the action.
226      */
227     virtual QAction *removeAction(QAction *action);
228 
229     /**
230      * Inserts the action action to this widget's list of actions, before the action before.
231      * It appends the action if before is a null pointer or before is not a valid action for this widget.
232      *
233      * @since 5.0
234      */
235     virtual void insertAction(QAction *before, QAction *action);
236 
237     /**
238      * Convenience function to create the list of selectable items.
239      * Any previously existing items will be cleared.
240      */
241     void setItems(const QStringList &lst);
242 
243     /**
244      * Convenience function which returns the items that can be selected with this action.
245      * It is the same as iterating selectableActionGroup()->actions() and looking at each
246      * action's text().
247      */
248     QStringList items() const;
249 
250     /**
251      * When this action is plugged into a toolbar, it creates a combobox.
252      * @return true if the combo editable.
253      */
254     bool isEditable() const;
255 
256     /**
257      * When this action is plugged into a toolbar, it creates a combobox.
258      * This makes the combo editable or read-only.
259      */
260     void setEditable(bool);
261 
262     /**
263      * When this action is plugged into a toolbar, it creates a combobox.
264      * This returns the maximum width set by setComboWidth
265      */
266     int comboWidth() const;
267 
268     /**
269      * When this action is plugged into a toolbar, it creates a combobox.
270      * This gives a _maximum_ size to the combobox.
271      * The minimum size is automatically given by the contents (the items).
272      */
273     void setComboWidth(int width);
274 
275     /**
276      * Sets the maximum items that are visible at once if the action
277      * is a combobox, that is the number of items in the combobox's viewport
278      */
279     void setMaxComboViewCount(int n);
280 
281     /**
282      * Remove and delete all the items in this action.
283      *
284      * @see removeAllActions()
285      */
286     void clear();
287 
288     /**
289      * Remove all the items in this action.
290      *
291      * Unlike clear(), this will not delete the actions.
292      *
293      * @see clear()
294      */
295     void removeAllActions();
296 
297     /**
298      * Sets whether any occurrence of the ampersand character ( & ) in items
299      * should be interpreted as keyboard accelerator for items displayed in a
300      * menu or not.  Only applies to (overloaded) methods dealing with QStrings,
301      * not those dealing with QActions.
302      *
303      * Defaults to true.
304      *
305      * \param b true if ampersands indicate a keyboard accelerator, otherwise false.
306      */
307     void setMenuAccelsEnabled(bool b);
308 
309     /**
310      * Returns whether ampersands passed to methods using QStrings are interpreted
311      * as keyboard accelerator indicators or as literal ampersands.
312      */
313     bool menuAccelsEnabled() const;
314 
315     /**
316      * Changes the text of item @param index to @param text .
317      */
318     void changeItem(int index, const QString &text);
319 
320 Q_SIGNALS:
321     /**
322      * This signal is emitted when an item is selected.
323      * @param action indicates the item selected
324      */
325     void triggered(QAction *action); // clazy:exclude=overloaded-signal
326     // TODO KF6:: rename to actionTriggered(QAction *action)
327     // We cannot do this in KF5, due to existing slot method with same signature, see below
328 
329 #if KWIDGETSADDONS_ENABLE_DEPRECATED_SINCE(5, 78)
330     /**
331      * This signal is emitted when an item is selected.
332      * @param index indicates the item selected
333      * @deprecated Since 5.78, use indexTriggered(int)
334      */
335     KWIDGETSADDONS_DEPRECATED_VERSION(5, 78, "Use KSelectAction::indexTriggered(int)")
336     void triggered(int index); // clazy:exclude=overloaded-signal
337 #endif
338     /**
339      * This signal is emitted when an item is selected.
340      * @param index indicates the item selected
341      *
342      * In your KSelectAction subclass to be backward-compatible to KF < 5.78, emit instead
343      * just the deprecated signal triggered(int). That will also
344      * automatically emit indexTriggered(int) because this signal
345      * is connected to the deprecated one in the KSelectAction constructor.
346      * Only if you compile against a variant of KWidgetsAddons built without all API
347      * deprecated up to version 5.78, then emit this signal directly.
348      * Your code would be like this:
349      * @code
350      *     const int index = selectableActionGroup()->actions().indexOf(action);
351      * #if KWIDGETSADDONS_BUILD_DEPRECATED_SINCE(5, 78)
352      *     // will also indirectly emit indexTriggered since 5.78
353      *     Q_EMIT triggered(index);
354      * #else
355      *     Q_EMIT indexTriggered(index);
356      * #endif
357      * @endcode
358      *
359      * @since 5.78
360      */
361     void indexTriggered(int index);
362 
363 #if KWIDGETSADDONS_ENABLE_DEPRECATED_SINCE(5, 78)
364     /**
365      * This signal is emitted when an item is selected.
366      * @param text indicates the item selected
367      * @deprecated Since 5.78, use textTriggered(const QString &)
368      */
369     KWIDGETSADDONS_DEPRECATED_VERSION(5, 78, "Use KSelectAction::textTriggered(const QString &)")
370     void triggered(const QString &text); // clazy:exclude=overloaded-signal
371 #endif
372 
373     /**
374      * This signal is emitted when an item is selected.
375      * @param text indicates the item selected
376      *
377      * In your KSelectAction subclass to be backward-compatible to KF < 5.78, emit instead
378      * just the deprecated signal triggered(const QString &). That will also
379      * automatically emit textTriggered(const QString &) because this signal
380      * is connected to the deprecated one in the KSelectAction constructor.
381      * Only if you compile against a variant of KWidgetsAddons built without all API
382      * deprecated up to version 5.78, then emit this signal directly.
383      * Your code would be like this:
384      * @code
385      * #if KWIDGETSADDONS_BUILD_DEPRECATED_SINCE(5, 78)
386      *     // will also indirectly emit textTriggered since 5.78
387      *     Q_EMIT triggered(action->text());
388      * #else
389      *     Q_EMIT textTriggered(action->text());
390      * #endif
391      * @endcode
392      *
393      * @since 5.78
394      */
395     void textTriggered(const QString &text);
396 
397 protected Q_SLOTS:
398     /**
399      * This function is called whenever an action from the selections is triggered.
400      * The default implementation calls trigger() if isCheckable() is @c true, then emits
401      * the signals triggered(QAction *), triggered(int) and triggered(const QString &)
402      * as well as since 5.78 the signals indexTriggered(int) and textTriggered(const QString &).
403      */
404     virtual void actionTriggered(QAction *action);
405     // TODO KF6:: rename to handleActionTriggered, to release name to signal
406 
407     /**
408      * For structured menu building. Deselects all items if the action was unchecked by the top menu
409      */
410     void slotToggled(bool);
411 
412 protected:
413     /**
414      * Reimplemented from QWidgetAction.
415      */
416     QWidget *createWidget(QWidget *parent) override;
417 
418     /**
419      * Reimplemented from QWidgetAction.
420      */
421     void deleteWidget(QWidget *widget) override;
422 
423     bool event(QEvent *event) override;
424 
425     bool eventFilter(QObject *watched, QEvent *event) override;
426 
427     /**
428      * @internal
429      * Creates a new KSelectAction object.
430      *
431      * @param dd the private d member
432      * @param parent The action's parent object.
433      */
434     KSelectAction(KSelectActionPrivate &dd, QObject *parent);
435 
436     std::unique_ptr<class KSelectActionPrivate> const d_ptr;
437 
438 private:
439     Q_DECLARE_PRIVATE(KSelectAction)
440 };
441 
442 #endif
443