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_actions.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
6#include <qdesigner_utils_p.h>
7#include "qdesigner.h"
10#include "mainwindow.h"
11#include "newform.h"
12#include "versiondialog.h"
15#include "appfontdialog.h"
16
17#include <pluginmanager_p.h>
18#include <qdesigner_formbuilder_p.h>
19#include <qdesigner_utils_p.h>
20#include <iconloader_p.h>
21#include <previewmanager_p.h>
22#include <codedialog_p.h>
23#include <qdesigner_formwindowmanager_p.h>
24
25// sdk
26#include <QtDesigner/abstractformeditor.h>
27#include <QtDesigner/abstractformwindow.h>
28#include <QtDesigner/abstractintegration.h>
29#include <QtDesigner/abstractlanguage.h>
30#include <QtDesigner/abstractmetadatabase.h>
31#include <QtDesigner/abstractformwindowmanager.h>
32#include <QtDesigner/abstractformwindowcursor.h>
33#include <QtDesigner/abstractformeditorplugin.h>
34#include <QtDesigner/qextensionmanager.h>
35
36#include <QtDesigner/private/shared_settings_p.h>
37#include <QtDesigner/private/formwindowbase_p.h>
38
39#include <QtWidgets/qstylefactory.h>
40#include <QtWidgets/qfiledialog.h>
41#include <QtWidgets/qmenu.h>
42#include <QtWidgets/qmessagebox.h>
43#include <QtWidgets/qmdisubwindow.h>
44#include <QtWidgets/qpushbutton.h>
45#include <QtWidgets/qstatusbar.h>
46
47#include <QtGui/qaction.h>
48#include <QtGui/qactiongroup.h>
49#include <QtGui/qevent.h>
50#include <QtGui/qicon.h>
51#include <QtGui/qimage.h>
52#include <QtGui/qpixmap.h>
53#include <QtGui/qscreen.h>
54#if defined(QT_PRINTSUPPORT_LIB) // Some platforms may not build QtPrintSupport
55# include <QtPrintSupport/qtprintsupportglobal.h>
56# if QT_CONFIG(printer) && QT_CONFIG(printdialog)
57# include <QtPrintSupport/qprinter.h>
58# include <QtPrintSupport/qprintdialog.h>
59# define HAS_PRINTER
60# endif
61#endif
62#include <QtGui/qpainter.h>
63#include <QtGui/qtransform.h>
64#include <QtGui/qcursor.h>
65
66#include <QtCore/qdir.h>
67#include <QtCore/qsize.h>
68#include <QtCore/qlibraryinfo.h>
69#include <QtCore/qbuffer.h>
70#include <QtCore/qpluginloader.h>
71#include <QtCore/qdebug.h>
72#include <QtCore/qtimer.h>
73#include <QtCore/qtextstream.h>
74#include <QtCore/qmetaobject.h>
75#include <QtCore/qfileinfo.h>
76#include <QtCore/qsavefile.h>
77#include <QtCore/qscopedpointer.h>
78#include <QtXml/qdom.h>
79
80#include <algorithm>
81
82#include <optional>
83
84QT_BEGIN_NAMESPACE
85
86using namespace Qt::StringLiterals;
87
88const char *QDesignerActions::defaultToolbarPropertyName = "__qt_defaultToolBarAction";
89
90//#ifdef Q_OS_MACOS
91# define NONMODAL_PREVIEW
92//#endif
93
94static QAction *createSeparator(QObject *parent) {
95 QAction * rc = new QAction(parent);
96 rc->setSeparator(true);
97 return rc;
98}
99
100static QActionGroup *createActionGroup(QObject *parent, bool exclusive = false) {
101 QActionGroup * rc = new QActionGroup(parent);
102 rc->setExclusive(exclusive);
103 return rc;
104}
105
106static void fixActionContext(const QList<QAction *> &actions)
107{
108 for (QAction *a : actions)
109 a->setShortcutContext(Qt::ApplicationShortcut);
110}
111
112static inline QString savedMessage(const QString &fileName)
113{
114 return QDesignerActions::tr("Saved %1.").arg(fileName);
115}
116
117static QString fileDialogFilters(const QString &extension)
118{
119 return QDesignerActions::tr("Designer UI files (*.%1);;All Files (*)").arg(extension);
120}
121
122static QString fixResourceFileBackupPath(const QDesignerFormWindowInterface *fwi,
123 const QDir& backupDir);
124
125static QByteArray formWindowContents(const QDesignerFormWindowInterface *fw,
126 std::optional<QDir> alternativeDir = {})
127{
128 QString contents = alternativeDir.has_value()
129 ? fixResourceFileBackupPath(fw, alternativeDir.value()) : fw->contents();
130 if (auto *fwb = qobject_cast<const qdesigner_internal::FormWindowBase *>(fw)) {
131 if (fwb->lineTerminatorMode() == qdesigner_internal::FormWindowBase::CRLFLineTerminator)
132 contents.replace(u'\n', "\r\n"_L1);
133 }
134 return contents.toUtf8();
135}
136
137QFileDialog *createSaveAsDialog(QWidget *parent, const QString &dir, const QString &extension)
138{
139 auto result = new QFileDialog(parent, QDesignerActions::tr("Save Form As"),
140 dir, fileDialogFilters(extension));
141 result->setAcceptMode(QFileDialog::AcceptSave);
142 result->setDefaultSuffix(extension);
143 return result;
144}
145
146QDesignerActions::QDesignerActions(QDesignerWorkbench *workbench)
147 : QObject(workbench),
148 m_workbench(workbench),
149 m_core(workbench->core()),
150 m_settings(workbench->core()),
151 m_backupTimer(new QTimer(this)),
152 m_fileActions(createActionGroup(this)),
153 m_recentFilesActions(createActionGroup(this)),
154 m_editActions(createActionGroup(this)),
155 m_formActions(createActionGroup(this)),
156 m_settingsActions(createActionGroup(this)),
157 m_windowActions(createActionGroup(this)),
158 m_toolActions(createActionGroup(this, true)),
159 m_editWidgetsAction(new QAction(tr("Edit Widgets"), this)),
160 m_newFormAction(new QAction(qdesigner_internal::createIconSet(QIcon::ThemeIcon::DocumentNew,
161 "filenew.png"_L1),
162 tr("&New..."), this)),
163 m_openFormAction(new QAction(qdesigner_internal::createIconSet(QIcon::ThemeIcon::DocumentOpen,
164 "fileopen.png"_L1),
165 tr("&Open..."), this)),
166 m_saveFormAction(new QAction(qdesigner_internal::createIconSet(QIcon::ThemeIcon::DocumentSave,
167 "filesave.png"_L1),
168 tr("&Save"), this)),
169 m_saveFormAsAction(new QAction(QIcon::fromTheme(QIcon::ThemeIcon::DocumentSaveAs),
170 tr("Save &As..."), this)),
171 m_saveAllFormsAction(new QAction(tr("Save A&ll"), this)),
172 m_saveFormAsTemplateAction(new QAction(tr("Save As &Template..."), this)),
173 m_closeFormAction(new QAction(QIcon::fromTheme(QIcon::ThemeIcon::WindowClose),
174 tr("&Close"), this)),
175 m_savePreviewImageAction(new QAction(tr("Save &Image..."), this)),
176 m_printPreviewAction(new QAction(QIcon::fromTheme(QIcon::ThemeIcon::DocumentPrint),
177 tr("&Print..."), this)),
178 m_quitAction(new QAction(QIcon::fromTheme(QIcon::ThemeIcon::ApplicationExit),
179 tr("&Quit"), this)),
180 m_viewCppCodeAction(new QAction(tr("View &C++ Code..."), this)),
181 m_viewPythonCodeAction(new QAction(tr("View &Python Code..."), this)),
182 m_minimizeAction(new QAction(tr("&Minimize"), this)),
183 m_bringAllToFrontSeparator(createSeparator(this)),
184 m_bringAllToFrontAction(new QAction(tr("Bring All to Front"), this)),
185 m_windowListSeparatorAction(createSeparator(this)),
186 m_preferencesAction(new QAction(tr("Preferences..."), this)),
187 m_appFontAction(new QAction(tr("Additional Fonts..."), this))
188{
189 Q_ASSERT(m_core != nullptr);
190 qdesigner_internal::QDesignerFormWindowManager *ifwm = qobject_cast<qdesigner_internal::QDesignerFormWindowManager *>(m_core->formWindowManager());
191 Q_ASSERT(ifwm);
192 m_previewManager = ifwm->previewManager();
193 m_previewFormAction = ifwm->action(QDesignerFormWindowManagerInterface::DefaultPreviewAction);
194 m_styleActions = ifwm->actionGroup(QDesignerFormWindowManagerInterface::StyledPreviewActionGroup);
195 connect(ifwm, &QDesignerFormWindowManagerInterface::formWindowSettingsChanged,
196 this, &QDesignerActions::formWindowSettingsChanged);
197
198 m_editWidgetsAction->setObjectName(u"__qt_edit_widgets_action"_s);
199 m_newFormAction->setObjectName(u"__qt_new_form_action"_s);
200 m_openFormAction->setObjectName(u"__qt_open_form_action"_s);
201 m_saveFormAction->setObjectName(u"__qt_save_form_action"_s);
202 m_saveFormAsAction->setObjectName(u"__qt_save_form_as_action"_s);
203 m_saveAllFormsAction->setObjectName(u"__qt_save_all_forms_action"_s);
204 m_saveFormAsTemplateAction->setObjectName(u"__qt_save_form_as_template_action"_s);
205 m_closeFormAction->setObjectName(u"__qt_close_form_action"_s);
206 m_quitAction->setObjectName(u"__qt_quit_action"_s);
207 m_previewFormAction->setObjectName(u"__qt_preview_form_action"_s);
208 m_viewCppCodeAction->setObjectName(u"__qt_preview_cpp_code_action"_s);
209 m_viewPythonCodeAction->setObjectName(u"__qt_preview_python_code_action"_s);
210 m_minimizeAction->setObjectName(u"__qt_minimize_action"_s);
211 m_bringAllToFrontAction->setObjectName(u"__qt_bring_all_to_front_action"_s);
212 m_preferencesAction->setObjectName(u"__qt_preferences_action"_s);
213
214 m_helpActions = createHelpActions();
215
216 m_newFormAction->setProperty(QDesignerActions::defaultToolbarPropertyName, true);
217 m_openFormAction->setProperty(QDesignerActions::defaultToolbarPropertyName, true);
218 m_saveFormAction->setProperty(QDesignerActions::defaultToolbarPropertyName, true);
219
220 QDesignerFormWindowManagerInterface *formWindowManager = m_core->formWindowManager();
221 Q_ASSERT(formWindowManager != nullptr);
222
223//
224// file actions
225//
226 m_newFormAction->setShortcut(QKeySequence::New);
227 connect(m_newFormAction, &QAction::triggered, this, &QDesignerActions::createForm);
228 m_fileActions->addAction(m_newFormAction);
229
230 m_openFormAction->setShortcut(QKeySequence::Open);
231 connect(m_openFormAction, &QAction::triggered, this, &QDesignerActions::slotOpenForm);
232 m_fileActions->addAction(m_openFormAction);
233
234 m_fileActions->addAction(createRecentFilesMenu());
235 m_fileActions->addAction(createSeparator(this));
236
237 m_saveFormAction->setShortcut(QKeySequence::Save);
238 connect(m_saveFormAction, &QAction::triggered, this,
239 QOverload<>::of(&QDesignerActions::saveForm));
240 m_fileActions->addAction(m_saveFormAction);
241
242 connect(m_saveFormAsAction, &QAction::triggered, this,
243 QOverload<>::of(&QDesignerActions::saveFormAs));
244 m_fileActions->addAction(m_saveFormAsAction);
245
246#ifdef Q_OS_MACOS
247 m_saveAllFormsAction->setShortcut(tr("ALT+CTRL+S"));
248#else
249 m_saveAllFormsAction->setShortcut(tr("CTRL+SHIFT+S")); // Commonly "Save As" on Mac
250#endif
251 connect(m_saveAllFormsAction, &QAction::triggered, this, &QDesignerActions::saveAllForms);
252 m_fileActions->addAction(m_saveAllFormsAction);
253
254 connect(m_saveFormAsTemplateAction, &QAction::triggered, this, &QDesignerActions::saveFormAsTemplate);
255 m_fileActions->addAction(m_saveFormAsTemplateAction);
256
257 m_fileActions->addAction(createSeparator(this));
258
259 m_printPreviewAction->setShortcut(QKeySequence::Print);
260 connect(m_printPreviewAction, &QAction::triggered, this, &QDesignerActions::printPreviewImage);
261 m_fileActions->addAction(m_printPreviewAction);
262 m_printPreviewAction->setObjectName(u"__qt_print_action"_s);
263
264 connect(m_savePreviewImageAction, &QAction::triggered, this, &QDesignerActions::savePreviewImage);
265 m_savePreviewImageAction->setObjectName(u"__qt_saveimage_action"_s);
266 m_fileActions->addAction(m_savePreviewImageAction);
267 m_fileActions->addAction(createSeparator(this));
268
269 m_closeFormAction->setShortcut(QKeySequence::Close);
270 connect(m_closeFormAction, &QAction::triggered, this, &QDesignerActions::closeForm);
271 m_fileActions->addAction(m_closeFormAction);
272 updateCloseAction();
273
274 m_fileActions->addAction(createSeparator(this));
275
276 m_quitAction->setShortcuts(QKeySequence::Quit);
277 m_quitAction->setMenuRole(QAction::QuitRole);
278 connect(m_quitAction, &QAction::triggered, this, &QDesignerActions::shutdown);
279 m_fileActions->addAction(m_quitAction);
280
281//
282// edit actions
283//
284 QAction *undoAction = formWindowManager->action(QDesignerFormWindowManagerInterface::UndoAction);
285 undoAction->setObjectName(u"__qt_undo_action"_s);
286 undoAction->setShortcut(QKeySequence::Undo);
287 m_editActions->addAction(undoAction);
288
289 QAction *redoAction = formWindowManager->action(QDesignerFormWindowManagerInterface::RedoAction);
290 redoAction->setObjectName(u"__qt_redo_action"_s);
291 redoAction->setShortcut(QKeySequence::Redo);
292 m_editActions->addAction(redoAction);
293
294 m_editActions->addAction(createSeparator(this));
295
296#if QT_CONFIG(clipboard)
297 m_editActions->addAction(formWindowManager->action(QDesignerFormWindowManagerInterface::CutAction));
298 m_editActions->addAction(formWindowManager->action(QDesignerFormWindowManagerInterface::CopyAction));
299 m_editActions->addAction(formWindowManager->action(QDesignerFormWindowManagerInterface::PasteAction));
300#endif
301 m_editActions->addAction(formWindowManager->action(QDesignerFormWindowManagerInterface::DeleteAction));
302
303 m_editActions->addAction(formWindowManager->action(QDesignerFormWindowManagerInterface::SelectAllAction));
304
305 m_editActions->addAction(createSeparator(this));
306
307 m_editActions->addAction(formWindowManager->action(QDesignerFormWindowManagerInterface::LowerAction));
308 m_editActions->addAction(formWindowManager->action(QDesignerFormWindowManagerInterface::RaiseAction));
309
310 formWindowManager->action(QDesignerFormWindowManagerInterface::LowerAction)->setProperty(QDesignerActions::defaultToolbarPropertyName, true);
311 formWindowManager->action(QDesignerFormWindowManagerInterface::RaiseAction)->setProperty(QDesignerActions::defaultToolbarPropertyName, true);
312
313//
314// edit mode actions
315//
316
317 m_editWidgetsAction->setCheckable(true);
318 QList<QKeySequence> shortcuts;
319 shortcuts.append(QKeySequence(Qt::Key_F3));
320 shortcuts.append(QKeySequence(Qt::Key_Escape));
321 m_editWidgetsAction->setShortcuts(shortcuts);
322 m_editWidgetsAction->setIcon(qdesigner_internal::createIconSet("widgettool.png"_L1));
323 connect(m_editWidgetsAction, &QAction::triggered, this, &QDesignerActions::editWidgetsSlot);
324 m_editWidgetsAction->setChecked(true);
325 m_editWidgetsAction->setEnabled(false);
326 m_editWidgetsAction->setProperty(QDesignerActions::defaultToolbarPropertyName, true);
327 m_toolActions->addAction(m_editWidgetsAction);
328
329 connect(formWindowManager, &qdesigner_internal::QDesignerFormWindowManager::activeFormWindowChanged,
330 this, &QDesignerActions::activeFormWindowChanged);
331
332 const QObjectList builtinPlugins = QPluginLoader::staticInstances()
333 + m_core->pluginManager()->instances();
334 for (QObject *plugin : builtinPlugins) {
335 if (QDesignerFormEditorPluginInterface *formEditorPlugin = qobject_cast<QDesignerFormEditorPluginInterface*>(plugin)) {
336 if (QAction *action = formEditorPlugin->action()) {
337 m_toolActions->addAction(action);
338 action->setProperty(QDesignerActions::defaultToolbarPropertyName, true);
339 action->setCheckable(true);
340 }
341 }
342 }
343
344 connect(m_preferencesAction, &QAction::triggered, this, &QDesignerActions::showPreferencesDialog);
345 m_preferencesAction->setMenuRole(QAction::PreferencesRole);
346 m_settingsActions->addAction(m_preferencesAction);
347
348 connect(m_appFontAction, &QAction::triggered, this, &QDesignerActions::showAppFontDialog);
349 m_settingsActions->addAction(m_appFontAction);
350//
351// form actions
352//
353
354 m_formActions->addAction(formWindowManager->action(QDesignerFormWindowManagerInterface::HorizontalLayoutAction));
355 m_formActions->addAction(formWindowManager->action(QDesignerFormWindowManagerInterface::VerticalLayoutAction));
356 m_formActions->addAction(formWindowManager->action(QDesignerFormWindowManagerInterface::SplitHorizontalAction));
357 m_formActions->addAction(formWindowManager->action(QDesignerFormWindowManagerInterface::SplitVerticalAction));
358 m_formActions->addAction(formWindowManager->action(QDesignerFormWindowManagerInterface::GridLayoutAction));
359 m_formActions->addAction(formWindowManager->action(QDesignerFormWindowManagerInterface::FormLayoutAction));
360 m_formActions->addAction(formWindowManager->action(QDesignerFormWindowManagerInterface::BreakLayoutAction));
361 m_formActions->addAction(formWindowManager->action(QDesignerFormWindowManagerInterface::AdjustSizeAction));
362 m_formActions->addAction(formWindowManager->action(QDesignerFormWindowManagerInterface::SimplifyLayoutAction));
363 m_formActions->addAction(createSeparator(this));
364
365 formWindowManager->action(QDesignerFormWindowManagerInterface::HorizontalLayoutAction)->setProperty(QDesignerActions::defaultToolbarPropertyName, true);
366 formWindowManager->action(QDesignerFormWindowManagerInterface::VerticalLayoutAction)->setProperty(QDesignerActions::defaultToolbarPropertyName, true);
367 formWindowManager->action(QDesignerFormWindowManagerInterface::SplitHorizontalAction)->setProperty(QDesignerActions::defaultToolbarPropertyName, true);
368 formWindowManager->action(QDesignerFormWindowManagerInterface::SplitVerticalAction)->setProperty(QDesignerActions::defaultToolbarPropertyName, true);
369 formWindowManager->action(QDesignerFormWindowManagerInterface::GridLayoutAction)->setProperty(QDesignerActions::defaultToolbarPropertyName, true);
370 formWindowManager->action(QDesignerFormWindowManagerInterface::FormLayoutAction)->setProperty(QDesignerActions::defaultToolbarPropertyName, true);
371 formWindowManager->action(QDesignerFormWindowManagerInterface::BreakLayoutAction)->setProperty(QDesignerActions::defaultToolbarPropertyName, true);
372 formWindowManager->action(QDesignerFormWindowManagerInterface::AdjustSizeAction)->setProperty(QDesignerActions::defaultToolbarPropertyName, true);
373
374 m_previewFormAction->setShortcut(tr("CTRL+R"));
375 m_formActions->addAction(m_previewFormAction);
376 connect(m_previewManager, &qdesigner_internal::PreviewManager::firstPreviewOpened,
377 this, &QDesignerActions::updateCloseAction);
378 connect(m_previewManager, &qdesigner_internal::PreviewManager::lastPreviewClosed,
379 this, &QDesignerActions::updateCloseAction);
380
381 connect(m_viewCppCodeAction, &QAction::triggered, this,
382 [this] () { this->viewCode(qdesigner_internal::UicLanguage::Cpp); });
383 connect(m_viewPythonCodeAction, &QAction::triggered, this,
384 [this] () { this->viewCode(qdesigner_internal::UicLanguage::Python); });
385
386 // Preview code only in Cpp/Python (uic)
387 if (qt_extension<QDesignerLanguageExtension *>(m_core->extensionManager(), m_core) == nullptr) {
388 m_formActions->addAction(m_viewCppCodeAction);
389 m_formActions->addAction(m_viewPythonCodeAction);
390 }
391
392 m_formActions->addAction(createSeparator(this));
393
394 m_formActions->addAction(ifwm->action(QDesignerFormWindowManagerInterface::FormWindowSettingsDialogAction));
395//
396// window actions
397//
398 m_minimizeAction->setEnabled(false);
399 m_minimizeAction->setCheckable(true);
400 m_minimizeAction->setShortcut(tr("CTRL+M"));
401 connect(m_minimizeAction, &QAction::triggered, m_workbench, &QDesignerWorkbench::toggleFormMinimizationState);
402 m_windowActions->addAction(m_minimizeAction);
403
404 m_windowActions->addAction(m_bringAllToFrontSeparator);
405 connect(m_bringAllToFrontAction, &QAction::triggered, m_workbench, &QDesignerWorkbench::bringAllToFront);
406 m_windowActions->addAction(m_bringAllToFrontAction);
407 m_windowActions->addAction(m_windowListSeparatorAction);
408
410
411//
412// connections
413//
414 fixActionContext(m_fileActions->actions());
415 fixActionContext(m_editActions->actions());
416 fixActionContext(m_toolActions->actions());
417 fixActionContext(m_formActions->actions());
418 fixActionContext(m_windowActions->actions());
419 fixActionContext(m_helpActions->actions());
420
421 activeFormWindowChanged(core()->formWindowManager()->activeFormWindow());
422
423 m_backupTimer->start(180000); // 3min
424 connect(m_backupTimer, &QTimer::timeout, this, &QDesignerActions::backupForms);
425
426 // Enable application font action
427 connect(formWindowManager, &QDesignerFormWindowManagerInterface::formWindowAdded,
428 this, &QDesignerActions::formWindowCountChanged);
429 connect(formWindowManager, &QDesignerFormWindowManagerInterface::formWindowRemoved,
430 this, &QDesignerActions::formWindowCountChanged);
431 formWindowCountChanged();
432}
433
434QActionGroup *QDesignerActions::createHelpActions()
435{
436 QActionGroup *helpActions = createActionGroup(this);
437
438#ifndef QT_JAMBI_BUILD
439 QAction *mainHelpAction = new QAction(tr("Qt Widgets Designer &Help"), this);
440 mainHelpAction->setObjectName(u"__qt_designer_help_action"_s);
441 connect(mainHelpAction, &QAction::triggered, this, &QDesignerActions::showDesignerHelp);
442 mainHelpAction->setShortcut(Qt::CTRL | Qt::Key_Question);
443 helpActions->addAction(mainHelpAction);
444
445 helpActions->addAction(createSeparator(this));
446 QAction *widgetHelp = new QAction(tr("Current Widget Help"), this);
447 widgetHelp->setObjectName(u"__qt_current_widget_help_action"_s);
448 widgetHelp->setShortcut(Qt::Key_F1);
449 connect(widgetHelp, &QAction::triggered, this, &QDesignerActions::showWidgetSpecificHelp);
450 helpActions->addAction(widgetHelp);
451
452#endif
453
454 helpActions->addAction(createSeparator(this));
455 QAction *aboutPluginsAction = new QAction(tr("About Plugins"), this);
456 aboutPluginsAction->setObjectName(u"__qt_about_plugins_action"_s);
457 aboutPluginsAction->setMenuRole(QAction::ApplicationSpecificRole);
458 connect(aboutPluginsAction, &QAction::triggered,
459 m_core->formWindowManager(), &QDesignerFormWindowManagerInterface::showPluginDialog);
460 helpActions->addAction(aboutPluginsAction);
461
462 QAction *aboutDesignerAction = new QAction(tr("About Qt Widgets Designer"), this);
463 aboutDesignerAction->setMenuRole(QAction::AboutRole);
464 aboutDesignerAction->setObjectName(u"__qt_about_designer_action"_s);
465 connect(aboutDesignerAction, &QAction::triggered, this, &QDesignerActions::aboutDesigner);
466 helpActions->addAction(aboutDesignerAction);
467
468 QAction *aboutQtAction = new QAction(tr("About Qt"), this);
469 aboutQtAction->setMenuRole(QAction::AboutQtRole);
470 aboutQtAction->setObjectName(u"__qt_about_qt_action"_s);
471 connect(aboutQtAction, &QAction::triggered, qApp, &QApplication::aboutQt);
472 helpActions->addAction(aboutQtAction);
473 return helpActions;
474}
475
477{
478#ifdef HAS_PRINTER
479 delete m_printer;
480#endif
481}
482
484{
485 QDesignerLanguageExtension *lang
486 = qt_extension<QDesignerLanguageExtension *>(m_core->extensionManager(), m_core);
487 if (lang)
488 return lang->uiExtension();
489 return u"ui"_s;
490}
491
492QAction *QDesignerActions::createRecentFilesMenu()
493{
494 m_recentMenu.reset(new QMenu);
495 QAction *act;
496 // Need to insert this into the QAction.
497 for (int i = 0; i < MaxRecentFiles; ++i) {
498 act = new QAction(this);
499 act->setVisible(false);
500 connect(act, &QAction::triggered, this, &QDesignerActions::openRecentForm);
501 m_recentFilesActions->addAction(act);
502 m_recentMenu->addAction(act);
503 }
504 updateRecentFileActions();
505 m_recentMenu->addSeparator();
506 act = new QAction(QIcon::fromTheme(QIcon::ThemeIcon::EditClear),
507 tr("Clear &Menu"), this);
508 act->setObjectName(u"__qt_action_clear_menu_"_s);
509 connect(act, &QAction::triggered, this, &QDesignerActions::clearRecentFiles);
510 m_recentFilesActions->addAction(act);
511 m_recentMenu->addAction(act);
512
513 act = new QAction(QIcon::fromTheme(QIcon::ThemeIcon::DocumentOpenRecent),
514 tr("&Recent Forms"), this);
515 act->setMenu(m_recentMenu.get());
516 return act;
517}
518
519QActionGroup *QDesignerActions::toolActions() const
520{ return m_toolActions; }
521
523{ return m_workbench; }
524
525QDesignerFormEditorInterface *QDesignerActions::core() const
526{ return m_core; }
527
528QActionGroup *QDesignerActions::fileActions() const
529{ return m_fileActions; }
530
531QActionGroup *QDesignerActions::editActions() const
532{ return m_editActions; }
533
534QActionGroup *QDesignerActions::formActions() const
535{ return m_formActions; }
536
537QActionGroup *QDesignerActions::settingsActions() const
538{ return m_settingsActions; }
539
540QActionGroup *QDesignerActions::windowActions() const
541{ return m_windowActions; }
542
543QActionGroup *QDesignerActions::helpActions() const
544{ return m_helpActions; }
545
546QActionGroup *QDesignerActions::styleActions() const
547{ return m_styleActions; }
548
550{ return m_previewFormAction; }
551
553{ return m_viewCppCodeAction; }
554
555
556void QDesignerActions::editWidgetsSlot()
557{
558 QDesignerFormWindowManagerInterface *formWindowManager = core()->formWindowManager();
559 for (int i=0; i<formWindowManager->formWindowCount(); ++i) {
560 QDesignerFormWindowInterface *formWindow = formWindowManager->formWindow(i);
561 formWindow->editWidgets();
562 }
563}
564
566{
567 showNewFormDialog(QString());
568}
569
570void QDesignerActions::showNewFormDialog(const QString &fileName)
571{
572 closePreview();
573 NewForm *dlg = new NewForm(workbench(), workbench()->core()->topLevel(), fileName);
574
575 dlg->setAttribute(Qt::WA_DeleteOnClose);
576 dlg->setAttribute(Qt::WA_ShowModal);
577
578 dlg->setGeometry(fixDialogRect(dlg->rect()));
579 dlg->exec();
580}
581
583{
584 openForm(core()->topLevel());
585}
586
588{
589 closePreview();
590 const QString extension = uiExtension();
591 const QStringList fileNames = QFileDialog::getOpenFileNames(parent, tr("Open Form"),
592 m_openDirectory, fileDialogFilters(extension), nullptr);
593
594 if (fileNames.isEmpty())
595 return false;
596
597 bool atLeastOne = false;
598 for (const QString &fileName : fileNames) {
599 if (readInForm(fileName) && !atLeastOne)
600 atLeastOne = true;
601 }
602
603 return atLeastOne;
604}
605
606bool QDesignerActions::saveFormAs(QDesignerFormWindowInterface *fw)
607{
608 const QString extension = uiExtension();
609
610 QString dir = fw->fileName();
611 if (dir.isEmpty()) {
612 do {
613 // Build untitled name
614 if (!m_saveDirectory.isEmpty()) {
615 dir = m_saveDirectory;
616 break;
617 }
618 if (!m_openDirectory.isEmpty()) {
619 dir = m_openDirectory;
620 break;
621 }
622 dir = QDir::current().absolutePath();
623 } while (false);
624 dir += QDir::separator();
625 dir += "untitled."_L1;
626 dir += extension;
627 }
628
629 QScopedPointer<QFileDialog> saveAsDialog(createSaveAsDialog(fw, dir, extension));
630 if (saveAsDialog->exec() != QDialog::Accepted)
631 return false;
632
633 const QString saveFile = saveAsDialog->selectedFiles().constFirst();
634 saveAsDialog.reset(); // writeOutForm potentially shows other dialogs
635
636 fw->setFileName(saveFile);
637 return writeOutForm(fw, saveFile);
638}
639
640void QDesignerActions::saveForm()
641{
642 if (QDesignerFormWindowInterface *fw = core()->formWindowManager()->activeFormWindow()) {
643 if (saveForm(fw))
644 showStatusBarMessage(savedMessage(QFileInfo(fw->fileName()).fileName()));
645 }
646}
647
648void QDesignerActions::saveAllForms()
649{
650 QString fileNames;
651 QDesignerFormWindowManagerInterface *formWindowManager = core()->formWindowManager();
652 if (const int totalWindows = formWindowManager->formWindowCount()) {
653 const auto separator = ", "_L1;
654 for (int i = 0; i < totalWindows; ++i) {
655 QDesignerFormWindowInterface *fw = formWindowManager->formWindow(i);
656 if (fw && fw->isDirty()) {
657 formWindowManager->setActiveFormWindow(fw);
658 if (saveForm(fw)) {
659 if (!fileNames.isEmpty())
660 fileNames += separator;
661 fileNames += QFileInfo(fw->fileName()).fileName();
662 } else {
663 break;
664 }
665 }
666 }
667 }
668
669 if (!fileNames.isEmpty()) {
670 showStatusBarMessage(savedMessage(fileNames));
671 }
672}
673
674bool QDesignerActions::saveForm(QDesignerFormWindowInterface *fw)
675{
676 bool ret;
677 if (fw->fileName().isEmpty())
678 ret = saveFormAs(fw);
679 else
680 ret = writeOutForm(fw, fw->fileName());
681 return ret;
682}
683
684void QDesignerActions::closeForm()
685{
686 if (m_previewManager->previewCount()) {
687 closePreview();
688 return;
689 }
690
691 if (QDesignerFormWindowInterface *fw = core()->formWindowManager()->activeFormWindow())
692 if (QWidget *parent = fw->parentWidget()) {
693 if (QMdiSubWindow *mdiSubWindow = qobject_cast<QMdiSubWindow *>(parent->parentWidget())) {
694 mdiSubWindow->close();
695 } else {
696 parent->close();
697 }
698 }
699}
700
701void QDesignerActions::saveFormAs()
702{
703 if (QDesignerFormWindowInterface *fw = core()->formWindowManager()->activeFormWindow()) {
704 if (saveFormAs(fw))
705 showStatusBarMessage(savedMessage(fw->fileName()));
706 }
707}
708
709void QDesignerActions::saveFormAsTemplate()
710{
711 if (QDesignerFormWindowInterface *fw = core()->formWindowManager()->activeFormWindow()) {
712 SaveFormAsTemplate dlg(core(), fw, fw->window());
713 dlg.exec();
714 }
715}
716
717void QDesignerActions::notImplementedYet()
718{
719 QMessageBox::information(core()->topLevel(), tr("Designer"), tr("Feature not implemented yet!"));
720}
721
722void QDesignerActions::closePreview()
723{
724 m_previewManager->closeAllPreviews();
725}
726
727void QDesignerActions::viewCode(qdesigner_internal::UicLanguage language)
728{
729 QDesignerFormWindowInterface *fw = core()->formWindowManager()->activeFormWindow();
730 if (!fw)
731 return;
732 QString errorMessage;
733 if (!qdesigner_internal::CodeDialog::showCodeDialog(fw, language, fw, &errorMessage))
734 QMessageBox::warning(fw, tr("Code generation failed"), errorMessage);
735}
736
737bool QDesignerActions::readInForm(const QString &fileName)
738{
739 QString fn = fileName;
740
741 // First make sure that we don't have this one open already.
742 QDesignerFormWindowManagerInterface *formWindowManager = core()->formWindowManager();
743 const int totalWindows = formWindowManager->formWindowCount();
744 for (int i = 0; i < totalWindows; ++i) {
745 QDesignerFormWindowInterface *w = formWindowManager->formWindow(i);
746 if (w->fileName() == fn) {
747 w->raise();
748 formWindowManager->setActiveFormWindow(w);
749 addRecentFile(fn);
750 return true;
751 }
752 }
753
754 // Otherwise load it.
755 do {
756 QString errorMessage;
757 if (workbench()->openForm(fn, &errorMessage)) {
758 addRecentFile(fn);
759 m_openDirectory = QFileInfo(fn).absolutePath();
760 return true;
761 } else {
762 // prompt to reload
763 QMessageBox box(QMessageBox::Warning, tr("Read error"),
764 tr("%1\nDo you want to update the file location or generate a new form?").arg(errorMessage),
765 QMessageBox::Cancel, core()->topLevel());
766
767 QPushButton *updateButton = box.addButton(tr("&Update"), QMessageBox::ActionRole);
768 QPushButton *newButton = box.addButton(tr("&New Form"), QMessageBox::ActionRole);
769 box.exec();
770 if (box.clickedButton() == box.button(QMessageBox::Cancel))
771 return false;
772
773 if (box.clickedButton() == updateButton) {
774 const QString extension = uiExtension();
775 fn = QFileDialog::getOpenFileName(core()->topLevel(),
776 tr("Open Form"), m_openDirectory,
777 fileDialogFilters(extension), nullptr);
778
779 if (fn.isEmpty())
780 return false;
781 } else if (box.clickedButton() == newButton) {
782 // If the file does not exist, but its directory, is valid, open the template with the editor file name set to it.
783 // (called from command line).
784 QString newFormFileName;
785 const QFileInfo fInfo(fn);
786 if (!fInfo.exists()) {
787 // Normalize file name
788 const QString directory = fInfo.absolutePath();
789 if (QDir(directory).exists())
790 newFormFileName = directory + u'/' + fInfo.fileName();
791 }
792 showNewFormDialog(newFormFileName);
793 return false;
794 }
795 }
796 } while (true);
797 return true;
798}
799
800bool QDesignerActions::writeOutForm(QDesignerFormWindowInterface *fw, const QString &saveFile, bool check)
801{
802 Q_ASSERT(fw && !saveFile.isEmpty());
803
804 if (check) {
805 const QStringList problems = fw->checkContents();
806 if (!problems.isEmpty())
807 QMessageBox::information(fw->window(), tr("Qt Widgets Designer"), problems.join("<br>"_L1));
808 }
809
810 m_workbench->updateBackup(fw);
811
812 QSaveFile f(saveFile);
813 while (!f.open(QFile::WriteOnly)) {
814 QMessageBox box(QMessageBox::Warning,
815 tr("Save Form?"),
816 tr("Could not open file"),
817 QMessageBox::NoButton, fw);
818
819 box.setWindowModality(Qt::WindowModal);
820 box.setInformativeText(tr("The file %1 could not be opened."
821 "\nReason: %2"
822 "\nWould you like to retry or select a different file?")
823 .arg(f.fileName(), f.errorString()));
824 QPushButton *retryButton = box.addButton(QMessageBox::Retry);
825 retryButton->setDefault(true);
826 QPushButton *switchButton = box.addButton(tr("Select New File"), QMessageBox::AcceptRole);
827 QPushButton *cancelButton = box.addButton(QMessageBox::Cancel);
828 box.exec();
829
830 if (box.clickedButton() == cancelButton)
831 return false;
832 if (box.clickedButton() == switchButton) {
833 QScopedPointer<QFileDialog> saveAsDialog(createSaveAsDialog(fw, QDir::currentPath(), uiExtension()));
834 if (saveAsDialog->exec() != QDialog::Accepted)
835 return false;
836
837 const QString fileName = saveAsDialog->selectedFiles().constFirst();
838 f.setFileName(fileName);
839 fw->setFileName(fileName);
840 }
841 // loop back around...
842 }
843 f.write(formWindowContents(fw));
844 if (!f.commit()) {
845 QMessageBox box(QMessageBox::Warning, tr("Save Form"),
846 tr("Could not write file"),
847 QMessageBox::Cancel, fw);
848 box.setWindowModality(Qt::WindowModal);
849 box.setInformativeText(tr("It was not possible to write the file %1 to disk."
850 "\nReason: %2")
851 .arg(f.fileName(), f.errorString()));
852 box.exec();
853 return false;
854 }
855 addRecentFile(saveFile);
856 m_saveDirectory = QFileInfo(f.fileName()).absolutePath();
857
858 fw->setDirty(false);
859 fw->parentWidget()->setWindowModified(false);
860 return true;
861}
862
863void QDesignerActions::shutdown()
864{
865 // Follow the idea from the Mac, i.e. send the Application a close event
866 // and if it's accepted, quit.
867 QCloseEvent ev;
868 QApplication::sendEvent(qDesigner, &ev);
869 if (ev.isAccepted())
870 qDesigner->quit();
871}
872
873void QDesignerActions::activeFormWindowChanged(QDesignerFormWindowInterface *formWindow)
874{
875 const bool enable = formWindow != nullptr;
876 m_saveFormAction->setEnabled(enable);
877 m_saveFormAsAction->setEnabled(enable);
878 m_saveAllFormsAction->setEnabled(enable);
879 m_saveFormAsTemplateAction->setEnabled(enable);
880 m_closeFormAction->setEnabled(enable);
881 m_savePreviewImageAction->setEnabled(enable);
882 m_printPreviewAction->setEnabled(enable);
883
884 m_editWidgetsAction->setEnabled(enable);
885
886 m_previewFormAction->setEnabled(enable);
887 m_viewCppCodeAction->setEnabled(enable);
888 m_viewPythonCodeAction->setEnabled(enable);
889 m_styleActions->setEnabled(enable);
890}
891
892void QDesignerActions::formWindowSettingsChanged(QDesignerFormWindowInterface *fw)
893{
894 if (QDesignerFormWindow *window = m_workbench->findFormWindow(fw))
895 window->updateChanged();
896}
897
898void QDesignerActions::updateRecentFileActions()
899{
900 QStringList files = m_settings.recentFilesList();
901 auto existingEnd = std::remove_if(files.begin(), files.end(),
902 [] (const QString &f) { return !QFileInfo::exists(f); });
903 if (existingEnd != files.end()) {
904 files.erase(existingEnd, files.end());
905 m_settings.setRecentFilesList(files);
906 }
907
908 const auto recentFilesActs = m_recentFilesActions->actions();
909 qsizetype i = 0;
910 for (QAction *action : recentFilesActs) {
911 if (i < files.size()) {
912 const QString &file = files.at(i);
913 action->setText(QFileInfo(file).fileName());
914 action->setIconText(file);
915 action->setVisible(true);
916 } else {
917 action->setVisible(false);
918 }
919 ++i;
920 }
921}
922
923void QDesignerActions::openRecentForm()
924{
925 if (const QAction *action = qobject_cast<const QAction *>(sender())) {
926 if (!readInForm(action->iconText()))
927 updateRecentFileActions(); // File doesn't exist, remove it from settings
928 }
929}
930
931void QDesignerActions::clearRecentFiles()
932{
933 m_settings.setRecentFilesList(QStringList());
934 updateRecentFileActions();
935}
936
938{
939 return m_recentFilesActions;
940}
941
942void QDesignerActions::addRecentFile(const QString &fileName)
943{
944 QStringList files = m_settings.recentFilesList();
945 files.removeAll(fileName);
946 files.prepend(fileName);
947 while (files.size() > MaxRecentFiles)
948 files.removeLast();
949
950 m_settings.setRecentFilesList(files);
951 updateRecentFileActions();
952}
953
955{
956 return m_openFormAction;
957}
958
960{
961 return m_closeFormAction;
962}
963
965{
966 return m_minimizeAction;
967}
968
969void QDesignerActions::showDesignerHelp()
970{
971 QString url = AssistantClient::designerManualUrl();
972 url += "qtdesigner-manual.html"_L1;
973 showHelp(url);
974}
975
976void QDesignerActions::helpRequested(const QString &manual, const QString &document)
977{
978 QString url = AssistantClient::documentUrl(manual);
979 url += document;
980 showHelp(url);
981}
982
983void QDesignerActions::showHelp(const QString &url)
984{
985 QString errorMessage;
986 if (!m_assistantClient.showPage(url, &errorMessage))
987 QMessageBox::warning(core()->topLevel(), tr("Assistant"), errorMessage);
988}
989
990void QDesignerActions::aboutDesigner()
991{
992 VersionDialog mb(core()->topLevel());
993 mb.setWindowTitle(tr("About Qt Widgets Designer"));
994 if (mb.exec()) {
995 QMessageBox messageBox(QMessageBox::Information, u"Easter Egg"_s,
996 u"Easter Egg"_s, QMessageBox::Ok, core()->topLevel());
997 messageBox.setInformativeText(u"The Easter Egg has been removed."_s);
998 messageBox.exec();
999 }
1000}
1001
1003{
1004 return m_editWidgetsAction;
1005}
1006
1007void QDesignerActions::showWidgetSpecificHelp()
1008{
1009 const QString helpId = core()->integration()->contextHelpId();
1010
1011 if (helpId.isEmpty()) {
1012 showDesignerHelp();
1013 return;
1014 }
1015
1016 QString errorMessage;
1017 const bool rc = m_assistantClient.activateIdentifier(helpId, &errorMessage);
1018 if (!rc)
1019 QMessageBox::warning(core()->topLevel(), tr("Assistant"), errorMessage);
1020}
1021
1022void QDesignerActions::updateCloseAction()
1023{
1024 if (m_previewManager->previewCount()) {
1025 m_closeFormAction->setText(tr("&Close Preview"));
1026 } else {
1027 m_closeFormAction->setText(tr("&Close"));
1028 }
1029}
1030
1031void QDesignerActions::backupForms()
1032{
1033 const int count = m_workbench->formWindowCount();
1034 if (!count || !ensureBackupDirectories())
1035 return;
1036
1037
1038 QMap<QString, QString> backupMap;
1039 QDir backupDir(m_backupPath);
1040 for (int i = 0; i < count; ++i) {
1041 QDesignerFormWindow *fw = m_workbench->formWindow(i);
1042 QDesignerFormWindowInterface *fwi = fw->editor();
1043
1044 QString formBackupName = m_backupPath + "/backup"_L1 + QString::number(i) + ".bak"_L1;
1045
1046 QString fwn = QDir::toNativeSeparators(fwi->fileName());
1047 if (fwn.isEmpty())
1048 fwn = fw->windowTitle();
1049
1050 backupMap.insert(fwn, formBackupName);
1051
1052 bool ok = false;
1053 QSaveFile file(formBackupName);
1054 if (file.open(QFile::WriteOnly)) {
1055 file.write(formWindowContents(fw->editor(), backupDir));
1056 ok = file.commit();
1057 }
1058 if (!ok) {
1059 backupMap.remove(fwn);
1060 qdesigner_internal::designerWarning(tr("The backup file %1 could not be written: %2").
1061 arg(QDir::toNativeSeparators(file.fileName()),
1062 file.errorString()));
1063 }
1064 }
1065
1066 if (!backupMap.isEmpty())
1067 m_settings.setBackup(backupMap);
1068}
1069
1070static QString fixResourceFileBackupPath(const QDesignerFormWindowInterface *fwi,
1071 const QDir& backupDir)
1072{
1073 const QString content = fwi->contents();
1074 QDomDocument domDoc(u"backup"_s);
1075 if(!domDoc.setContent(content))
1076 return content;
1077
1078 const QDomNodeList list = domDoc.elementsByTagName(u"resources"_s);
1079 if (list.isEmpty())
1080 return content;
1081
1082 for (int i = 0; i < list.count(); i++) {
1083 const QDomNode node = list.at(i);
1084 if (!node.isNull()) {
1085 const QDomElement element = node.toElement();
1086 if (!element.isNull() && element.tagName() == "resources"_L1) {
1087 QDomNode childNode = element.firstChild();
1088 while (!childNode.isNull()) {
1089 QDomElement childElement = childNode.toElement();
1090 if (!childElement.isNull() && childElement.tagName() == "include"_L1) {
1091 const QString attr = childElement.attribute(u"location"_s);
1092 const QString path = fwi->absoluteDir().absoluteFilePath(attr);
1093 childElement.setAttribute(u"location"_s, backupDir.relativeFilePath(path));
1094 }
1095 childNode = childNode.nextSibling();
1096 }
1097 }
1098 }
1099 }
1100
1101
1102 return domDoc.toString();
1103}
1104
1105QRect QDesignerActions::fixDialogRect(const QRect &rect) const
1106{
1107 QRect frameGeometry;
1108 const QRect availableGeometry = core()->topLevel()->screen()->geometry();
1109
1110 if (workbench()->mode() == DockedMode) {
1111 frameGeometry = core()->topLevel()->frameGeometry();
1112 } else
1113 frameGeometry = availableGeometry;
1114
1115 QRect dlgRect = rect;
1116 dlgRect.moveCenter(frameGeometry.center());
1117
1118 // make sure that parts of the dialog are not outside of screen
1119 dlgRect.moveBottom(qMin(dlgRect.bottom(), availableGeometry.bottom()));
1120 dlgRect.moveRight(qMin(dlgRect.right(), availableGeometry.right()));
1121 dlgRect.moveLeft(qMax(dlgRect.left(), availableGeometry.left()));
1122 dlgRect.moveTop(qMax(dlgRect.top(), availableGeometry.top()));
1123
1124 return dlgRect;
1125}
1126
1127void QDesignerActions::showStatusBarMessage(const QString &message) const
1128{
1129 if (workbench()->mode() == DockedMode) {
1130 QStatusBar *bar = qDesigner->mainWindow()->statusBar();
1131 if (bar && !bar->isHidden())
1132 bar->showMessage(message, 3000);
1133 }
1134}
1135
1137{
1138 m_bringAllToFrontSeparator->setVisible(visible);
1139 m_bringAllToFrontAction->setVisible(visible);
1140}
1141
1143{
1144 m_windowListSeparatorAction->setVisible(visible);
1145}
1146
1147bool QDesignerActions::ensureBackupDirectories() {
1148
1149 if (m_backupPath.isEmpty()) // create names
1150 m_backupPath = qdesigner_internal::dataDirectory() + u"/backup"_s;
1151
1152 // ensure directories
1153 const QDir backupDir(m_backupPath);
1154
1155 if (!backupDir.exists()) {
1156 if (!backupDir.mkpath(m_backupPath)) {
1157 qdesigner_internal::designerWarning(tr("The backup directory %1 could not be created.")
1158 .arg(QDir::toNativeSeparators(m_backupPath)));
1159 return false;
1160 }
1161 }
1162 return true;
1163}
1164
1165void QDesignerActions::showPreferencesDialog()
1166{
1167 {
1168 PreferencesDialog preferencesDialog(workbench()->core(), m_core->topLevel());
1169 preferencesDialog.exec();
1170 } // Make sure the preference dialog is destroyed before switching UI modes.
1171 m_workbench->applyUiSettings();
1172}
1173
1174void QDesignerActions::showAppFontDialog()
1175{
1176 if (!m_appFontDialog) // Might get deleted when switching ui modes
1177 m_appFontDialog = new AppFontDialog(core()->topLevel());
1178 m_appFontDialog->show();
1179 m_appFontDialog->raise();
1180}
1181
1182QPixmap QDesignerActions::createPreviewPixmap(QDesignerFormWindowInterface *fw)
1183{
1184 const QCursor oldCursor = core()->topLevel()->cursor();
1185 core()->topLevel()->setCursor(Qt::WaitCursor);
1186
1187 QString errorMessage;
1188 const QPixmap pixmap = m_previewManager->createPreviewPixmap(fw, QString(), &errorMessage);
1189 core()->topLevel()->setCursor(oldCursor);
1190 if (pixmap.isNull()) {
1191 QMessageBox::warning(fw, tr("Preview failed"), errorMessage);
1192 }
1193 return pixmap;
1194}
1195
1196qdesigner_internal::PreviewConfiguration QDesignerActions::previewConfiguration()
1197{
1198 qdesigner_internal::PreviewConfiguration pc;
1200 if (settings.isCustomPreviewConfigurationEnabled())
1201 pc = settings.customPreviewConfiguration();
1202 return pc;
1203}
1204
1205void QDesignerActions::savePreviewImage()
1206{
1207 const char *format = "png";
1208
1209 QDesignerFormWindowInterface *fw = core()->formWindowManager()->activeFormWindow();
1210 if (!fw)
1211 return;
1212
1213 QImage image;
1214 const QString extension = QString::fromLatin1(format);
1215 const QString filter = tr("Image files (*.%1)").arg(extension);
1216
1217 QString suggestion = fw->fileName();
1218 if (!suggestion.isEmpty())
1219 suggestion = QFileInfo(suggestion).baseName() + u'.' + extension;
1220
1221 QFileDialog dialog(fw, tr("Save Image"), suggestion, filter);
1222 dialog.setAcceptMode(QFileDialog::AcceptSave);
1223 dialog.setDefaultSuffix(extension);
1224
1225 do {
1226 if (dialog.exec() != QDialog::Accepted)
1227 break;
1228 const QString fileName = dialog.selectedFiles().constFirst();
1229
1230 if (image.isNull()) {
1231 const QPixmap pixmap = createPreviewPixmap(fw);
1232 if (pixmap.isNull())
1233 break;
1234
1235 image = pixmap.toImage();
1236 }
1237
1238 if (image.save(fileName, format)) {
1239 showStatusBarMessage(tr("Saved image %1.").arg(QFileInfo(fileName).fileName()));
1240 break;
1241 }
1242
1243 QMessageBox box(QMessageBox::Warning, tr("Save Image"),
1244 tr("The file %1 could not be written.").arg( fileName),
1245 QMessageBox::Retry|QMessageBox::Cancel, fw);
1246 if (box.exec() == QMessageBox::Cancel)
1247 break;
1248 } while (true);
1249}
1250
1251void QDesignerActions::formWindowCountChanged()
1252{
1253 const bool enabled = m_core->formWindowManager()->formWindowCount() == 0;
1254 /* Disable the application font action if there are form windows open
1255 * as the reordering of the fonts sets font properties to 'changed'
1256 * and overloaded fonts are not updated. */
1257 static const QString disabledTip = tr("Please close all forms to enable the loading of additional fonts.");
1258 m_appFontAction->setEnabled(enabled);
1259 m_appFontAction->setStatusTip(enabled ? QString() : disabledTip);
1260}
1261
1262void QDesignerActions::printPreviewImage()
1263{
1264#ifdef HAS_PRINTER
1265 QDesignerFormWindowInterface *fw = core()->formWindowManager()->activeFormWindow();
1266 if (!fw)
1267 return;
1268
1269 if (!m_printer)
1270 m_printer = new QPrinter(QPrinter::HighResolution);
1271
1272 m_printer->setFullPage(false);
1273
1274 // Grab the image to be able to a suggest suitable orientation
1275 const QPixmap pixmap = createPreviewPixmap(fw);
1276 if (pixmap.isNull())
1277 return;
1278
1279 const QSizeF pixmapSize = pixmap.size();
1280
1281 m_printer->setPageOrientation(pixmapSize.width() > pixmapSize.height() ?
1282 QPageLayout::Landscape : QPageLayout::Portrait);
1283
1284 // Printer parameters
1285 QPrintDialog dialog(m_printer, fw);
1286 if (!dialog.exec())
1287 return;
1288
1289 const QCursor oldCursor = core()->topLevel()->cursor();
1290 core()->topLevel()->setCursor(Qt::WaitCursor);
1291 // Estimate of required scaling to make form look the same on screen and printer.
1292 const double suggestedScaling = static_cast<double>(m_printer->physicalDpiX()) / static_cast<double>(fw->physicalDpiX());
1293
1294 QPainter painter(m_printer);
1295 painter.setRenderHint(QPainter::SmoothPixmapTransform);
1296
1297 // Clamp to page
1298 const QRectF page = painter.viewport();
1299 const double maxScaling = qMin(page.size().width() / pixmapSize.width(), page.size().height() / pixmapSize.height());
1300 const double scaling = qMin(suggestedScaling, maxScaling);
1301
1302 const double xOffset = page.left() + qMax(0.0, (page.size().width() - scaling * pixmapSize.width()) / 2.0);
1303 const double yOffset = page.top() + qMax(0.0, (page.size().height() - scaling * pixmapSize.height()) / 2.0);
1304
1305 // Draw.
1306 painter.translate(xOffset, yOffset);
1307 painter.scale(scaling, scaling);
1308 painter.drawPixmap(0, 0, pixmap);
1309 core()->topLevel()->setCursor(oldCursor);
1310
1311 showStatusBarMessage(tr("Printed %1.").arg(QFileInfo(fw->fileName()).fileName()));
1312#endif // HAS_PRINTER
1313}
1314
1315QT_END_NAMESPACE
QActionGroup * helpActions() const
QActionGroup * fileActions() const
QActionGroup * settingsActions() const
QAction * minimizeAction() const
QActionGroup * styleActions() const
QActionGroup * editActions() const
bool writeOutForm(QDesignerFormWindowInterface *formWindow, const QString &fileName, bool check=true)
QString uiExtension() const
QActionGroup * formActions() const
bool openForm(QWidget *parent)
QDesignerFormEditorInterface * core() const
QActionGroup * toolActions() const
QAction * previewFormAction() const
QActionGroup * windowActions() const
bool readInForm(const QString &fileName)
QAction * closeFormAction() const
void setWindowListSeparatorVisible(bool visible)
bool saveForm(QDesignerFormWindowInterface *fw)
QAction * editWidgets() const
QAction * openFormAction() const
QAction * viewCodeAction() const
QDesignerWorkbench * workbench() const
void helpRequested(const QString &manual, const QString &document)
static const char * defaultToolbarPropertyName
void setBringAllToFrontVisible(bool visible)
QActionGroup * recentFilesActions() const
QDesignerFormWindowInterface * editor() const
QDesignerSettings(QDesignerFormEditorInterface *core)
QDesignerFormEditorInterface * core() const
QDesignerFormWindow * formWindow(int index) const
void updateBackup(QDesignerFormWindowInterface *fwi)
friend class QWidget
Definition qpainter.h:431
Auxiliary methods to store/retrieve settings.
#define qDesigner
Definition qdesigner.h:12
static QByteArray formWindowContents(const QDesignerFormWindowInterface *fw, std::optional< QDir > alternativeDir={})
static QString fixResourceFileBackupPath(const QDesignerFormWindowInterface *fwi, const QDir &backupDir)
static QString fileDialogFilters(const QString &extension)
static QString savedMessage(const QString &fileName)
static QAction * createSeparator(QObject *parent)
QFileDialog * createSaveAsDialog(QWidget *parent, const QString &dir, const QString &extension)
static QActionGroup * createActionGroup(QObject *parent, bool exclusive=false)
static void fixActionContext(const QList< QAction * > &actions)