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