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 "mainwindow.h"
30#include "qdesigner.h"
31#include "qdesigner_actions.h"
32#include "qdesigner_workbench.h"
33#include "qdesigner_formwindow.h"
34#include "qdesigner_toolwindow.h"
35#include "qdesigner_settings.h"
36#include "qttoolbardialog.h"
37
38#include <QtDesigner/abstractformwindow.h>
39
40#include <QtWidgets/qaction.h>
41#include <QtGui/qevent.h>
42#include <QtWidgets/qtoolbar.h>
43#include <QtWidgets/qmdisubwindow.h>
44#include <QtWidgets/qstatusbar.h>
45#include <QtWidgets/qmenu.h>
46#include <QtWidgets/qlayout.h>
47#include <QtWidgets/qdockwidget.h>
48
49#include <QtCore/qurl.h>
50#include <QtCore/qdebug.h>
51#include <QtCore/qmimedata.h>
52
53#include <algorithm>
54
55static const char *uriListMimeFormatC = "text/uri-list";
56
57QT_BEGIN_NAMESPACE
58
59using ActionList = QList<QAction *>;
60
61// Helpers for creating toolbars and menu
62
63static void addActionsToToolBar(const ActionList &actions, QToolBar *t)
64{
65 for (QAction *action : actions) {
66 if (action->property(name: QDesignerActions::defaultToolbarPropertyName).toBool())
67 t->addAction(action);
68 }
69}
70static QToolBar *createToolBar(const QString &title, const QString &objectName, const ActionList &actions)
71{
72 QToolBar *rc = new QToolBar;
73 rc->setObjectName(objectName);
74 rc->setWindowTitle(title);
75 addActionsToToolBar(actions, t: rc);
76 return rc;
77}
78
79// ---------------- MainWindowBase
80
81MainWindowBase::MainWindowBase(QWidget *parent, Qt::WindowFlags flags) :
82 QMainWindow(parent, flags)
83{
84#ifndef Q_OS_MACOS
85 setWindowIcon(qDesigner->windowIcon());
86#endif
87}
88
89void MainWindowBase::closeEvent(QCloseEvent *e)
90{
91 switch (m_policy) {
92 case AcceptCloseEvents:
93 QMainWindow::closeEvent(event: e);
94 break;
95 case EmitCloseEventSignal:
96 emit closeEventReceived(e);
97 break;
98 }
99}
100
101QVector<QToolBar *> MainWindowBase::createToolBars(const QDesignerActions *actions, bool singleToolBar)
102{
103 // Note that whenever you want to add a new tool bar here, you also have to update the default
104 // action groups added to the toolbar manager in the mainwindow constructor
105 QVector<QToolBar *> rc;
106 if (singleToolBar) {
107 //: Not currently used (main tool bar)
108 QToolBar *main = createToolBar(title: tr(s: "Main"), QStringLiteral("mainToolBar"), actions: actions->fileActions()->actions());
109 addActionsToToolBar(actions: actions->editActions()->actions(), t: main);
110 addActionsToToolBar(actions: actions->toolActions()->actions(), t: main);
111 addActionsToToolBar(actions: actions->formActions()->actions(), t: main);
112 rc.push_back(t: main);
113 } else {
114 rc.push_back(t: createToolBar(title: tr(s: "File"), QStringLiteral("fileToolBar"), actions: actions->fileActions()->actions()));
115 rc.push_back(t: createToolBar(title: tr(s: "Edit"), QStringLiteral("editToolBar"), actions: actions->editActions()->actions()));
116 rc.push_back(t: createToolBar(title: tr(s: "Tools"), QStringLiteral("toolsToolBar"), actions: actions->toolActions()->actions()));
117 rc.push_back(t: createToolBar(title: tr(s: "Form"), QStringLiteral("formToolBar"), actions: actions->formActions()->actions()));
118 }
119 return rc;
120}
121
122QString MainWindowBase::mainWindowTitle()
123{
124 return tr(s: "Qt Designer");
125}
126
127// Use the minor Qt version as settings versions to avoid conflicts
128int MainWindowBase::settingsVersion()
129{
130 const int version = QT_VERSION;
131 return (version & 0x00FF00) >> 8;
132}
133
134// ----------------- DockedMdiArea
135
136DockedMdiArea::DockedMdiArea(const QString &extension, QWidget *parent) :
137 QMdiArea(parent),
138 m_extension(extension)
139{
140 setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
141 setLineWidth(1);
142 setAcceptDrops(true);
143 setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
144 setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
145}
146
147QStringList DockedMdiArea::uiFiles(const QMimeData *d) const
148{
149 // Extract dropped UI files from Mime data.
150 QStringList rc;
151 if (!d->hasFormat(mimetype: QLatin1String(uriListMimeFormatC)))
152 return rc;
153 const auto urls = d->urls();
154 if (urls.isEmpty())
155 return rc;
156 for (const auto &url : urls) {
157 const QString fileName = url.toLocalFile();
158 if (!fileName.isEmpty() && fileName.endsWith(s: m_extension))
159 rc.push_back(t: fileName);
160 }
161 return rc;
162}
163
164bool DockedMdiArea::event(QEvent *event)
165{
166 // Listen for desktop file manager drop and emit a signal once a file is
167 // dropped.
168 switch (event->type()) {
169 case QEvent::DragEnter: {
170 QDragEnterEvent *e = static_cast<QDragEnterEvent*>(event);
171 if (!uiFiles(d: e->mimeData()).isEmpty()) {
172 e->acceptProposedAction();
173 return true;
174 }
175 }
176 break;
177 case QEvent::Drop: {
178 QDropEvent *e = static_cast<QDropEvent*>(event);
179 const QStringList files = uiFiles(d: e->mimeData());
180 const QStringList::const_iterator cend = files.constEnd();
181 for (QStringList::const_iterator it = files.constBegin(); it != cend; ++it) {
182 emit fileDropped(*it);
183 }
184 e->acceptProposedAction();
185 return true;
186 }
187 break;
188 default:
189 break;
190 }
191 return QMdiArea::event(event);
192}
193
194// ------------- ToolBarManager:
195
196static void addActionsToToolBarManager(const ActionList &al, const QString &title, QtToolBarManager *tbm)
197{
198 for (QAction *action : al)
199 tbm->addAction(action, category: title);
200}
201
202ToolBarManager::ToolBarManager(QMainWindow *configureableMainWindow,
203 QWidget *parent,
204 QMenu *toolBarMenu,
205 const QDesignerActions *actions,
206 const QVector<QToolBar *> &toolbars,
207 const QVector<QDesignerToolWindow *> &toolWindows) :
208 QObject(parent),
209 m_configureableMainWindow(configureableMainWindow),
210 m_parent(parent),
211 m_toolBarMenu(toolBarMenu),
212 m_manager(new QtToolBarManager(this)),
213 m_configureAction(new QAction(tr(s: "Configure Toolbars..."), this)),
214 m_toolbars(toolbars)
215{
216 m_configureAction->setMenuRole(QAction::NoRole);
217 m_configureAction->setObjectName(QStringLiteral("__qt_configure_tool_bars_action"));
218 connect(sender: m_configureAction, signal: &QAction::triggered, receiver: this, slot: &ToolBarManager::configureToolBars);
219
220 m_manager->setMainWindow(configureableMainWindow);
221
222 for (QToolBar *tb : qAsConst(t&: m_toolbars)) {
223 const QString title = tb->windowTitle();
224 m_manager->addToolBar(toolBar: tb, category: title);
225 addActionsToToolBarManager(al: tb->actions(), title, tbm: m_manager);
226 }
227
228 addActionsToToolBarManager(al: actions->windowActions()->actions(), title: tr(s: "Window"), tbm: m_manager);
229 addActionsToToolBarManager(al: actions->helpActions()->actions(), title: tr(s: "Help"), tbm: m_manager);
230
231 // Filter out the device profile preview actions which have int data().
232 ActionList previewActions = actions->styleActions()->actions();
233 ActionList::iterator it = previewActions.begin();
234 for ( ; (*it)->isSeparator() || (*it)->data().type() == QVariant::Int; ++it) ;
235 previewActions.erase(afirst: previewActions.begin(), alast: it);
236 addActionsToToolBarManager(al: previewActions, title: tr(s: "Style"), tbm: m_manager);
237
238 const QString dockTitle = tr(s: "Dock views");
239 for (QDesignerToolWindow *tw : toolWindows) {
240 if (QAction *action = tw->action())
241 m_manager->addAction(action, category: dockTitle);
242 }
243
244 addActionsToToolBarManager(al: actions->fileActions()->actions(), title: tr(s: "File"), tbm: m_manager);
245 addActionsToToolBarManager(al: actions->editActions()->actions(), title: tr(s: "Edit"), tbm: m_manager);
246 addActionsToToolBarManager(al: actions->toolActions()->actions(), title: tr(s: "Tools"), tbm: m_manager);
247 addActionsToToolBarManager(al: actions->formActions()->actions(), title: tr(s: "Form"), tbm: m_manager);
248
249 m_manager->addAction(action: m_configureAction, category: tr(s: "Toolbars"));
250 updateToolBarMenu();
251}
252
253// sort function for sorting tool bars alphabetically by title [non-static since called from template]
254
255bool toolBarTitleLessThan(const QToolBar *t1, const QToolBar *t2)
256{
257 return t1->windowTitle() < t2->windowTitle();
258}
259
260void ToolBarManager::updateToolBarMenu()
261{
262 // Sort tool bars alphabetically by title
263 std::stable_sort(first: m_toolbars.begin(), last: m_toolbars.end(), comp: toolBarTitleLessThan);
264 // add to menu
265 m_toolBarMenu->clear();
266 for (QToolBar *tb : qAsConst(t&: m_toolbars))
267 m_toolBarMenu->addAction(action: tb->toggleViewAction());
268 m_toolBarMenu->addAction(action: m_configureAction);
269}
270
271void ToolBarManager::configureToolBars()
272{
273 QtToolBarDialog dlg(m_parent);
274 dlg.setWindowFlags(dlg.windowFlags() & ~Qt::WindowContextHelpButtonHint);
275 dlg.setToolBarManager(m_manager);
276 dlg.exec();
277 updateToolBarMenu();
278}
279
280QByteArray ToolBarManager::saveState(int version) const
281{
282 return m_manager->saveState(version);
283}
284
285bool ToolBarManager::restoreState(const QByteArray &state, int version)
286{
287 return m_manager->restoreState(state, version);
288}
289
290// ---------- DockedMainWindow
291
292DockedMainWindow::DockedMainWindow(QDesignerWorkbench *wb,
293 QMenu *toolBarMenu,
294 const QVector<QDesignerToolWindow *> &toolWindows) :
295 m_toolBarManager(nullptr)
296{
297 setObjectName(QStringLiteral("MDIWindow"));
298 setWindowTitle(mainWindowTitle());
299
300 const QVector<QToolBar *> toolbars = createToolBars(actions: wb->actionManager(), singleToolBar: false);
301 for (QToolBar *tb : toolbars)
302 addToolBar(toolbar: tb);
303 DockedMdiArea *dma = new DockedMdiArea(wb->actionManager()->uiExtension());
304 connect(sender: dma, signal: &DockedMdiArea::fileDropped,
305 receiver: this, slot: &DockedMainWindow::fileDropped);
306 connect(sender: dma, signal: &QMdiArea::subWindowActivated,
307 receiver: this, slot: &DockedMainWindow::slotSubWindowActivated);
308 setCentralWidget(dma);
309
310 QStatusBar *sb = statusBar();
311 Q_UNUSED(sb);
312
313 m_toolBarManager = new ToolBarManager(this, this, toolBarMenu, wb->actionManager(), toolbars, toolWindows);
314}
315
316QMdiArea *DockedMainWindow::mdiArea() const
317{
318 return static_cast<QMdiArea *>(centralWidget());
319}
320
321void DockedMainWindow::slotSubWindowActivated(QMdiSubWindow* subWindow)
322{
323 if (subWindow) {
324 QWidget *widget = subWindow->widget();
325 if (QDesignerFormWindow *fw = qobject_cast<QDesignerFormWindow*>(object: widget)) {
326 emit formWindowActivated(fw);
327 mdiArea()->setActiveSubWindow(subWindow);
328 }
329 }
330}
331
332// Create a MDI subwindow for the form.
333QMdiSubWindow *DockedMainWindow::createMdiSubWindow(QWidget *fw, Qt::WindowFlags f, const QKeySequence &designerCloseActionShortCut)
334{
335 QMdiSubWindow *rc = mdiArea()->addSubWindow(widget: fw, flags: f);
336 // Make action shortcuts respond only if focused to avoid conflicts with
337 // designer menu actions
338 if (designerCloseActionShortCut == QKeySequence(QKeySequence::Close)) {
339 const ActionList systemMenuActions = rc->systemMenu()->actions();
340 if (!systemMenuActions.isEmpty()) {
341 const ActionList::const_iterator cend = systemMenuActions.constEnd();
342 for (ActionList::const_iterator it = systemMenuActions.constBegin(); it != cend; ++it) {
343 if ( (*it)->shortcut() == designerCloseActionShortCut) {
344 (*it)->setShortcutContext(Qt::WidgetShortcut);
345 break;
346 }
347 }
348 }
349 }
350 return rc;
351}
352
353DockedMainWindow::DockWidgetList DockedMainWindow::addToolWindows(const DesignerToolWindowList &tls)
354{
355 DockWidgetList rc;
356 for (QDesignerToolWindow *tw : tls) {
357 QDockWidget *dockWidget = new QDockWidget;
358 dockWidget->setObjectName(tw->objectName() + QStringLiteral("_dock"));
359 dockWidget->setWindowTitle(tw->windowTitle());
360 addDockWidget(area: tw->dockWidgetAreaHint(), dockwidget: dockWidget);
361 dockWidget->setWidget(tw);
362 rc.push_back(t: dockWidget);
363 }
364 return rc;
365}
366
367// Settings consist of MainWindow state and tool bar manager state
368void DockedMainWindow::restoreSettings(const QDesignerSettings &s, const DockWidgetList &dws, const QRect &desktopArea)
369{
370 const int version = settingsVersion();
371 m_toolBarManager->restoreState(state: s.toolBarsState(mode: DockedMode), version);
372
373 // If there are no old geometry settings, show the window maximized
374 s.restoreGeometry(w: this, fallBack: QRect(desktopArea.topLeft(), QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)));
375
376 const QByteArray mainWindowState = s.mainWindowState(mode: DockedMode);
377 const bool restored = !mainWindowState.isEmpty() && restoreState(state: mainWindowState, version);
378 if (!restored) {
379 // Default: Tabify less relevant windows bottom/right.
380 tabifyDockWidget(first: dws.at(i: QDesignerToolWindow::SignalSlotEditor),
381 second: dws.at(i: QDesignerToolWindow::ActionEditor));
382 tabifyDockWidget(first: dws.at(i: QDesignerToolWindow::ActionEditor),
383 second: dws.at(i: QDesignerToolWindow::ResourceEditor));
384 }
385}
386
387void DockedMainWindow::saveSettings(QDesignerSettings &s) const
388{
389 const int version = settingsVersion();
390 s.setToolBarsState(mode: DockedMode, mainWindowState: m_toolBarManager->saveState(version));
391 s.saveGeometryFor(w: this);
392 s.setMainWindowState(mode: DockedMode, mainWindowState: saveState(version));
393}
394
395QT_END_NAMESPACE
396

source code of qttools/src/designer/src/designer/mainwindow.cpp