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
844{
845 switch (m_mode) {
846 case NeutralMode:
847 break;
848 case TopLevelMode:
850 widgetBoxToolWindow()->windowHandle()->requestActivate();
851 break;
852 case DockedMode:
853 raiseWindow(m_dockedMainWindow);
854 m_dockedMainWindow->windowHandle()->requestActivate();
855 break;
856 }
857}
858
859// Resize a form window taking MDI decorations into account
860// Apply maximum size as there is no layout connection between
861// the form's main container and the integration's outer
862// container due to the tool widget stack.
863
864void QDesignerWorkbench::resizeForm(QDesignerFormWindow *fw, const QWidget *mainContainer) const
865{
866 const QSize containerSize = mainContainer->size();
867 const QSize containerMaximumSize = mainContainer->maximumSize();
868 if (m_mode != DockedMode) {
869 fw->resize(containerSize);
870 fw->setMaximumSize(containerMaximumSize);
871 return;
872 }
873 // get decorations and resize MDI
874 auto *mdiSubWindow = qobject_cast<QMdiSubWindow *>(fw->parent());
875 Q_ASSERT(mdiSubWindow);
876 const QSize decorationSize = mdiSubWindow->geometry().size() - mdiSubWindow->contentsRect().size();
877 mdiSubWindow->resize(containerSize + decorationSize);
878 // In Qt::RightToLeft mode, the window can grow to be partially hidden by the right border.
879 const int mdiAreaWidth = m_dockedMainWindow->mdiArea()->width();
880 if (qApp->layoutDirection() == Qt::RightToLeft && mdiSubWindow->geometry().right() >= mdiAreaWidth)
881 mdiSubWindow->move(mdiAreaWidth - mdiSubWindow->width(), mdiSubWindow->pos().y());
882
883 if (containerMaximumSize == QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)) {
884 mdiSubWindow->setMaximumSize(containerMaximumSize);
885 } else {
886 mdiSubWindow->setMaximumSize(containerMaximumSize + decorationSize);
887 }
888}
889
890
891// Load a form or return 0 and do cleanup. file name and editor file
892// name in case of loading a template file.
893
894QDesignerFormWindow * QDesignerWorkbench::loadForm(const QString &fileName,
895 bool detectLineTermiantorMode,
896 QString *errorMessage)
897{
898 QFile file(fileName);
899
900 qdesigner_internal::FormWindowBase::LineTerminatorMode mode = qdesigner_internal::FormWindowBase::NativeLineTerminator;
901
902 if (detectLineTermiantorMode) {
903 if (file.open(QFile::ReadOnly)) {
904 const QString text = QString::fromUtf8(file.readLine());
905 file.close();
906
907 const auto lf = text.indexOf(u'\n');
908 if (lf > 0 && text.at(lf - 1) == u'\r') {
909 mode = qdesigner_internal::FormWindowBase::CRLFLineTerminator;
910 } else if (lf >= 0) {
911 mode = qdesigner_internal::FormWindowBase::LFLineTerminator;
912 }
913 }
914 }
915
916 if (!file.open(QFile::ReadOnly|QFile::Text)) {
917 *errorMessage = tr("The file <b>%1</b> could not be opened: %2").arg(file.fileName(), file.errorString());
918 return nullptr;
919 }
920
921 // Create a form
922 QDesignerFormWindowManagerInterface *formWindowManager = m_core->formWindowManager();
923
924 auto *formWindow = new QDesignerFormWindow(/*formWindow=*/ nullptr, this);
925 addFormWindow(formWindow);
926 QDesignerFormWindowInterface *editor = formWindow->editor();
927 Q_ASSERT(editor);
928
929 // Temporarily set the file name. It is needed when converting a UIC 3 file.
930 // In this case, the file name will we be cleared on return to force a save box.
931 editor->setFileName(fileName);
932
933 if (!editor->setContents(&file, errorMessage)) {
934 removeFormWindow(formWindow);
935 formWindowManager->removeFormWindow(editor);
936 m_core->metaDataBase()->remove(editor);
937 return nullptr;
938 }
939
940 if (qdesigner_internal::FormWindowBase *fwb = qobject_cast<qdesigner_internal::FormWindowBase *>(editor))
941 fwb->setLineTerminatorMode(mode);
942
943 switch (m_mode) {
944 case DockedMode: {
945 // below code must be after above call to setContents(), because setContents() may popup warning dialogs which would cause
946 // mdi sub window activation (because of dialogs internal call to processEvent or such)
947 // That activation could have worse consequences, e.g. NULL resource set for active form) before the form is loaded
948 QMdiSubWindow *subWin = m_dockedMainWindow->createMdiSubWindow(formWindow, magicalWindowFlags(formWindow), m_actionManager->closeFormAction()->shortcut());
949 m_dockedMainWindow->mdiArea()->setActiveSubWindow(subWin);
950 }
951 break;
952 case TopLevelMode: {
953 const QRect formWindowGeometryHint = formWindow->geometryHint();
954 formWindow->setAttribute(Qt::WA_DeleteOnClose, true);
955 formWindow->setParent(magicalParent(formWindow), magicalWindowFlags(formWindow));
956 formWindow->resize(formWindowGeometryHint.size());
957 formWindow->move(availableFormGeometry().center() - formWindowGeometryHint.center());
958 }
959 break;
960 case NeutralMode:
961 break;
962 }
963
964 // Did user specify another (missing) resource path -> set dirty.
965 const bool dirty = editor->property("_q_resourcepathchanged").toBool();
966 editor->setDirty(dirty);
967 resizeForm(formWindow, editor->mainContainer());
968 formWindowManager->setActiveFormWindow(editor);
969 return formWindow;
970}
971
972
973QDesignerFormWindow * QDesignerWorkbench::openForm(const QString &fileName, QString *errorMessage)
974{
975 QDesignerFormWindow *rc = loadForm(fileName, true, errorMessage);
976 if (!rc)
977 return nullptr;
978 rc->editor()->setFileName(fileName);
980 return rc;
981}
982
983QDesignerFormWindow * QDesignerWorkbench::openTemplate(const QString &templateFileName,
984 const QString &editorFileName,
985 QString *errorMessage)
986{
987 QDesignerFormWindow *rc = loadForm(templateFileName, false, errorMessage);
988 if (!rc)
989 return nullptr;
990
991 rc->editor()->setFileName(editorFileName);
993 return rc;
994}
995
996void QDesignerWorkbench::minimizationStateChanged(QDesignerFormWindowInterface *formWindow, bool minimized)
997{
998 // refresh the minimize action state
999 if (core()->formWindowManager()->activeFormWindow() == formWindow) {
1000 m_actionManager->minimizeAction()->setChecked(minimized);
1001 }
1002}
1003
1005{
1006 QDesignerFormWindowInterface *fwi = core()->formWindowManager()->activeFormWindow();
1007 if (!fwi || m_mode == NeutralMode)
1008 return;
1009 QDesignerFormWindow *fw = qobject_cast<QDesignerFormWindow *>(fwi->parentWidget());
1010 Q_ASSERT(fw);
1011 setFormWindowMinimized(fw, !isFormWindowMinimized(fw));
1012}
1013
1014bool QDesignerWorkbench::isFormWindowMinimized(const QDesignerFormWindow *fw)
1015{
1016 switch (m_mode) {
1017 case DockedMode:
1018 return mdiSubWindowOf(fw)->isShaded();
1019 case TopLevelMode:
1020 return fw->window()->isMinimized();
1021 default:
1022 break;
1023 }
1024 return fw->isMinimized();
1025}
1026
1027void QDesignerWorkbench::setFormWindowMinimized(QDesignerFormWindow *fw, bool minimized)
1028{
1029 switch (m_mode) {
1030 case DockedMode: {
1031 QMdiSubWindow *mdiSubWindow = mdiSubWindowOf(fw);
1032 if (minimized) {
1033 mdiSubWindow->showShaded();
1034 } else {
1035 mdiSubWindow->setWindowState(mdiSubWindow->windowState() & ~Qt::WindowMinimized);
1036 }
1037 }
1038 break;
1039 case TopLevelMode: {
1040 QWidget *window = fw->window();
1041 if (window->isMinimized()) {
1042 window->setWindowState(window->windowState() & ~Qt::WindowMinimized);
1043 } else {
1044 window->showMinimized();
1045 }
1046 }
1047 break;
1048 default:
1049 break;
1050 }
1051}
1052
1053/* Applies UI mode changes using Timer-0 delayed signal
1054 * signal to make sure the preferences dialog is closed and destroyed
1055 * before a possible switch from docked mode to top-level mode happens.
1056 * (The switch deletes the main window, which the preference dialog is
1057 * a child of -> BOOM) */
1058
1060{
1061 if (m_uiSettingsChanged) {
1062 m_uiSettingsChanged = false;
1063 QTimer::singleShot(0, this, &QDesignerWorkbench::restoreUISettings);
1064 }
1065}
1066
1067void QDesignerWorkbench::notifyUISettingsChanged()
1068{
1069 m_uiSettingsChanged = true;
1070}
1071
1072void QDesignerWorkbench::restoreUISettings()
1073{
1074 UIMode mode = QDesignerSettings(m_core).uiMode();
1075 switch (mode) {
1076 case TopLevelMode:
1077 switchToTopLevelMode();
1078 break;
1079 case DockedMode:
1080 switchToDockedMode();
1081 break;
1082
1083 default: Q_ASSERT(0);
1084 }
1085
1087 const QFont &font = fontSettings.m_useFont ? fontSettings.m_font : qApp->font();
1088
1089 if (font == m_toolWindows.constFirst()->font())
1090 return;
1091
1092 for (QDesignerToolWindow *tw : std::as_const(m_toolWindows))
1093 tw->setFont(font);
1094}
1095
1096void QDesignerWorkbench::handleCloseEvent(QCloseEvent *ev)
1097{
1098 ev->setAccepted(handleClose());
1099 if (ev->isAccepted())
1100 QMetaObject::invokeMethod(qDesigner, "quit", Qt::QueuedConnection); // We're going down!
1101}
1102
1103QDesignerToolWindow *QDesignerWorkbench::widgetBoxToolWindow() const
1104{
1105 return m_toolWindows.at(QDesignerToolWindow::WidgetBox);
1106}
1107
1108QT_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:431
#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)