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