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