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