1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtWidgets module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qwidgetaction.h"
41 #include "qdebug.h"
42 
43 #ifndef QT_NO_ACTION
44 #include "qwidgetaction_p.h"
45 
46 QT_BEGIN_NAMESPACE
47 
48 /*!
49     \class QWidgetAction
50     \since 4.2
51     \brief The QWidgetAction class extends QAction by an interface
52     for inserting custom widgets into action based containers, such
53     as toolbars.
54 
55     \ingroup mainwindow-classes
56     \inmodule QtWidgets
57 
58     Most actions in an application are represented as items in menus or
59     buttons in toolbars. However sometimes more complex widgets are
60     necessary. For example a zoom action in a word processor may be
61     realized using a QComboBox in a QToolBar, presenting a range
62     of different zoom levels. QToolBar provides QToolBar::insertWidget()
63     as convenience function for inserting a single widget.
64     However if you want to implement an action that uses custom
65     widgets for visualization in multiple containers then you have to
66     subclass QWidgetAction.
67 
68     If a QWidgetAction is added for example to a QToolBar then
69     QWidgetAction::createWidget() is called. Reimplementations of that
70     function should create a new custom widget with the specified parent.
71 
72     If the action is removed from a container widget then
73     QWidgetAction::deleteWidget() is called with the previously created custom
74     widget as argument. The default implementation hides the widget and deletes
75     it using QObject::deleteLater().
76 
77     If you have only one single custom widget then you can set it as default
78     widget using setDefaultWidget(). That widget will then be used if the
79     action is added to a QToolBar, or in general to an action container that
80     supports QWidgetAction. If a QWidgetAction with only a default widget is
81     added to two toolbars at the same time then the default widget is shown
82     only in the first toolbar the action was added to. QWidgetAction takes
83     over ownership of the default widget.
84 
85     Note that it is up to the widget to activate the action, for example by
86     reimplementing mouse event handlers and calling QAction::trigger().
87 
88     \b {\macos}: If you add a widget to a menu in the application's menu
89     bar on \macos, the widget will be added and it will function but with some
90     limitations:
91     \list 1
92         \li The widget is reparented away from the QMenu to the native menu
93             view. If you show the menu in some other place (e.g. as a popup menu),
94             the widget will not be there.
95         \li Focus/Keyboard handling of the widget is not possible.
96         \li Due to Apple's design, mouse tracking on the widget currently does
97             not work.
98         \li Connecting the triggered() signal to a slot that opens a modal
99             dialog will cause a crash in \macos 10.4 (known bug acknowledged
100             by Apple), a workaround is to use a QueuedConnection instead of a
101             DirectConnection.
102     \endlist
103 
104     \sa QAction, QActionGroup, QWidget
105 */
106 
107 /*!
108     Constructs an action with \a parent.
109 */
QWidgetAction(QObject * parent)110 QWidgetAction::QWidgetAction(QObject *parent)
111     : QAction(*(new QWidgetActionPrivate), parent)
112 {
113 }
114 
115 /*!
116     Destroys the object and frees allocated resources.
117 */
~QWidgetAction()118 QWidgetAction::~QWidgetAction()
119 {
120     Q_D(QWidgetAction);
121     for (int i = 0; i < d->createdWidgets.count(); ++i)
122         disconnect(d->createdWidgets.at(i), SIGNAL(destroyed(QObject*)),
123                    this, SLOT(_q_widgetDestroyed(QObject*)));
124     QList<QWidget *> widgetsToDelete = d->createdWidgets;
125     d->createdWidgets.clear();
126     qDeleteAll(widgetsToDelete);
127     delete d->defaultWidget;
128 }
129 
130 /*!
131     Sets \a widget to be the default widget. The ownership is
132     transferred to QWidgetAction. Unless createWidget() is
133     reimplemented by a subclass to return a new widget the default
134     widget is used when a container widget requests a widget through
135     requestWidget().
136 */
setDefaultWidget(QWidget * widget)137 void QWidgetAction::setDefaultWidget(QWidget *widget)
138 {
139     Q_D(QWidgetAction);
140     if (widget == d->defaultWidget || d->defaultWidgetInUse)
141         return;
142     delete d->defaultWidget;
143     d->defaultWidget = widget;
144     if (!widget)
145         return;
146 
147     setVisible(!(widget->isHidden() && widget->testAttribute(Qt::WA_WState_ExplicitShowHide)));
148     d->defaultWidget->hide();
149     d->defaultWidget->setParent(nullptr);
150     d->defaultWidgetInUse = false;
151     if (!isEnabled())
152         d->defaultWidget->setEnabled(false);
153 }
154 
155 /*!
156     Returns the default widget.
157 */
defaultWidget() const158 QWidget *QWidgetAction::defaultWidget() const
159 {
160     Q_D(const QWidgetAction);
161     return d->defaultWidget;
162 }
163 
164 /*!
165     Returns a widget that represents the action, with the given \a
166     parent.
167 
168     Container widgets that support actions can call this function to
169     request a widget as visual representation of the action.
170 
171     \sa releaseWidget(), createWidget(), defaultWidget()
172 */
requestWidget(QWidget * parent)173 QWidget *QWidgetAction::requestWidget(QWidget *parent)
174 {
175     Q_D(QWidgetAction);
176 
177     QWidget *w = createWidget(parent);
178     if (!w) {
179         if (d->defaultWidgetInUse || !d->defaultWidget)
180             return nullptr;
181         d->defaultWidget->setParent(parent);
182         d->defaultWidgetInUse = true;
183         return d->defaultWidget;
184     }
185 
186     connect(w, SIGNAL(destroyed(QObject*)),
187             this, SLOT(_q_widgetDestroyed(QObject*)));
188     d->createdWidgets.append(w);
189     return w;
190 }
191 
192 /*!
193     Releases the specified \a widget.
194 
195     Container widgets that support actions call this function when a widget
196     action is removed.
197 
198     \sa requestWidget(), deleteWidget(), defaultWidget()
199 */
releaseWidget(QWidget * widget)200 void QWidgetAction::releaseWidget(QWidget *widget)
201 {
202     Q_D(QWidgetAction);
203 
204     if (widget == d->defaultWidget) {
205         d->defaultWidget->hide();
206         d->defaultWidget->setParent(nullptr);
207         d->defaultWidgetInUse = false;
208         return;
209     }
210 
211     if (!d->createdWidgets.contains(widget))
212         return;
213 
214     disconnect(widget, SIGNAL(destroyed(QObject*)),
215                this, SLOT(_q_widgetDestroyed(QObject*)));
216     d->createdWidgets.removeAll(widget);
217     deleteWidget(widget);
218 }
219 
220 /*!
221     \reimp
222 */
event(QEvent * event)223 bool QWidgetAction::event(QEvent *event)
224 {
225     Q_D(QWidgetAction);
226     if (event->type() == QEvent::ActionChanged) {
227         if (d->defaultWidget)
228             d->defaultWidget->setEnabled(isEnabled());
229         for (int i = 0; i < d->createdWidgets.count(); ++i)
230             d->createdWidgets.at(i)->setEnabled(isEnabled());
231     }
232     return QAction::event(event);
233 }
234 
235 /*!
236     \reimp
237  */
eventFilter(QObject * obj,QEvent * event)238 bool QWidgetAction::eventFilter(QObject *obj, QEvent *event)
239 {
240     return QAction::eventFilter(obj,event);
241 }
242 
243 /*!
244     This function is called whenever the action is added to a container widget
245     that supports custom widgets. If you don't want a custom widget to be
246     used as representation of the action in the specified \a parent widget then
247     0 should be returned.
248 
249     \sa deleteWidget()
250 */
createWidget(QWidget * parent)251 QWidget *QWidgetAction::createWidget(QWidget *parent)
252 {
253     Q_UNUSED(parent)
254     return nullptr;
255 }
256 
257 /*!
258     This function is called whenever the action is removed from a
259     container widget that displays the action using a custom \a
260     widget previously created using createWidget(). The default
261     implementation hides the \a widget and schedules it for deletion
262     using QObject::deleteLater().
263 
264     \sa createWidget()
265 */
deleteWidget(QWidget * widget)266 void QWidgetAction::deleteWidget(QWidget *widget)
267 {
268     widget->hide();
269     widget->deleteLater();
270 }
271 
272 /*!
273     Returns the list of widgets that have been using createWidget() and
274     are currently in use by widgets the action has been added to.
275 */
createdWidgets() const276 QList<QWidget *> QWidgetAction::createdWidgets() const
277 {
278     Q_D(const QWidgetAction);
279     return d->createdWidgets;
280 }
281 
282 QT_END_NAMESPACE
283 
284 #include "moc_qwidgetaction.cpp"
285 
286 #endif // QT_NO_ACTION
287