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

source code of qttools/src/designer/src/components/taskmenu/containerwidget_taskmenu.cpp