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
53 QT_BEGIN_NAMESPACE
54
55 namespace qdesigner_internal {
56
ContainerWidgetTaskMenu(QWidget * widget,ContainerType type,QObject * parent)57 ContainerWidgetTaskMenu::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("Delete"), this))
68 {
69 Q_ASSERT(m_core);
70 m_taskActions.append(createSeparator());
71
72 connect(m_actionDeletePage, &QAction::triggered, this, &ContainerWidgetTaskMenu::removeCurrentPage);
73
74 connect(m_actionInsertPageAfter, &QAction::triggered, this, &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(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(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("Insert"), this);
92 QMenu *insertMenu = new QMenu;
93 // before
94 m_actionInsertPage = new QAction(tr("Insert Page Before Current Page"), this);
95 connect(m_actionInsertPage, &QAction::triggered, this, &ContainerWidgetTaskMenu::addPage);
96 insertMenu->addAction(m_actionInsertPage);
97 // after
98 m_actionInsertPageAfter->setText(tr("Insert Page After Current Page"));
99 insertMenu->addAction(m_actionInsertPageAfter);
100
101 insertMenuAction->setMenu(insertMenu);
102 m_taskActions.append(insertMenuAction);
103 }
104 break;
105 case MdiContainer: // No concept of order
106 m_actionInsertPageAfter->setText(tr("Add Subwindow"));
107 m_taskActions.append(m_actionInsertPageAfter);
108 break;
109 }
110 }
111
112 ContainerWidgetTaskMenu::~ContainerWidgetTaskMenu() = default;
113
preferredEditAction() const114 QAction *ContainerWidgetTaskMenu::preferredEditAction() const
115 {
116 return nullptr;
117 }
118
canDeletePage() const119 bool 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
pageCount() const132 int ContainerWidgetTaskMenu::pageCount() const
133 {
134 if (const QDesignerContainerExtension *ce = containerExtension())
135 return ce->count();
136 return 0;
137 }
138
pageMenuText(ContainerType ct,int index,int count)139 QString ContainerWidgetTaskMenu::pageMenuText(ContainerType ct, int index, int count)
140 {
141 if (ct == MdiContainer)
142 return tr("Subwindow"); // No concept of order, same text everywhere
143 if (index < 0)
144 return tr("Page");
145 return tr("Page %1 of %2").arg(index + 1).arg(count);
146 }
147
taskActions() const148 QList<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(m_actionDeletePage);
162 m_actionDeletePage->setEnabled(index >= 0 && ce->canRemove(index) && canDeletePage());
163 m_pageMenuAction->setText(pageMenuText(m_type, index, 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(PromotionTaskMenu::LeadingSeparator|PromotionTaskMenu::SuppressGlobalEdit, m_pageMenu);
168 } else { // No page
169 m_pageMenuAction->setEnabled(false);
170 }
171
172 return actions;
173 }
174
formWindow() const175 QDesignerFormWindowInterface *ContainerWidgetTaskMenu::formWindow() const
176 {
177 return QDesignerFormWindowInterface::findFormWindow(m_containerWidget);
178 }
179
containerExtension() const180 QDesignerContainerExtension *ContainerWidgetTaskMenu::containerExtension() const
181 {
182 QExtensionManager *mgr = m_core->extensionManager();
183 return qt_extension<QDesignerContainerExtension*>(mgr, m_containerWidget);
184 }
185
removeCurrentPage()186 void 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(m_containerWidget, m_type);
195 fw->commandHistory()->push(cmd);
196 }
197 }
198
addPage()199 void ContainerWidgetTaskMenu::addPage()
200 {
201 if (containerExtension()) {
202 QDesignerFormWindowInterface *fw = formWindow();
203 AddContainerWidgetPageCommand *cmd = new AddContainerWidgetPageCommand(fw);
204 cmd->init(m_containerWidget, m_type, AddContainerWidgetPageCommand::InsertBefore);
205 fw->commandHistory()->push(cmd);
206 }
207 }
208
addPageAfter()209 void ContainerWidgetTaskMenu::addPageAfter()
210 {
211 if (containerExtension()) {
212 QDesignerFormWindowInterface *fw = formWindow();
213 AddContainerWidgetPageCommand *cmd = new AddContainerWidgetPageCommand(fw);
214 cmd->init(m_containerWidget, m_type, AddContainerWidgetPageCommand::InsertAfter);
215 fw->commandHistory()->push(cmd);
216 }
217 }
218
219 // -------------- WizardContainerWidgetTaskMenu
WizardContainerWidgetTaskMenu(QWizard * w,QObject * parent)220 WizardContainerWidgetTaskMenu::WizardContainerWidgetTaskMenu(QWizard *w, QObject *parent) :
221 ContainerWidgetTaskMenu(w, WizardContainer, parent),
222 m_nextAction(new QAction(tr("Next"), this)),
223 m_previousAction(new QAction(tr("Back"), this))
224 {
225 connect(m_nextAction, &QAction::triggered, w, &QWizard::next);
226 connect(m_previousAction, &QAction::triggered, w, &QWizard::back);
227 auto &l = containerActions();
228 l.push_front(createSeparator());
229 l.push_front(m_nextAction);
230 l.push_front(m_previousAction);
231 l.push_front(createSeparator());
232 }
233
taskActions() const234 QList<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
MdiContainerWidgetTaskMenu(QMdiArea * m,QObject * parent)246 MdiContainerWidgetTaskMenu::MdiContainerWidgetTaskMenu(QMdiArea *m, QObject *parent) :
247 ContainerWidgetTaskMenu(m, MdiContainer, parent)
248 {
249 initializeActions();
250 connect(m_nextAction, &QAction::triggered, m, &QMdiArea::activateNextSubWindow);
251 connect(m_previousAction, &QAction::triggered, m , &QMdiArea::activatePreviousSubWindow);
252 connect(m_tileAction, &QAction::triggered, m, &QMdiArea::tileSubWindows);
253 connect(m_cascadeAction, &QAction::triggered, m, &QMdiArea::cascadeSubWindows);
254 }
255
initializeActions()256 void MdiContainerWidgetTaskMenu::initializeActions()
257 {
258 m_nextAction =new QAction(tr("Next Subwindow"), this);
259 m_previousAction = new QAction(tr("Previous Subwindow"), this);
260 m_tileAction = new QAction(tr("Tile"), this);
261 m_cascadeAction = new QAction(tr("Cascade"), this);
262
263 auto &l = containerActions();
264 l.push_front(createSeparator());
265 l.push_front(m_tileAction);
266 l.push_front(m_cascadeAction);
267 l.push_front(m_previousAction);
268 l.push_front(m_nextAction);
269 l.push_front(createSeparator());
270 }
271
taskActions() const272 QList<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
ContainerWidgetTaskMenuFactory(QDesignerFormEditorInterface * core,QExtensionManager * extensionManager)286 ContainerWidgetTaskMenuFactory::ContainerWidgetTaskMenuFactory(QDesignerFormEditorInterface *core, QExtensionManager *extensionManager) :
287 QExtensionFactory(extensionManager),
288 m_core(core)
289 {
290 }
291
createExtension(QObject * object,const QString & iid,QObject * parent) const292 QObject *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*>(object);
298
299 if (qobject_cast<QStackedWidget*>(widget)
300 || qobject_cast<QToolBox*>(widget)
301 || qobject_cast<QTabWidget*>(widget)
302 || qobject_cast<QMainWindow*>(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 *>(m_core->widgetDataBase())) {
306 const int idx = wb->indexOfObject(widget);
307 const WidgetDataBaseItem *item = static_cast<const WidgetDataBaseItem *>(wb->item(idx));
308 if (item->addPageMethod().isEmpty())
309 return nullptr;
310 }
311 }
312
313 if (qt_extension<QDesignerContainerExtension*>(extensionManager(), object) == nullptr)
314 return nullptr;
315
316 if (QMdiArea* ma = qobject_cast<QMdiArea*>(widget))
317 return new MdiContainerWidgetTaskMenu(ma, parent);
318 if (QWizard *wz = qobject_cast<QWizard *>(widget))
319 return new WizardContainerWidgetTaskMenu(wz, parent);
320 return new ContainerWidgetTaskMenu(widget, PageContainer, parent);
321 }
322
323 }
324 QT_END_NAMESPACE
325