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