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 "qdesigner_workbench.h"
30#include "qdesigner.h"
31#include "qdesigner_actions.h"
32#include "qdesigner_appearanceoptions.h"
33#include "qdesigner_settings.h"
34#include "qdesigner_toolwindow.h"
35#include "qdesigner_formwindow.h"
36#include "appfontdialog.h"
37
38#include <QtDesigner/abstractformeditor.h>
39#include <QtDesigner/abstractformwindow.h>
40#include <QtDesigner/abstractformwindowmanager.h>
41#include <QtDesigner/abstractformeditorplugin.h>
42#include <QtDesigner/abstractwidgetbox.h>
43#include <QtDesigner/abstractmetadatabase.h>
44
45#include <QtDesigner/QDesignerComponents>
46#include <QtDesigner/abstractintegration.h>
47#include <QtDesigner/private/pluginmanager_p.h>
48#include <QtDesigner/private/formwindowbase_p.h>
49#include <QtDesigner/private/actioneditor_p.h>
50
51#include <QtCore/qdir.h>
52#include <QtCore/qfile.h>
53#include <QtCore/qurl.h>
54#include <QtCore/qtimer.h>
55#include <QtCore/qpluginloader.h>
56#include <QtCore/qdebug.h>
57
58#include <QtWidgets/qactiongroup.h>
59#include <QtGui/qevent.h>
60#include <QtGui/qscreen.h>
61#include <QtWidgets/qdockwidget.h>
62#include <QtWidgets/qmenu.h>
63#include <QtWidgets/qmenubar.h>
64#include <QtWidgets/qmessagebox.h>
65#include <QtWidgets/qpushbutton.h>
66#include <QtWidgets/qtoolbar.h>
67#include <QtWidgets/qmdiarea.h>
68#include <QtWidgets/qmdisubwindow.h>
69#include <QtWidgets/qlayout.h>
70
71QT_BEGIN_NAMESPACE
72
73static const char *appFontPrefixC = "AppFonts";
74
75using ActionList = QList<QAction *>;
76
77static QMdiSubWindow *mdiSubWindowOf(const QWidget *w)
78{
79 QMdiSubWindow *rc = qobject_cast<QMdiSubWindow *>(object: w->parentWidget());
80 Q_ASSERT(rc);
81 return rc;
82}
83
84static QDockWidget *dockWidgetOf(const QWidget *w)
85{
86 for (QWidget *parentWidget = w->parentWidget(); parentWidget ; parentWidget = parentWidget->parentWidget()) {
87 if (QDockWidget *dw = qobject_cast<QDockWidget *>(object: parentWidget)) {
88 return dw;
89 }
90 }
91 Q_ASSERT("Dock widget not found");
92 return nullptr;
93}
94
95// ------------ QDesignerWorkbench::Position
96QDesignerWorkbench::Position::Position(const QMdiSubWindow *mdiSubWindow, const QPoint &mdiAreaOffset) :
97 m_minimized(mdiSubWindow->isShaded()),
98 m_position(mdiSubWindow->pos() + mdiAreaOffset)
99{
100}
101
102QDesignerWorkbench::Position::Position(const QDockWidget *dockWidget) :
103 m_minimized(dockWidget->isMinimized()),
104 m_position(dockWidget->pos())
105{
106}
107
108QDesignerWorkbench::Position::Position(const QWidget *topLevelWindow, const QPoint &desktopTopLeft)
109{
110 const QWidget *window =topLevelWindow->window ();
111 Q_ASSERT(window);
112 m_minimized = window->isMinimized();
113 m_position = window->pos() - desktopTopLeft;
114}
115
116void QDesignerWorkbench::Position::applyTo(QMdiSubWindow *mdiSubWindow,
117 const QPoint &mdiAreaOffset) const
118{
119 // QMdiSubWindow attempts to resize its children to sizeHint() when switching user interface modes.
120 // Restore old size
121 const QPoint mdiAreaPos = QPoint(qMax(a: 0, b: m_position.x() - mdiAreaOffset.x()),
122 qMax(a: 0, b: m_position.y() - mdiAreaOffset.y()));
123 mdiSubWindow->move(mdiAreaPos);
124 const QSize decorationSize = mdiSubWindow->size() - mdiSubWindow->contentsRect().size();
125 mdiSubWindow->resize(mdiSubWindow->widget()->size() + decorationSize);
126 mdiSubWindow->show();
127 if (m_minimized) {
128 mdiSubWindow->showShaded();
129 }
130}
131
132void QDesignerWorkbench::Position::applyTo(QWidget *topLevelWindow, const QPoint &desktopTopLeft) const
133{
134 QWidget *window = topLevelWindow->window ();
135 const QPoint newPos = m_position + desktopTopLeft;
136 window->move(newPos);
137 if ( m_minimized) {
138 topLevelWindow->showMinimized();
139 } else {
140 topLevelWindow->show();
141 }
142}
143
144void QDesignerWorkbench::Position::applyTo(QDockWidget *dockWidget) const
145{
146 dockWidget->widget()->setVisible(true);
147 dockWidget->setVisible(!m_minimized);
148}
149
150static inline void addActionsToMenu(QMenu *m, const ActionList &al)
151{
152 const ActionList::const_iterator cend = al.constEnd();
153 for (ActionList::const_iterator it = al.constBegin(); it != cend; ++it)
154 m->addAction(action: *it);
155}
156
157static inline QMenu *addMenu(QMenuBar *mb, const QString &title, const ActionList &al)
158{
159 QMenu *rc = mb->addMenu(title);
160 addActionsToMenu(m: rc, al);
161 return rc;
162}
163
164// -------- QDesignerWorkbench
165
166QDesignerWorkbench::QDesignerWorkbench() :
167 m_core(QDesignerComponents::createFormEditor(parent: this)),
168 m_windowActions(new QActionGroup(this)),
169 m_globalMenuBar(new QMenuBar)
170{
171 QDesignerSettings settings(m_core);
172
173 (void) QDesignerComponents::createTaskMenu(core: core(), parent: this);
174
175 initializeCorePlugins();
176 QDesignerComponents::initializePlugins(core: core());
177 m_actionManager = new QDesignerActions(this); // accesses plugin components
178
179 m_windowActions->setExclusive(true);
180 connect(sender: m_windowActions, signal: &QActionGroup::triggered,
181 receiver: this, slot: &QDesignerWorkbench::formWindowActionTriggered);
182
183 // Build main menu bar
184 addMenu(mb: m_globalMenuBar, title: tr(s: "&File"), al: m_actionManager->fileActions()->actions());
185
186 QMenu *editMenu = addMenu(mb: m_globalMenuBar, title: tr(s: "&Edit"), al: m_actionManager->editActions()->actions());
187 editMenu->addSeparator();
188 addActionsToMenu(m: editMenu, al: m_actionManager->toolActions()->actions());
189
190 QMenu *formMenu = addMenu(mb: m_globalMenuBar, title: tr(s: "F&orm"), al: m_actionManager->formActions()->actions());
191 QMenu *previewSubMenu = new QMenu(tr(s: "Preview in"), formMenu);
192 formMenu->insertMenu(before: m_actionManager->previewFormAction(), menu: previewSubMenu);
193 addActionsToMenu(m: previewSubMenu, al: m_actionManager->styleActions()->actions());
194
195 QMenu *viewMenu = m_globalMenuBar->addMenu(title: tr(s: "&View"));
196
197 addMenu(mb: m_globalMenuBar, title: tr(s: "&Settings"), al: m_actionManager->settingsActions()->actions());
198
199 m_windowMenu = addMenu(mb: m_globalMenuBar, title: tr(s: "&Window"), al: m_actionManager->windowActions()->actions());
200
201 addMenu(mb: m_globalMenuBar, title: tr(s: "&Help"), al: m_actionManager->helpActions()->actions());
202
203 // Add the tools in view menu order
204 QActionGroup *viewActions = new QActionGroup(this);
205 viewActions->setExclusive(false);
206
207 for (int i = 0; i < QDesignerToolWindow::StandardToolWindowCount; i++) {
208 QDesignerToolWindow *toolWindow = QDesignerToolWindow::createStandardToolWindow(which: static_cast< QDesignerToolWindow::StandardToolWindow>(i), workbench: this);
209 m_toolWindows.push_back(t: toolWindow);
210 if (QAction *action = toolWindow->action()) {
211 viewMenu->addAction(action);
212 viewActions->addAction(a: action);
213 }
214 // The widget box becomes the main window in top level mode
215 if (i == QDesignerToolWindow::WidgetBox) {
216 connect(sender: toolWindow, signal: &QDesignerToolWindow::closeEventReceived,
217 receiver: this, slot: &QDesignerWorkbench::handleCloseEvent);
218 }
219 }
220 // Integration
221 m_integration = new QDesignerIntegration(m_core, this);
222 connect(sender: m_integration, signal: &QDesignerIntegration::helpRequested,
223 receiver: m_actionManager, slot: &QDesignerActions::helpRequested);
224
225 // remaining view options (config toolbars)
226 viewMenu->addSeparator();
227 m_toolbarMenu = viewMenu->addMenu(title: tr(s: "Toolbars"));
228
229 emit initialized();
230
231 connect(sender: m_core->formWindowManager(), signal: &QDesignerFormWindowManagerInterface::activeFormWindowChanged,
232 receiver: this, slot: &QDesignerWorkbench::updateWindowMenu);
233
234
235 { // Add application specific options pages
236 QDesignerAppearanceOptionsPage *appearanceOptions = new QDesignerAppearanceOptionsPage(m_core);
237 connect(sender: appearanceOptions, signal: &QDesignerAppearanceOptionsPage::settingsChanged, receiver: this, slot: &QDesignerWorkbench::notifyUISettingsChanged);
238 auto optionsPages = m_core->optionsPages();
239 optionsPages.push_front(t: appearanceOptions);
240 m_core->setOptionsPages(optionsPages);
241 }
242
243 restoreUISettings();
244 AppFontWidget::restore(s: m_core->settingsManager(), prefix: QLatin1String(appFontPrefixC));
245 m_state = StateUp;
246}
247
248QDesignerWorkbench::~QDesignerWorkbench()
249{
250 switch (m_mode) {
251 case NeutralMode:
252 case DockedMode:
253 qDeleteAll(c: m_toolWindows);
254 break;
255 case TopLevelMode: // Everything parented here
256 delete widgetBoxToolWindow();
257 break;
258 }
259}
260
261void QDesignerWorkbench::saveGeometriesForModeChange()
262{
263 m_Positions.clear();
264 switch (m_mode) {
265 case NeutralMode:
266 break;
267 case TopLevelMode: {
268 const QPoint desktopOffset = QGuiApplication::primaryScreen()->availableGeometry().topLeft();
269 for (QDesignerToolWindow *tw : qAsConst(t&: m_toolWindows))
270 m_Positions.insert(akey: tw, avalue: Position(tw, desktopOffset));
271 for (QDesignerFormWindow *fw : qAsConst(t&: m_formWindows))
272 m_Positions.insert(akey: fw, avalue: Position(fw, desktopOffset));
273 }
274 break;
275 case DockedMode: {
276 const QPoint mdiAreaOffset = m_dockedMainWindow->mdiArea()->pos();
277 for (QDesignerToolWindow *tw : qAsConst(t&: m_toolWindows))
278 m_Positions.insert(akey: tw, avalue: Position(dockWidgetOf(w: tw)));
279 for (QDesignerFormWindow *fw : qAsConst(t&: m_formWindows))
280 m_Positions.insert(akey: fw, avalue: Position(mdiSubWindowOf(w: fw), mdiAreaOffset));
281 }
282 break;
283 }
284}
285
286UIMode QDesignerWorkbench::mode() const
287{
288 return m_mode;
289}
290
291void QDesignerWorkbench::addFormWindow(QDesignerFormWindow *formWindow)
292{
293 // ### Q_ASSERT(formWindow->windowTitle().isEmpty() == false);
294
295 m_formWindows.append(t: formWindow);
296
297
298 m_actionManager->setWindowListSeparatorVisible(true);
299
300 if (QAction *action = formWindow->action()) {
301 m_windowActions->addAction(a: action);
302 m_windowMenu->addAction(action);
303 action->setChecked(true);
304 }
305
306 m_actionManager->minimizeAction()->setEnabled(true);
307 m_actionManager->minimizeAction()->setChecked(false);
308 connect(sender: formWindow, signal: &QDesignerFormWindow::minimizationStateChanged,
309 receiver: this, slot: &QDesignerWorkbench::minimizationStateChanged);
310
311 m_actionManager->editWidgets()->trigger();
312}
313
314Qt::WindowFlags QDesignerWorkbench::magicalWindowFlags(const QWidget *widgetForFlags) const
315{
316 switch (m_mode) {
317 case TopLevelMode: {
318#ifdef Q_OS_MACOS
319 if (qobject_cast<const QDesignerToolWindow *>(widgetForFlags))
320 return Qt::Tool;
321#else
322 Q_UNUSED(widgetForFlags);
323#endif
324 return Qt::Window;
325 }
326 case DockedMode:
327 return Qt::Window | Qt::WindowShadeButtonHint | Qt::WindowSystemMenuHint | Qt::WindowTitleHint;
328 case NeutralMode:
329 return Qt::Window;
330 default:
331 Q_ASSERT(0);
332 return {};
333 }
334}
335
336QWidget *QDesignerWorkbench::magicalParent(const QWidget *w) const
337{
338 switch (m_mode) {
339 case TopLevelMode: {
340 // Use widget box as parent for all windows except self. This will
341 // result in having just one entry in the MS Windows task bar.
342 QWidget *widgetBoxWrapper = widgetBoxToolWindow();
343 return w == widgetBoxWrapper ? nullptr : widgetBoxWrapper;
344 }
345 case DockedMode:
346 return m_dockedMainWindow->mdiArea();
347 case NeutralMode:
348 return nullptr;
349 default:
350 Q_ASSERT(0);
351 return 0;
352 }
353}
354
355void QDesignerWorkbench::switchToNeutralMode()
356{
357 QDesignerSettings settings(m_core);
358 saveGeometries(settings);
359 saveGeometriesForModeChange();
360
361 if (m_mode == TopLevelMode) {
362 delete m_topLevelData.toolbarManager;
363 m_topLevelData.toolbarManager = nullptr;
364 qDeleteAll(c: m_topLevelData.toolbars);
365 m_topLevelData.toolbars.clear();
366 }
367
368 m_mode = NeutralMode;
369
370 for (QDesignerToolWindow *tw : qAsConst(t&: m_toolWindows)) {
371 tw->setCloseEventPolicy(MainWindowBase::AcceptCloseEvents);
372 tw->setParent(nullptr);
373 }
374
375 for (QDesignerFormWindow *fw : qAsConst(t&: m_formWindows)) {
376 fw->setParent(nullptr);
377 fw->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
378 }
379
380#ifndef Q_OS_MACOS
381 m_globalMenuBar->setParent(nullptr);
382#endif
383
384 m_core->setTopLevel(nullptr);
385 qDesigner->setMainWindow(nullptr);
386
387 delete m_dockedMainWindow;
388 m_dockedMainWindow = nullptr;
389}
390
391void QDesignerWorkbench::switchToDockedMode()
392{
393 if (m_mode == DockedMode)
394 return;
395
396 switchToNeutralMode();
397
398#if !defined(Q_OS_MACOS)
399# if defined(Q_OS_UNIX)
400 QApplication::setAttribute(attribute: Qt::AA_DontUseNativeMenuBar, on: false);
401# endif // Q_OS_UNIX
402 QDesignerToolWindow *widgetBoxWrapper = widgetBoxToolWindow();
403 widgetBoxWrapper->action()->setVisible(true);
404 widgetBoxWrapper->setWindowTitle(tr(s: "Widget Box"));
405#endif // !Q_OS_MACOS
406
407 m_mode = DockedMode;
408 const QDesignerSettings settings(m_core);
409 m_dockedMainWindow = new DockedMainWindow(this, m_toolbarMenu, m_toolWindows);
410 m_dockedMainWindow->setUnifiedTitleAndToolBarOnMac(true);
411 m_dockedMainWindow->setCloseEventPolicy(MainWindowBase::EmitCloseEventSignal);
412 connect(sender: m_dockedMainWindow, signal: &DockedMainWindow::closeEventReceived,
413 receiver: this, slot: &QDesignerWorkbench::handleCloseEvent);
414 connect(sender: m_dockedMainWindow, signal: &DockedMainWindow::fileDropped,
415 receiver: this, slot: &QDesignerWorkbench::slotFileDropped);
416 connect(sender: m_dockedMainWindow, signal: &DockedMainWindow::formWindowActivated,
417 receiver: this, slot: &QDesignerWorkbench::slotFormWindowActivated);
418 m_dockedMainWindow->restoreSettings(s: settings, dws: m_dockedMainWindow->addToolWindows(toolWindows: m_toolWindows), desktopArea: desktopGeometry());
419
420 m_core->setTopLevel(m_dockedMainWindow);
421
422#ifndef Q_OS_MACOS
423 m_dockedMainWindow->setMenuBar(m_globalMenuBar);
424 m_globalMenuBar->show();
425#endif
426 qDesigner->setMainWindow(m_dockedMainWindow);
427
428 for (QDesignerFormWindow *fw : qAsConst(t&: m_formWindows)) {
429 QMdiSubWindow *subwin = m_dockedMainWindow->createMdiSubWindow(fw, f: magicalWindowFlags(widgetForFlags: fw),
430 designerCloseActionShortCut: m_actionManager->closeFormAction()->shortcut());
431 subwin->hide();
432 if (QWidget *mainContainer = fw->editor()->mainContainer())
433 resizeForm(fw, mainContainer);
434 }
435
436 m_actionManager->setBringAllToFrontVisible(false);
437 m_dockedMainWindow->show();
438 // Trigger adjustMDIFormPositions() delayed as viewport size is not yet known.
439
440 if (m_state != StateInitializing)
441 QMetaObject::invokeMethod(obj: this, member: "adjustMDIFormPositions", type: Qt::QueuedConnection);
442}
443
444void QDesignerWorkbench::adjustMDIFormPositions()
445{
446 const QPoint mdiAreaOffset = m_dockedMainWindow->mdiArea()->pos();
447
448 for (QDesignerFormWindow *fw : qAsConst(t&: m_formWindows)) {
449 const PositionMap::const_iterator pit = m_Positions.constFind(akey: fw);
450 if (pit != m_Positions.constEnd())
451 pit->applyTo(mdiSubWindow: mdiSubWindowOf(w: fw), mdiAreaOffset);
452 }
453}
454
455void QDesignerWorkbench::switchToTopLevelMode()
456{
457 if (m_mode == TopLevelMode)
458 return;
459
460 // make sure that the widgetbox is visible if it is different from neutral.
461 QDesignerToolWindow *widgetBoxWrapper = widgetBoxToolWindow();
462 Q_ASSERT(widgetBoxWrapper);
463
464 switchToNeutralMode();
465 const QPoint desktopOffset = desktopGeometry().topLeft();
466 m_mode = TopLevelMode;
467
468 // The widget box is special, it gets the menubar and gets to be the main widget.
469
470 m_core->setTopLevel(widgetBoxWrapper);
471#if !defined(Q_OS_MACOS)
472# if defined(Q_OS_UNIX)
473 // For now the appmenu protocol does not make it possible to associate a
474 // menubar with all application windows. This means in top level mode you
475 // can only reach the menubar when the widgetbox window is active. Since
476 // this is quite inconvenient, better not use the native menubar in this
477 // configuration and keep the menubar in the widgetbox window.
478 QApplication::setAttribute(attribute: Qt::AA_DontUseNativeMenuBar, on: true);
479# endif // Q_OS_UNIX
480 widgetBoxWrapper->setMenuBar(m_globalMenuBar);
481 widgetBoxWrapper->action()->setVisible(false);
482 widgetBoxWrapper->setCloseEventPolicy(MainWindowBase::EmitCloseEventSignal);
483 qDesigner->setMainWindow(widgetBoxWrapper);
484 widgetBoxWrapper->setWindowTitle(MainWindowBase::mainWindowTitle());
485#endif // !Q_OS_MACOS
486
487 const QDesignerSettings settings(m_core);
488 m_topLevelData.toolbars = MainWindowBase::createToolBars(actions: m_actionManager, singleToolBar: false);
489 m_topLevelData.toolbarManager = new ToolBarManager(widgetBoxWrapper, widgetBoxWrapper,
490 m_toolbarMenu, m_actionManager,
491 m_topLevelData.toolbars, m_toolWindows);
492 const int toolBarCount = m_topLevelData.toolbars.size();
493 for (int i = 0; i < toolBarCount; i++) {
494 widgetBoxWrapper->addToolBar(toolbar: m_topLevelData.toolbars.at(i));
495 if (i == 3)
496 widgetBoxWrapper->insertToolBarBreak(before: m_topLevelData.toolbars.at(i));
497 }
498 m_topLevelData.toolbarManager->restoreState(state: settings.toolBarsState(mode: m_mode), version: MainWindowBase::settingsVersion());
499 widgetBoxWrapper->restoreState(state: settings.mainWindowState(mode: m_mode), version: MainWindowBase::settingsVersion());
500
501 bool found_visible_window = false;
502 for (QDesignerToolWindow *tw : qAsConst(t&: m_toolWindows)) {
503 tw->setParent(parent: magicalParent(w: tw), f: magicalWindowFlags(widgetForFlags: tw));
504 settings.restoreGeometry(w: tw, fallBack: tw->geometryHint());
505 tw->action()->setChecked(tw->isVisible());
506 found_visible_window |= tw->isVisible();
507 }
508
509 if (!m_toolWindows.isEmpty() && !found_visible_window)
510 m_toolWindows.first()->show();
511
512 m_actionManager->setBringAllToFrontVisible(true);
513
514 for (QDesignerFormWindow *fw : qAsConst(t&: m_formWindows)) {
515 fw->setParent(parent: magicalParent(w: fw), f: magicalWindowFlags(widgetForFlags: fw));
516 fw->setAttribute(Qt::WA_DeleteOnClose, on: true);
517 const PositionMap::const_iterator pit = m_Positions.constFind(akey: fw);
518 if (pit != m_Positions.constEnd()) pit->applyTo(topLevelWindow: fw, desktopTopLeft: desktopOffset);
519 // Force an activate in order to refresh minimumSize, otherwise it will not be respected
520 if (QLayout *layout = fw->layout())
521 layout->invalidate();
522 if (QWidget *mainContainer = fw->editor()->mainContainer())
523 resizeForm(fw, mainContainer);
524 }
525}
526
527QDesignerFormWindowManagerInterface *QDesignerWorkbench::formWindowManager() const
528{
529 return m_core->formWindowManager();
530}
531
532QDesignerFormEditorInterface *QDesignerWorkbench::core() const
533{
534 return m_core;
535}
536
537int QDesignerWorkbench::toolWindowCount() const
538{
539 return m_toolWindows.count();
540}
541
542QDesignerToolWindow *QDesignerWorkbench::toolWindow(int index) const
543{
544 return m_toolWindows.at(i: index);
545}
546
547int QDesignerWorkbench::formWindowCount() const
548{
549 return m_formWindows.count();
550}
551
552QDesignerFormWindow *QDesignerWorkbench::formWindow(int index) const
553{
554 return m_formWindows.at(i: index);
555}
556
557QRect QDesignerWorkbench::desktopGeometry() const
558{
559 // Return geometry of the desktop designer is running in.
560 QWidget *widget = nullptr;
561 switch (m_mode) {
562 case DockedMode:
563 widget = m_dockedMainWindow;
564 break;
565 case TopLevelMode:
566 widget = widgetBoxToolWindow();
567 break;
568 case NeutralMode:
569 break;
570 }
571 const auto screen = widget ? widget->screen() : QGuiApplication::primaryScreen();
572 return screen ? screen->availableGeometry()
573 : QGuiApplication::primaryScreen()->availableGeometry();
574}
575
576QRect QDesignerWorkbench::availableGeometry() const
577{
578 if (m_mode == DockedMode)
579 return m_dockedMainWindow->mdiArea()->geometry();
580
581 const auto screen = widgetBoxToolWindow()->screen();
582 return screen ? screen->availableGeometry() : QGuiApplication::primaryScreen()->availableGeometry() ;
583}
584
585int QDesignerWorkbench::marginHint() const
586{ return 20;
587}
588
589void QDesignerWorkbench::slotFormWindowActivated(QDesignerFormWindow* fw)
590{
591 core()->formWindowManager()->setActiveFormWindow(fw->editor());
592}
593
594void QDesignerWorkbench::removeFormWindow(QDesignerFormWindow *formWindow)
595{
596 QDesignerFormWindowInterface *editor = formWindow->editor();
597 const bool loadOk = editor->mainContainer();
598 updateBackup(fwi: editor);
599 const int index = m_formWindows.indexOf(t: formWindow);
600 if (index != -1) {
601 m_formWindows.removeAt(i: index);
602 }
603
604 if (QAction *action = formWindow->action()) {
605 m_windowActions->removeAction(a: action);
606 m_windowMenu->removeAction(action);
607 }
608
609 if (m_formWindows.isEmpty()) {
610 m_actionManager->setWindowListSeparatorVisible(false);
611 // Show up new form dialog unless closing
612 if (loadOk && m_state == StateUp
613 && QDesignerSettings(m_core).showNewFormOnStartup()) {
614 QTimer::singleShot(interval: 200, receiver: m_actionManager, slot: &QDesignerActions::createForm);
615 }
616 }
617}
618
619void QDesignerWorkbench::initializeCorePlugins()
620{
621 QObjectList plugins = QPluginLoader::staticInstances();
622 plugins += core()->pluginManager()->instances();
623
624 for (QObject *plugin : qAsConst(t&: plugins)) {
625 if (QDesignerFormEditorPluginInterface *formEditorPlugin = qobject_cast<QDesignerFormEditorPluginInterface*>(object: plugin)) {
626 if (!formEditorPlugin->isInitialized())
627 formEditorPlugin->initialize(core: core());
628 }
629 }
630}
631
632void QDesignerWorkbench::saveSettings() const
633{
634 QDesignerSettings settings(m_core);
635 settings.clearBackup();
636 saveGeometries(settings);
637 AppFontWidget::save(s: m_core->settingsManager(), prefix: QLatin1String(appFontPrefixC));
638}
639
640void QDesignerWorkbench::saveGeometries(QDesignerSettings &settings) const
641{
642 switch (m_mode) {
643 case DockedMode:
644 m_dockedMainWindow->saveSettings(settings);
645 break;
646 case TopLevelMode:
647 settings.setToolBarsState(mode: m_mode, mainWindowState: m_topLevelData.toolbarManager->saveState(version: MainWindowBase::settingsVersion()));
648 settings.setMainWindowState(mode: m_mode, mainWindowState: widgetBoxToolWindow()->saveState(version: MainWindowBase::settingsVersion()));
649 for (const QDesignerToolWindow *tw : m_toolWindows)
650 settings.saveGeometryFor(w: tw);
651 break;
652 case NeutralMode:
653 break;
654 }
655}
656
657void QDesignerWorkbench::slotFileDropped(const QString &f)
658{
659 readInForm(fileName: f);
660}
661
662bool QDesignerWorkbench::readInForm(const QString &fileName) const
663{
664 return m_actionManager->readInForm(fileName);
665}
666
667bool QDesignerWorkbench::writeOutForm(QDesignerFormWindowInterface *formWindow, const QString &fileName) const
668{
669 return m_actionManager->writeOutForm(formWindow, fileName);
670}
671
672bool QDesignerWorkbench::saveForm(QDesignerFormWindowInterface *frm)
673{
674 return m_actionManager->saveForm(fw: frm);
675}
676
677QDesignerFormWindow *QDesignerWorkbench::findFormWindow(QWidget *widget) const
678{
679 for (QDesignerFormWindow *formWindow : m_formWindows) {
680 if (formWindow->editor() == widget)
681 return formWindow;
682 }
683
684 return nullptr;
685}
686
687bool QDesignerWorkbench::handleClose()
688{
689 m_state = StateClosing;
690 QVector<QDesignerFormWindow *> dirtyForms;
691 for (QDesignerFormWindow *w : qAsConst(t&: m_formWindows)) {
692 if (w->editor()->isDirty())
693 dirtyForms << w;
694 }
695
696 const int count = dirtyForms.size();
697 if (count == 1) {
698 if (!dirtyForms.at(i: 0)->close()) {
699 m_state = StateUp;
700 return false;
701 }
702 } else if (count > 1) {
703 QMessageBox box(QMessageBox::Warning, tr(s: "Save Forms?"),
704 tr(s: "There are %n forms with unsaved changes."
705 " Do you want to review these changes before quitting?", c: "", n: count),
706 QMessageBox::Cancel | QMessageBox::Discard | QMessageBox::Save);
707 box.setInformativeText(tr(s: "If you do not review your documents, all your changes will be lost."));
708 box.button(which: QMessageBox::Discard)->setText(tr(s: "Discard Changes"));
709 QPushButton *save = static_cast<QPushButton *>(box.button(which: QMessageBox::Save));
710 save->setText(tr(s: "Review Changes"));
711 box.setDefaultButton(save);
712 switch (box.exec()) {
713 case QMessageBox::Cancel:
714 m_state = StateUp;
715 return false;
716 case QMessageBox::Save:
717 for (QDesignerFormWindow *fw : qAsConst(t&: dirtyForms)) {
718 fw->show();
719 fw->raise();
720 if (!fw->close()) {
721 m_state = StateUp;
722 return false;
723 }
724 }
725 break;
726 case QMessageBox::Discard:
727 for (QDesignerFormWindow *fw : qAsConst(t&: dirtyForms)) {
728 fw->editor()->setDirty(false);
729 fw->setWindowModified(false);
730 }
731 break;
732 }
733 }
734
735 for (QDesignerFormWindow *fw : qAsConst(t&: m_formWindows))
736 fw->close();
737
738 saveSettings();
739 return true;
740}
741
742QDesignerActions *QDesignerWorkbench::actionManager() const
743{
744 return m_actionManager;
745}
746
747void QDesignerWorkbench::updateWindowMenu(QDesignerFormWindowInterface *fwi)
748{
749 bool minimizeChecked = false;
750 bool minimizeEnabled = false;
751 QDesignerFormWindow *activeFormWindow = nullptr;
752 do {
753 if (!fwi)
754 break;
755 activeFormWindow = qobject_cast<QDesignerFormWindow *>(object: fwi->parentWidget());
756 if (!activeFormWindow)
757 break;
758
759 minimizeEnabled = true;
760 minimizeChecked = isFormWindowMinimized(fw: activeFormWindow);
761 } while (false) ;
762
763 m_actionManager->minimizeAction()->setEnabled(minimizeEnabled);
764 m_actionManager->minimizeAction()->setChecked(minimizeChecked);
765
766 for (QDesignerFormWindow *fw : qAsConst(t&: m_formWindows))
767 fw->action()->setChecked(fw == activeFormWindow);
768}
769
770void QDesignerWorkbench::formWindowActionTriggered(QAction *a)
771{
772 QDesignerFormWindow *fw = qobject_cast<QDesignerFormWindow *>(object: a->parentWidget());
773 Q_ASSERT(fw);
774
775 if (isFormWindowMinimized(fw))
776 setFormWindowMinimized(fw, minimized: false);
777
778 if (m_mode == DockedMode) {
779 if (QMdiSubWindow *subWindow = qobject_cast<QMdiSubWindow *>(object: fw->parent())) {
780 m_dockedMainWindow->mdiArea()->setActiveSubWindow(subWindow);
781 }
782 } else {
783 fw->activateWindow();
784 fw->raise();
785 }
786}
787
788void QDesignerWorkbench::closeAllToolWindows()
789{
790 for (QDesignerToolWindow *tw : qAsConst(t&: m_toolWindows))
791 tw->hide();
792}
793
794bool QDesignerWorkbench::readInBackup()
795{
796 const QMap<QString, QString> backupFileMap = QDesignerSettings(m_core).backup();
797 if (backupFileMap.isEmpty())
798 return false;
799
800 const QMessageBox::StandardButton answer =
801 QMessageBox::question(parent: nullptr, title: tr(s: "Backup Information"),
802 text: tr(s: "The last session of Designer was not terminated correctly. "
803 "Backup files were left behind. Do you want to load them?"),
804 buttons: QMessageBox::Yes|QMessageBox::No, defaultButton: QMessageBox::Yes);
805 if (answer == QMessageBox::No)
806 return false;
807
808 const QString modifiedPlaceHolder = QStringLiteral("[*]");
809 for (auto it = backupFileMap.cbegin(), end = backupFileMap.cend(); it != end; ++it) {
810 QString fileName = it.key();
811 fileName.remove(s: modifiedPlaceHolder);
812
813 if(m_actionManager->readInForm(fileName: it.value()))
814 formWindowManager()->activeFormWindow()->setFileName(fileName);
815
816 }
817 return true;
818}
819
820void QDesignerWorkbench::updateBackup(QDesignerFormWindowInterface* fwi)
821{
822 QString fwn = QDir::toNativeSeparators(pathName: fwi->fileName());
823 if (fwn.isEmpty())
824 fwn = fwi->parentWidget()->windowTitle();
825
826 QDesignerSettings settings(m_core);
827 QMap<QString, QString> map = settings.backup();
828 map.remove(akey: fwn);
829 settings.setBackup(map);
830}
831
832namespace {
833 void raiseWindow(QWidget *w) {
834 if (w->isMinimized())
835 w->setWindowState(w->windowState() & ~Qt::WindowMinimized);
836 w->raise();
837 }
838}
839
840void QDesignerWorkbench::bringAllToFront()
841{
842 if (m_mode != TopLevelMode)
843 return;
844 for (QDesignerToolWindow *tw : qAsConst(t&: m_toolWindows))
845 raiseWindow(w: tw);
846 for (QDesignerFormWindow *dfw : qAsConst(t&: m_formWindows))
847 raiseWindow(w: dfw);
848}
849
850// Resize a form window taking MDI decorations into account
851// Apply maximum size as there is no layout connection between
852// the form's main container and the integration's outer
853// container due to the tool widget stack.
854
855void QDesignerWorkbench::resizeForm(QDesignerFormWindow *fw, const QWidget *mainContainer) const
856{
857 const QSize containerSize = mainContainer->size();
858 const QSize containerMaximumSize = mainContainer->maximumSize();
859 if (m_mode != DockedMode) {
860 fw->resize(containerSize);
861 fw->setMaximumSize(containerMaximumSize);
862 return;
863 }
864 // get decorations and resize MDI
865 QMdiSubWindow *mdiSubWindow = qobject_cast<QMdiSubWindow *>(object: fw->parent());
866 Q_ASSERT(mdiSubWindow);
867 const QSize decorationSize = mdiSubWindow->geometry().size() - mdiSubWindow->contentsRect().size();
868 mdiSubWindow->resize(containerSize + decorationSize);
869 // In Qt::RightToLeft mode, the window can grow to be partially hidden by the right border.
870 const int mdiAreaWidth = m_dockedMainWindow->mdiArea()->width();
871 if (qApp->layoutDirection() == Qt::RightToLeft && mdiSubWindow->geometry().right() >= mdiAreaWidth)
872 mdiSubWindow->move(ax: mdiAreaWidth - mdiSubWindow->width(), ay: mdiSubWindow->pos().y());
873
874 if (containerMaximumSize == QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)) {
875 mdiSubWindow->setMaximumSize(containerMaximumSize);
876 } else {
877 mdiSubWindow->setMaximumSize(containerMaximumSize + decorationSize);
878 }
879}
880
881
882// Load a form or return 0 and do cleanup. file name and editor file
883// name in case of loading a template file.
884
885QDesignerFormWindow * QDesignerWorkbench::loadForm(const QString &fileName,
886 bool detectLineTermiantorMode,
887 QString *errorMessage)
888{
889 QFile file(fileName);
890
891 qdesigner_internal::FormWindowBase::LineTerminatorMode mode = qdesigner_internal::FormWindowBase::NativeLineTerminator;
892
893 if (detectLineTermiantorMode) {
894 if (file.open(flags: QFile::ReadOnly)) {
895 const QString text = QString::fromUtf8(str: file.readLine());
896 file.close();
897
898 const int lf = text.indexOf(c: QLatin1Char('\n'));
899 if (lf > 0 && text.at(i: lf-1) == QLatin1Char('\r')) {
900 mode = qdesigner_internal::FormWindowBase::CRLFLineTerminator;
901 } else if (lf >= 0) {
902 mode = qdesigner_internal::FormWindowBase::LFLineTerminator;
903 }
904 }
905 }
906
907 if (!file.open(flags: QFile::ReadOnly|QFile::Text)) {
908 *errorMessage = tr(s: "The file <b>%1</b> could not be opened: %2").arg(args: file.fileName(), args: file.errorString());
909 return nullptr;
910 }
911
912 // Create a form
913 QDesignerFormWindowManagerInterface *formWindowManager = m_core->formWindowManager();
914
915 QDesignerFormWindow *formWindow = new QDesignerFormWindow(/*formWindow=*/ nullptr, this);
916 addFormWindow(formWindow);
917 QDesignerFormWindowInterface *editor = formWindow->editor();
918 Q_ASSERT(editor);
919
920 // Temporarily set the file name. It is needed when converting a UIC 3 file.
921 // In this case, the file name will we be cleared on return to force a save box.
922 editor->setFileName(fileName);
923
924 if (!editor->setContents(dev: &file, errorMessage)) {
925 removeFormWindow(formWindow);
926 formWindowManager->removeFormWindow(formWindow: editor);
927 m_core->metaDataBase()->remove(object: editor);
928 return nullptr;
929 }
930
931 if (qdesigner_internal::FormWindowBase *fwb = qobject_cast<qdesigner_internal::FormWindowBase *>(object: editor))
932 fwb->setLineTerminatorMode(mode);
933
934 switch (m_mode) {
935 case DockedMode: {
936 // below code must be after above call to setContents(), because setContents() may popup warning dialogs which would cause
937 // mdi sub window activation (because of dialogs internal call to processEvent or such)
938 // That activation could have worse consequences, e.g. NULL resource set for active form) before the form is loaded
939 QMdiSubWindow *subWin = m_dockedMainWindow->createMdiSubWindow(fw: formWindow, f: magicalWindowFlags(widgetForFlags: formWindow), designerCloseActionShortCut: m_actionManager->closeFormAction()->shortcut());
940 m_dockedMainWindow->mdiArea()->setActiveSubWindow(subWin);
941 }
942 break;
943 case TopLevelMode: {
944 const QRect formWindowGeometryHint = formWindow->geometryHint();
945 formWindow->setAttribute(Qt::WA_DeleteOnClose, on: true);
946 formWindow->setParent(parent: magicalParent(w: formWindow), f: magicalWindowFlags(widgetForFlags: formWindow));
947 formWindow->resize(formWindowGeometryHint.size());
948 formWindow->move(availableGeometry().center() - formWindowGeometryHint.center());
949 }
950 break;
951 case NeutralMode:
952 break;
953 }
954
955 // Did user specify another (missing) resource path -> set dirty.
956 const bool dirty = editor->property(name: "_q_resourcepathchanged").toBool();
957 editor->setDirty(dirty);
958 resizeForm(fw: formWindow, mainContainer: editor->mainContainer());
959 formWindowManager->setActiveFormWindow(editor);
960 return formWindow;
961}
962
963
964QDesignerFormWindow * QDesignerWorkbench::openForm(const QString &fileName, QString *errorMessage)
965{
966 QDesignerFormWindow *rc = loadForm(fileName, detectLineTermiantorMode: true, errorMessage);
967 if (!rc)
968 return nullptr;
969 rc->editor()->setFileName(fileName);
970 rc->firstShow();
971 return rc;
972}
973
974QDesignerFormWindow * QDesignerWorkbench::openTemplate(const QString &templateFileName,
975 const QString &editorFileName,
976 QString *errorMessage)
977{
978 QDesignerFormWindow *rc = loadForm(fileName: templateFileName, detectLineTermiantorMode: false, errorMessage);
979 if (!rc)
980 return nullptr;
981
982 rc->editor()->setFileName(editorFileName);
983 rc->firstShow();
984 return rc;
985}
986
987void QDesignerWorkbench::minimizationStateChanged(QDesignerFormWindowInterface *formWindow, bool minimized)
988{
989 // refresh the minimize action state
990 if (core()->formWindowManager()->activeFormWindow() == formWindow) {
991 m_actionManager->minimizeAction()->setChecked(minimized);
992 }
993}
994
995void QDesignerWorkbench::toggleFormMinimizationState()
996{
997 QDesignerFormWindowInterface *fwi = core()->formWindowManager()->activeFormWindow();
998 if (!fwi || m_mode == NeutralMode)
999 return;
1000 QDesignerFormWindow *fw = qobject_cast<QDesignerFormWindow *>(object: fwi->parentWidget());
1001 Q_ASSERT(fw);
1002 setFormWindowMinimized(fw, minimized: !isFormWindowMinimized(fw));
1003}
1004
1005bool QDesignerWorkbench::isFormWindowMinimized(const QDesignerFormWindow *fw)
1006{
1007 switch (m_mode) {
1008 case DockedMode:
1009 return mdiSubWindowOf(w: fw)->isShaded();
1010 case TopLevelMode:
1011 return fw->window()->isMinimized();
1012 default:
1013 break;
1014 }
1015 return fw->isMinimized();
1016}
1017
1018void QDesignerWorkbench::setFormWindowMinimized(QDesignerFormWindow *fw, bool minimized)
1019{
1020 switch (m_mode) {
1021 case DockedMode: {
1022 QMdiSubWindow *mdiSubWindow = mdiSubWindowOf(w: fw);
1023 if (minimized) {
1024 mdiSubWindow->showShaded();
1025 } else {
1026 mdiSubWindow->setWindowState(mdiSubWindow->windowState() & ~Qt::WindowMinimized);
1027 }
1028 }
1029 break;
1030 case TopLevelMode: {
1031 QWidget *window = fw->window();
1032 if (window->isMinimized()) {
1033 window->setWindowState(window->windowState() & ~Qt::WindowMinimized);
1034 } else {
1035 window->showMinimized();
1036 }
1037 }
1038 break;
1039 default:
1040 break;
1041 }
1042}
1043
1044/* Applies UI mode changes using Timer-0 delayed signal
1045 * signal to make sure the preferences dialog is closed and destroyed
1046 * before a possible switch from docked mode to top-level mode happens.
1047 * (The switch deletes the main window, which the preference dialog is
1048 * a child of -> BOOM) */
1049
1050void QDesignerWorkbench::applyUiSettings()
1051{
1052 if (m_uiSettingsChanged) {
1053 m_uiSettingsChanged = false;
1054 QTimer::singleShot(interval: 0, receiver: this, slot: &QDesignerWorkbench::restoreUISettings);
1055 }
1056}
1057
1058void QDesignerWorkbench::notifyUISettingsChanged()
1059{
1060 m_uiSettingsChanged = true;
1061}
1062
1063void QDesignerWorkbench::restoreUISettings()
1064{
1065 UIMode mode = QDesignerSettings(m_core).uiMode();
1066 switch (mode) {
1067 case TopLevelMode:
1068 switchToTopLevelMode();
1069 break;
1070 case DockedMode:
1071 switchToDockedMode();
1072 break;
1073
1074 default: Q_ASSERT(0);
1075 }
1076
1077 ToolWindowFontSettings fontSettings = QDesignerSettings(m_core).toolWindowFont();
1078 const QFont &font = fontSettings.m_useFont ? fontSettings.m_font : qApp->font();
1079
1080 if (font == m_toolWindows.constFirst()->font())
1081 return;
1082
1083 for (QDesignerToolWindow *tw : qAsConst(t&: m_toolWindows))
1084 tw->setFont(font);
1085}
1086
1087void QDesignerWorkbench::handleCloseEvent(QCloseEvent *ev)
1088{
1089 ev->setAccepted(handleClose());
1090 if (ev->isAccepted())
1091 QMetaObject::invokeMethod(qDesigner, member: "quit", type: Qt::QueuedConnection); // We're going down!
1092}
1093
1094QDesignerToolWindow *QDesignerWorkbench::widgetBoxToolWindow() const
1095{
1096 return m_toolWindows.at(i: QDesignerToolWindow::WidgetBox);
1097}
1098
1099QT_END_NAMESPACE
1100

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