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 Qt Designer of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
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 General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include "promotiontaskmenu_p.h"
30#include "qdesigner_promotiondialog_p.h"
31#include "widgetfactory_p.h"
32#include "metadatabase_p.h"
33#include "widgetdatabase_p.h"
34#include "qdesigner_command_p.h"
35#include "signalslotdialog_p.h"
36#include "qdesigner_objectinspector_p.h"
37#include "abstractintrospection_p.h"
38
39#include <QtDesigner/abstractformwindow.h>
40#include <QtDesigner/abstractformwindowcursor.h>
41#include <QtDesigner/abstractlanguage.h>
42#include <QtDesigner/abstractformeditor.h>
43#include <QtDesigner/qextensionmanager.h>
44
45#include <QtWidgets/qaction.h>
46#include <QtWidgets/qwidget.h>
47#include <QtWidgets/qmenu.h>
48#include <QtCore/qdebug.h>
49
50QT_BEGIN_NAMESPACE
51
52static QAction *separatorAction(QObject *parent)
53{
54 QAction *rc = new QAction(parent);
55 rc->setSeparator(true);
56 return rc;
57}
58
59static inline QDesignerLanguageExtension *languageExtension(QDesignerFormEditorInterface *core)
60{
61 return qt_extension<QDesignerLanguageExtension*>(manager: core->extensionManager(), object: core);
62}
63
64namespace qdesigner_internal {
65
66PromotionTaskMenu::PromotionTaskMenu(QWidget *widget,Mode mode, QObject *parent) :
67 QObject(parent),
68 m_mode(mode),
69 m_widget(widget),
70 m_globalEditAction(new QAction(tr(s: "Promoted widgets..."), this)),
71 m_EditPromoteToAction(new QAction(tr(s: "Promote to ..."), this)),
72 m_EditSignalsSlotsAction(new QAction(tr(s: "Change signals/slots..."), this)),
73 m_promoteLabel(tr(s: "Promote to")),
74 m_demoteLabel(tr(s: "Demote to %1"))
75{
76 connect(sender: m_globalEditAction, signal: &QAction::triggered, receiver: this, slot: &PromotionTaskMenu::slotEditPromotedWidgets);
77 connect(sender: m_EditPromoteToAction, signal: &QAction::triggered, receiver: this, slot: &PromotionTaskMenu::slotEditPromoteTo);
78 connect(sender: m_EditSignalsSlotsAction, signal: &QAction::triggered, receiver: this, slot: &PromotionTaskMenu::slotEditSignalsSlots);
79}
80
81PromotionTaskMenu::Mode PromotionTaskMenu::mode() const
82{
83 return m_mode;
84}
85
86void PromotionTaskMenu::setMode(Mode m)
87{
88 m_mode = m;
89}
90
91void PromotionTaskMenu::setWidget(QWidget *widget)
92{
93 m_widget = widget;
94}
95
96void PromotionTaskMenu::setPromoteLabel(const QString &promoteLabel)
97{
98 m_promoteLabel = promoteLabel;
99}
100
101void PromotionTaskMenu::setEditPromoteToLabel(const QString &promoteEditLabel)
102{
103 m_EditPromoteToAction->setText(promoteEditLabel);
104}
105
106void PromotionTaskMenu::setDemoteLabel(const QString &demoteLabel)
107{
108 m_demoteLabel = demoteLabel;
109}
110
111PromotionTaskMenu::PromotionState PromotionTaskMenu::createPromotionActions(QDesignerFormWindowInterface *formWindow)
112{
113 // clear out old
114 if (!m_promotionActions.isEmpty()) {
115 qDeleteAll(c: m_promotionActions);
116 m_promotionActions.clear();
117 }
118 // No promotion of main container
119 if (formWindow->mainContainer() == m_widget)
120 return NotApplicable;
121
122 // Check for a homogenous selection
123 const PromotionSelectionList promotionSelection = promotionSelectionList(formWindow);
124
125 if (promotionSelection.isEmpty())
126 return NoHomogenousSelection;
127
128 QDesignerFormEditorInterface *core = formWindow->core();
129 // if it is promoted: demote only.
130 if (isPromoted(core: formWindow->core(), w: m_widget)) {
131 const QString label = m_demoteLabel.arg( a: promotedExtends(core , w: m_widget));
132 QAction *demoteAction = new QAction(label, this);
133 connect(sender: demoteAction, signal: &QAction::triggered, receiver: this, slot: &PromotionTaskMenu::slotDemoteFromCustomWidget);
134 m_promotionActions.push_back(t: demoteAction);
135 return CanDemote;
136 }
137 // figure out candidates
138 const QString baseClassName = WidgetFactory::classNameOf(core, o: m_widget);
139 const WidgetDataBaseItemList candidates = promotionCandidates(db: core->widgetDataBase(), baseClassName );
140 if (candidates.isEmpty()) {
141 // Is this thing promotable at all?
142 return QDesignerPromotionDialog::baseClassNames(promotion: core->promotion()).contains(str: baseClassName) ? CanPromote : NotApplicable;
143 }
144
145 QMenu *candidatesMenu = new QMenu();
146 // Create a sub menu
147 const WidgetDataBaseItemList::const_iterator cend = candidates.constEnd();
148 // Set up actions and map class names
149 for (WidgetDataBaseItemList::const_iterator it = candidates.constBegin(); it != cend; ++it) {
150 const QString customClassName = (*it)->name();
151 candidatesMenu->addAction(text: customClassName,
152 object: this, slot: [this, customClassName] { this->slotPromoteToCustomWidget(customClassName); });
153 }
154 // Sub menu action
155 QAction *subMenuAction = new QAction(m_promoteLabel, this);
156 subMenuAction->setMenu(candidatesMenu);
157 m_promotionActions.push_back(t: subMenuAction);
158 return CanPromote;
159}
160
161void PromotionTaskMenu::addActions(unsigned separatorFlags, ActionList &actionList)
162{
163 addActions(fw: formWindow(), flags: separatorFlags, actionList);
164}
165
166void PromotionTaskMenu::addActions(QDesignerFormWindowInterface *fw, unsigned flags,
167 ActionList &actionList)
168{
169 Q_ASSERT(m_widget);
170 const int previousSize = actionList.size();
171 const PromotionState promotionState = createPromotionActions(formWindow: fw);
172
173 // Promotion candidates/demote
174 actionList += m_promotionActions;
175
176 // Edit action depending on context
177 switch (promotionState) {
178 case CanPromote:
179 actionList += m_EditPromoteToAction;
180 break;
181 case CanDemote:
182 if (!(flags & SuppressGlobalEdit))
183 actionList += m_globalEditAction;
184 if (!languageExtension(core: fw->core())) {
185 actionList += separatorAction(parent: this);
186 actionList += m_EditSignalsSlotsAction;
187 }
188 break;
189 default:
190 if (!(flags & SuppressGlobalEdit))
191 actionList += m_globalEditAction;
192 break;
193 }
194 // Add separators if required
195 if (actionList.size() > previousSize) {
196 if (flags & LeadingSeparator)
197 actionList.insert(i: previousSize, t: separatorAction(parent: this));
198 if (flags & TrailingSeparator)
199 actionList += separatorAction(parent: this);
200 }
201}
202
203void PromotionTaskMenu::addActions(QDesignerFormWindowInterface *fw, unsigned flags, QMenu *menu)
204{
205 ActionList actionList;
206 addActions(fw, flags, actionList);
207 menu->addActions(actions: actionList);
208}
209
210void PromotionTaskMenu::addActions(unsigned flags, QMenu *menu)
211{
212 addActions(fw: formWindow(), flags, menu);
213}
214
215void PromotionTaskMenu::promoteTo(QDesignerFormWindowInterface *fw, const QString &customClassName)
216{
217 Q_ASSERT(m_widget);
218 PromoteToCustomWidgetCommand *cmd = new PromoteToCustomWidgetCommand(fw);
219 cmd->init(widgets: promotionSelectionList(formWindow: fw), customClassName);
220 fw->commandHistory()->push(cmd);
221}
222
223
224void PromotionTaskMenu::slotPromoteToCustomWidget(const QString &customClassName)
225{
226 promoteTo(fw: formWindow(), customClassName);
227}
228
229void PromotionTaskMenu::slotDemoteFromCustomWidget()
230{
231 QDesignerFormWindowInterface *fw = formWindow();
232 const PromotionSelectionList promotedWidgets = promotionSelectionList(formWindow: fw);
233 Q_ASSERT(!promotedWidgets.isEmpty() && isPromoted(fw->core(), promotedWidgets.constFirst()));
234
235 // ### use the undo stack
236 DemoteFromCustomWidgetCommand *cmd = new DemoteFromCustomWidgetCommand(fw);
237 cmd->init(promoted: promotedWidgets);
238 fw->commandHistory()->push(cmd);
239}
240
241void PromotionTaskMenu::slotEditPromoteTo()
242{
243 Q_ASSERT(m_widget);
244 // Check whether invoked over a promotable widget
245 QDesignerFormWindowInterface *fw = formWindow();
246 QDesignerFormEditorInterface *core = fw->core();
247 const QString base_class_name = WidgetFactory::classNameOf(core, o: m_widget);
248 Q_ASSERT(QDesignerPromotionDialog::baseClassNames(core->promotion()).contains(base_class_name));
249 // Show over promotable widget
250 QString promoteToClassName;
251 QDialog *promotionEditor = nullptr;
252 if (QDesignerLanguageExtension *lang = languageExtension(core))
253 promotionEditor = lang->createPromotionDialog(formEditor: core, promotableWidgetClassName: base_class_name, promoteToClassName: &promoteToClassName, parentWidget: fw);
254 if (!promotionEditor)
255 promotionEditor = new QDesignerPromotionDialog(core, fw, base_class_name, &promoteToClassName);
256 if (promotionEditor->exec() == QDialog::Accepted && !promoteToClassName.isEmpty()) {
257 promoteTo(fw, customClassName: promoteToClassName);
258 }
259 delete promotionEditor;
260}
261
262void PromotionTaskMenu::slotEditPromotedWidgets()
263{
264 // Global context, show over non-promotable widget
265 QDesignerFormWindowInterface *fw = formWindow();
266 if (!fw)
267 return;
268 editPromotedWidgets(core: fw->core(), parent: fw);
269}
270
271PromotionTaskMenu::PromotionSelectionList PromotionTaskMenu::promotionSelectionList(QDesignerFormWindowInterface *formWindow) const
272{
273 // In multi selection mode, check for a homogenous selection (same class, same promotion state)
274 // and return the list if this is the case. Also make sure m_widget
275 // is the last widget in the list so that it is re-selected as the last
276 // widget by the promotion commands.
277
278 PromotionSelectionList rc;
279
280 if (m_mode != ModeSingleWidget) {
281 QDesignerFormEditorInterface *core = formWindow->core();
282 const QDesignerIntrospectionInterface *intro = core->introspection();
283 const QString className = intro->metaObject(object: m_widget)->className();
284 const bool promoted = isPromoted(core: formWindow->core(), w: m_widget);
285 // Just in case someone plugged an old-style Object Inspector
286 if (QDesignerObjectInspector *designerObjectInspector = qobject_cast<QDesignerObjectInspector *>(object: core->objectInspector())) {
287 Selection s;
288 designerObjectInspector->getSelection(s);
289 // Find objects of similar state
290 const QWidgetList &source = m_mode == ModeManagedMultiSelection ? s.managed : s.unmanaged;
291 const QWidgetList::const_iterator cend = source.constEnd();
292 for (QWidgetList::const_iterator it = source.constBegin(); it != cend; ++it) {
293 QWidget *w = *it;
294 if (w != m_widget) {
295 // Selection state mismatch
296 if (intro->metaObject(object: w)->className() != className || isPromoted(core, w) != promoted)
297 return PromotionSelectionList();
298 rc.push_back(t: w);
299 }
300 }
301 }
302 }
303
304 rc.push_back(t: m_widget);
305 return rc;
306}
307
308QDesignerFormWindowInterface *PromotionTaskMenu::formWindow() const
309{
310 // Use the QObject overload of QDesignerFormWindowInterface::findFormWindow since that works
311 // for QDesignerMenus also.
312 QObject *o = m_widget;
313 QDesignerFormWindowInterface *result = QDesignerFormWindowInterface::findFormWindow(obj: o);
314 Q_ASSERT(result != nullptr);
315 return result;
316}
317
318void PromotionTaskMenu::editPromotedWidgets(QDesignerFormEditorInterface *core, QWidget* parent) {
319 QDesignerLanguageExtension *lang = languageExtension(core);
320 // Show over non-promotable widget
321 QDialog *promotionEditor = nullptr;
322 if (lang)
323 lang->createPromotionDialog(formEditor: core, parentWidget: parent);
324 if (!promotionEditor)
325 promotionEditor = new QDesignerPromotionDialog(core, parent);
326 promotionEditor->exec();
327 delete promotionEditor;
328}
329
330void PromotionTaskMenu::slotEditSignalsSlots()
331{
332 QDesignerFormWindowInterface *fw = formWindow();
333 if (!fw)
334 return;
335 SignalSlotDialog::editPromotedClass(core: fw->core(), baseObject: m_widget, parent: fw);
336}
337} // namespace qdesigner_internal
338
339QT_END_NAMESPACE
340

source code of qttools/src/designer/src/lib/shared/promotiontaskmenu.cpp