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 "qwidget.h"
42#include "qdebug.h"
43
44#include "qwidgetaction_p.h"
45
46QT_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*/
110QWidgetAction::QWidgetAction(QObject *parent)
111 : QAction(*(new QWidgetActionPrivate), parent)
112{
113}
114
115/*!
116 Destroys the object and frees allocated resources.
117*/
118QWidgetAction::~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*/
137void 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*/
158QWidget *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*/
173QWidget *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*/
200void 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*/
223bool 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 */
238bool 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*/
251QWidget *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*/
266void 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*/
276QList<QWidget *> QWidgetAction::createdWidgets() const
277{
278 Q_D(const QWidgetAction);
279 return d->createdWidgets;
280}
281
282QT_END_NAMESPACE
283
284#include "moc_qwidgetaction.cpp"
285