1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 |
3 | |
4 | #include "containerwidget_taskmenu.h" |
5 | |
6 | #include <QtDesigner/abstractformeditor.h> |
7 | #include <QtDesigner/abstractformwindow.h> |
8 | #include <QtDesigner/qextensionmanager.h> |
9 | #include <QtDesigner/container.h> |
10 | |
11 | #include <qdesigner_command_p.h> |
12 | #include <qdesigner_dockwidget_p.h> |
13 | #include <promotiontaskmenu_p.h> |
14 | #include <widgetdatabase_p.h> |
15 | |
16 | #include <QtWidgets/qmainwindow.h> |
17 | #include <QtWidgets/qtoolbox.h> |
18 | #include <QtWidgets/qstackedwidget.h> |
19 | #include <QtWidgets/qtabwidget.h> |
20 | #include <QtWidgets/qscrollarea.h> |
21 | #include <QtWidgets/qmdiarea.h> |
22 | #include <QtWidgets/qwizard.h> |
23 | #include <QtWidgets/qmenu.h> |
24 | |
25 | #include <QtGui/qaction.h> |
26 | |
27 | #include <QtCore/qdebug.h> |
28 | |
29 | QT_BEGIN_NAMESPACE |
30 | |
31 | using namespace Qt::StringLiterals; |
32 | |
33 | namespace qdesigner_internal { |
34 | |
35 | ContainerWidgetTaskMenu::(QWidget *widget, ContainerType type, QObject *parent) : |
36 | QDesignerTaskMenu(widget, parent), |
37 | m_type(type), |
38 | m_containerWidget(widget), |
39 | m_core(formWindow()->core()), |
40 | m_pagePromotionTaskMenu(new PromotionTaskMenu(nullptr, PromotionTaskMenu::ModeSingleWidget, this)), |
41 | m_pageMenuAction(new QAction(this)), |
42 | m_pageMenu(new QMenu), |
43 | m_actionInsertPageAfter(new QAction(this)), |
44 | m_actionInsertPage(nullptr), |
45 | m_actionDeletePage(new QAction(tr(s: "Delete" ), this)) |
46 | { |
47 | Q_ASSERT(m_core); |
48 | m_taskActions.append(t: createSeparator()); |
49 | |
50 | connect(sender: m_actionDeletePage, signal: &QAction::triggered, context: this, slot: &ContainerWidgetTaskMenu::removeCurrentPage); |
51 | |
52 | connect(sender: m_actionInsertPageAfter, signal: &QAction::triggered, context: this, slot: &ContainerWidgetTaskMenu::addPageAfter); |
53 | // Empty Per-Page submenu, deletion and promotion. Updated on demand due to promotion state |
54 | switch (m_type) { |
55 | case WizardContainer: |
56 | case PageContainer: |
57 | m_taskActions.append(t: createSeparator()); // for the browse actions |
58 | break; |
59 | case MdiContainer: |
60 | break; |
61 | } |
62 | // submenu |
63 | m_pageMenuAction->setMenu(m_pageMenu); |
64 | m_taskActions.append(t: m_pageMenuAction); |
65 | // Insertion |
66 | switch (m_type) { |
67 | case WizardContainer: |
68 | case PageContainer: { // Before and after in a submenu |
69 | QAction * = new QAction(tr(s: "Insert" ), this); |
70 | QMenu * = new QMenu; |
71 | // before |
72 | m_actionInsertPage = new QAction(tr(s: "Insert Page Before Current Page" ), this); |
73 | connect(sender: m_actionInsertPage, signal: &QAction::triggered, context: this, slot: &ContainerWidgetTaskMenu::addPage); |
74 | insertMenu->addAction(action: m_actionInsertPage); |
75 | // after |
76 | m_actionInsertPageAfter->setText(tr(s: "Insert Page After Current Page" )); |
77 | insertMenu->addAction(action: m_actionInsertPageAfter); |
78 | |
79 | insertMenuAction->setMenu(insertMenu); |
80 | m_taskActions.append(t: insertMenuAction); |
81 | } |
82 | break; |
83 | case MdiContainer: // No concept of order |
84 | m_actionInsertPageAfter->setText(tr(s: "Add Subwindow" )); |
85 | m_taskActions.append(t: m_actionInsertPageAfter); |
86 | break; |
87 | } |
88 | } |
89 | |
90 | ContainerWidgetTaskMenu::() = default; |
91 | |
92 | QAction *ContainerWidgetTaskMenu::() const |
93 | { |
94 | return nullptr; |
95 | } |
96 | |
97 | bool ContainerWidgetTaskMenu::canDeletePage() const |
98 | { |
99 | switch (pageCount()) { |
100 | case 0: |
101 | return false; |
102 | case 1: |
103 | return m_type != PageContainer; // Do not delete last page of page-type container |
104 | default: |
105 | break; |
106 | } |
107 | return true; |
108 | } |
109 | |
110 | int ContainerWidgetTaskMenu::() const |
111 | { |
112 | if (const QDesignerContainerExtension *ce = containerExtension()) |
113 | return ce->count(); |
114 | return 0; |
115 | } |
116 | |
117 | QString ContainerWidgetTaskMenu::(ContainerType ct, int index, int count) |
118 | { |
119 | if (ct == MdiContainer) |
120 | return tr(s: "Subwindow" ); // No concept of order, same text everywhere |
121 | if (index < 0) |
122 | return tr(s: "Page" ); |
123 | return tr(s: "Page %1 of %2" ).arg(a: index + 1).arg(a: count); |
124 | } |
125 | |
126 | QList<QAction*> ContainerWidgetTaskMenu::() const |
127 | { |
128 | const QDesignerContainerExtension *ce = containerExtension(); |
129 | const int index = ce->currentIndex(); |
130 | |
131 | auto actions = QDesignerTaskMenu::taskActions(); |
132 | actions += m_taskActions; |
133 | // Update the page submenu, deletion and promotion. Updated on demand due to promotion state. |
134 | m_pageMenu->clear(); |
135 | const bool canAddWidget = ce->canAddWidget(); |
136 | if (m_actionInsertPage) |
137 | m_actionInsertPage->setEnabled(canAddWidget); |
138 | m_actionInsertPageAfter->setEnabled(canAddWidget); |
139 | m_pageMenu->addAction(action: m_actionDeletePage); |
140 | m_actionDeletePage->setEnabled(index >= 0 && ce->canRemove(index) && canDeletePage()); |
141 | m_pageMenuAction->setText(pageMenuText(ct: m_type, index, count: ce->count())); |
142 | if (index != -1) { // Has a page |
143 | m_pageMenuAction->setEnabled(true); |
144 | m_pagePromotionTaskMenu->setWidget(ce->widget(index)); |
145 | m_pagePromotionTaskMenu->addActions(flags: PromotionTaskMenu::LeadingSeparator|PromotionTaskMenu::SuppressGlobalEdit, menu: m_pageMenu); |
146 | } else { // No page |
147 | m_pageMenuAction->setEnabled(false); |
148 | } |
149 | |
150 | return actions; |
151 | } |
152 | |
153 | QDesignerFormWindowInterface *ContainerWidgetTaskMenu::() const |
154 | { |
155 | return QDesignerFormWindowInterface::findFormWindow(w: m_containerWidget); |
156 | } |
157 | |
158 | QDesignerContainerExtension *ContainerWidgetTaskMenu::() const |
159 | { |
160 | QExtensionManager *mgr = m_core->extensionManager(); |
161 | return qt_extension<QDesignerContainerExtension*>(manager: mgr, object: m_containerWidget); |
162 | } |
163 | |
164 | void ContainerWidgetTaskMenu::() |
165 | { |
166 | if (QDesignerContainerExtension *c = containerExtension()) { |
167 | if (c->currentIndex() == -1) |
168 | return; |
169 | |
170 | QDesignerFormWindowInterface *fw = formWindow(); |
171 | DeleteContainerWidgetPageCommand *cmd = new DeleteContainerWidgetPageCommand(fw); |
172 | cmd->init(containerWidget: m_containerWidget, ct: m_type); |
173 | fw->commandHistory()->push(cmd); |
174 | } |
175 | } |
176 | |
177 | void ContainerWidgetTaskMenu::() |
178 | { |
179 | if (containerExtension()) { |
180 | QDesignerFormWindowInterface *fw = formWindow(); |
181 | AddContainerWidgetPageCommand *cmd = new AddContainerWidgetPageCommand(fw); |
182 | cmd->init(containerWidget: m_containerWidget, ct: m_type, mode: AddContainerWidgetPageCommand::InsertBefore); |
183 | fw->commandHistory()->push(cmd); |
184 | } |
185 | } |
186 | |
187 | void ContainerWidgetTaskMenu::() |
188 | { |
189 | if (containerExtension()) { |
190 | QDesignerFormWindowInterface *fw = formWindow(); |
191 | AddContainerWidgetPageCommand *cmd = new AddContainerWidgetPageCommand(fw); |
192 | cmd->init(containerWidget: m_containerWidget, ct: m_type, mode: AddContainerWidgetPageCommand::InsertAfter); |
193 | fw->commandHistory()->push(cmd); |
194 | } |
195 | } |
196 | |
197 | // -------------- WizardContainerWidgetTaskMenu |
198 | WizardContainerWidgetTaskMenu::(QWizard *w, QObject *parent) : |
199 | ContainerWidgetTaskMenu(w, WizardContainer, parent), |
200 | m_nextAction(new QAction(tr(s: "Next" ), this)), |
201 | m_previousAction(new QAction(tr(s: "Back" ), this)) |
202 | { |
203 | connect(sender: m_nextAction, signal: &QAction::triggered, context: w, slot: &QWizard::next); |
204 | connect(sender: m_previousAction, signal: &QAction::triggered, context: w, slot: &QWizard::back); |
205 | auto &l = containerActions(); |
206 | l.push_front(t: createSeparator()); |
207 | l.push_front(t: m_nextAction); |
208 | l.push_front(t: m_previousAction); |
209 | l.push_front(t: createSeparator()); |
210 | } |
211 | |
212 | QList<QAction*> WizardContainerWidgetTaskMenu::() const |
213 | { |
214 | // Enable |
215 | const QDesignerContainerExtension *ce = containerExtension(); |
216 | const int index = ce->currentIndex(); |
217 | m_previousAction->setEnabled(index > 0); |
218 | m_nextAction->setEnabled(index >= 0 && index < (ce->count() - 1)); |
219 | return ContainerWidgetTaskMenu::taskActions(); |
220 | } |
221 | |
222 | // -------------- MdiContainerWidgetTaskMenu |
223 | |
224 | MdiContainerWidgetTaskMenu::(QMdiArea *m, QObject *parent) : |
225 | ContainerWidgetTaskMenu(m, MdiContainer, parent) |
226 | { |
227 | initializeActions(); |
228 | connect(sender: m_nextAction, signal: &QAction::triggered, context: m, slot: &QMdiArea::activateNextSubWindow); |
229 | connect(sender: m_previousAction, signal: &QAction::triggered, context: m , slot: &QMdiArea::activatePreviousSubWindow); |
230 | connect(sender: m_tileAction, signal: &QAction::triggered, context: m, slot: &QMdiArea::tileSubWindows); |
231 | connect(sender: m_cascadeAction, signal: &QAction::triggered, context: m, slot: &QMdiArea::cascadeSubWindows); |
232 | } |
233 | |
234 | void MdiContainerWidgetTaskMenu::() |
235 | { |
236 | m_nextAction =new QAction(tr(s: "Next Subwindow" ), this); |
237 | m_previousAction = new QAction(tr(s: "Previous Subwindow" ), this); |
238 | m_tileAction = new QAction(tr(s: "Tile" ), this); |
239 | m_cascadeAction = new QAction(tr(s: "Cascade" ), this); |
240 | |
241 | auto &l = containerActions(); |
242 | l.push_front(t: createSeparator()); |
243 | l.push_front(t: m_tileAction); |
244 | l.push_front(t: m_cascadeAction); |
245 | l.push_front(t: m_previousAction); |
246 | l.push_front(t: m_nextAction); |
247 | l.push_front(t: createSeparator()); |
248 | } |
249 | |
250 | QList<QAction*> MdiContainerWidgetTaskMenu::() const |
251 | { |
252 | const auto rc = ContainerWidgetTaskMenu::taskActions(); |
253 | // Enable |
254 | const int count = pageCount(); |
255 | m_nextAction->setEnabled(count > 1); |
256 | m_previousAction->setEnabled(count > 1); |
257 | m_tileAction->setEnabled(count); |
258 | m_cascadeAction->setEnabled(count); |
259 | return rc; |
260 | } |
261 | |
262 | // -------------- ContainerWidgetTaskMenuFactory |
263 | |
264 | ContainerWidgetTaskMenuFactory::(QDesignerFormEditorInterface *core, QExtensionManager *extensionManager) : |
265 | QExtensionFactory(extensionManager), |
266 | m_core(core) |
267 | { |
268 | } |
269 | |
270 | QObject *ContainerWidgetTaskMenuFactory::(QObject *object, const QString &iid, QObject *parent) const |
271 | { |
272 | if (iid != "QDesignerInternalTaskMenuExtension"_L1 || !object->isWidgetType()) |
273 | return nullptr; |
274 | |
275 | QWidget *widget = qobject_cast<QWidget*>(o: object); |
276 | |
277 | if (qobject_cast<QStackedWidget*>(object: widget) |
278 | || qobject_cast<QToolBox*>(object: widget) |
279 | || qobject_cast<QTabWidget*>(object: widget) |
280 | || qobject_cast<QMainWindow*>(object: widget)) { |
281 | // Are we using Designer's own container extensions and task menus or did |
282 | // someone provide an extra one with an addpage method, for example for a QScrollArea? |
283 | if (const WidgetDataBase *wb = qobject_cast<const WidgetDataBase *>(object: m_core->widgetDataBase())) { |
284 | const int idx = wb->indexOfObject(o: widget); |
285 | const WidgetDataBaseItem *item = static_cast<const WidgetDataBaseItem *>(wb->item(index: idx)); |
286 | if (item->addPageMethod().isEmpty()) |
287 | return nullptr; |
288 | } |
289 | } |
290 | |
291 | if (qt_extension<QDesignerContainerExtension*>(manager: extensionManager(), object) == nullptr) |
292 | return nullptr; |
293 | |
294 | if (QMdiArea* ma = qobject_cast<QMdiArea*>(object: widget)) |
295 | return new MdiContainerWidgetTaskMenu(ma, parent); |
296 | if (QWizard *wz = qobject_cast<QWizard *>(object: widget)) |
297 | return new WizardContainerWidgetTaskMenu(wz, parent); |
298 | return new ContainerWidgetTaskMenu(widget, PageContainer, parent); |
299 | } |
300 | |
301 | } |
302 | QT_END_NAMESPACE |
303 | |