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 | |
50 | QT_BEGIN_NAMESPACE |
51 | |
52 | static QAction *separatorAction(QObject *parent) |
53 | { |
54 | QAction *rc = new QAction(parent); |
55 | rc->setSeparator(true); |
56 | return rc; |
57 | } |
58 | |
59 | static inline QDesignerLanguageExtension *languageExtension(QDesignerFormEditorInterface *core) |
60 | { |
61 | return qt_extension<QDesignerLanguageExtension*>(manager: core->extensionManager(), object: core); |
62 | } |
63 | |
64 | namespace qdesigner_internal { |
65 | |
66 | 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 | |
81 | PromotionTaskMenu::Mode PromotionTaskMenu::() const |
82 | { |
83 | return m_mode; |
84 | } |
85 | |
86 | void PromotionTaskMenu::(Mode m) |
87 | { |
88 | m_mode = m; |
89 | } |
90 | |
91 | void PromotionTaskMenu::(QWidget *widget) |
92 | { |
93 | m_widget = widget; |
94 | } |
95 | |
96 | void PromotionTaskMenu::(const QString &promoteLabel) |
97 | { |
98 | m_promoteLabel = promoteLabel; |
99 | } |
100 | |
101 | void PromotionTaskMenu::(const QString &promoteEditLabel) |
102 | { |
103 | m_EditPromoteToAction->setText(promoteEditLabel); |
104 | } |
105 | |
106 | void PromotionTaskMenu::(const QString &demoteLabel) |
107 | { |
108 | m_demoteLabel = demoteLabel; |
109 | } |
110 | |
111 | PromotionTaskMenu::PromotionState PromotionTaskMenu::(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 * = new QAction(m_promoteLabel, this); |
156 | subMenuAction->setMenu(candidatesMenu); |
157 | m_promotionActions.push_back(t: subMenuAction); |
158 | return CanPromote; |
159 | } |
160 | |
161 | void PromotionTaskMenu::(unsigned separatorFlags, ActionList &actionList) |
162 | { |
163 | addActions(fw: formWindow(), flags: separatorFlags, actionList); |
164 | } |
165 | |
166 | void PromotionTaskMenu::(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 | |
203 | void PromotionTaskMenu::(QDesignerFormWindowInterface *fw, unsigned flags, QMenu *) |
204 | { |
205 | ActionList actionList; |
206 | addActions(fw, flags, actionList); |
207 | menu->addActions(actions: actionList); |
208 | } |
209 | |
210 | void PromotionTaskMenu::(unsigned flags, QMenu *) |
211 | { |
212 | addActions(fw: formWindow(), flags, menu); |
213 | } |
214 | |
215 | void PromotionTaskMenu::(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 | |
224 | void PromotionTaskMenu::(const QString &customClassName) |
225 | { |
226 | promoteTo(fw: formWindow(), customClassName); |
227 | } |
228 | |
229 | void PromotionTaskMenu::() |
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 | |
241 | void PromotionTaskMenu::() |
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 | |
262 | void PromotionTaskMenu::() |
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 | |
271 | PromotionTaskMenu::PromotionSelectionList PromotionTaskMenu::(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 | |
308 | QDesignerFormWindowInterface *PromotionTaskMenu::() 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 | |
318 | void PromotionTaskMenu::(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 | |
330 | void PromotionTaskMenu::() |
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 | |
339 | QT_END_NAMESPACE |
340 | |