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

source code of qttools/src/designer/src/designer/qdesigner_actions.cpp