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// designer
30#include "qdesigner.h"
31#include "qdesigner_actions.h"
32#include "qdesigner_server.h"
33#include "qdesigner_settings.h"
34#include "qdesigner_workbench.h"
35#include "mainwindow.h"
36
37#include <qdesigner_propertysheet_p.h>
38
39#include <QtGui/qevent.h>
40#include <QtWidgets/qmessagebox.h>
41#include <QtGui/qicon.h>
42#include <QtWidgets/qerrormessage.h>
43#include <QtCore/qmetaobject.h>
44#include <QtCore/qfile.h>
45#include <QtCore/qlibraryinfo.h>
46#include <QtCore/qlocale.h>
47#include <QtCore/qtimer.h>
48#include <QtCore/qtranslator.h>
49#include <QtCore/qfileinfo.h>
50#include <QtCore/qdebug.h>
51#include <QtCore/qcommandlineparser.h>
52#include <QtCore/qcommandlineoption.h>
53
54#include <QtDesigner/QDesignerComponents>
55
56QT_BEGIN_NAMESPACE
57
58static const char *designerApplicationName = "Designer";
59static const char designerDisplayName[] = "Qt Designer";
60static const char *designerWarningPrefix = "Designer: ";
61static QtMessageHandler previousMessageHandler = nullptr;
62
63static void designerMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
64{
65 // Only Designer warnings are displayed as box
66 QDesigner *designerApp = qDesigner;
67 if (type != QtWarningMsg || !designerApp || !msg.startsWith(s: QLatin1String(designerWarningPrefix))) {
68 previousMessageHandler(type, context, msg);
69 return;
70 }
71 designerApp->showErrorMessage(message: msg);
72}
73
74QDesigner::QDesigner(int &argc, char **argv)
75 : QApplication(argc, argv),
76 m_server(nullptr),
77 m_client(nullptr),
78 m_workbench(0), m_suppressNewFormShow(false)
79{
80 setOrganizationName(QStringLiteral("QtProject"));
81 QGuiApplication::setApplicationDisplayName(QLatin1String(designerDisplayName));
82 setApplicationName(QLatin1String(designerApplicationName));
83 QDesignerComponents::initializeResources();
84
85#if !defined(Q_OS_OSX) && !defined(Q_OS_WIN)
86 setWindowIcon(QIcon(QStringLiteral(":/qt-project.org/designer/images/designer.png")));
87#endif
88}
89
90QDesigner::~QDesigner()
91{
92 delete m_workbench;
93 delete m_server;
94 delete m_client;
95}
96
97void QDesigner::showErrorMessage(const QString &message)
98{
99 // strip the prefix
100 const QString qMessage =
101 message.right(n: message.size() - int(qstrlen(str: designerWarningPrefix)));
102 // If there is no main window yet, just store the message.
103 // The QErrorMessage would otherwise be hidden by the main window.
104 if (m_mainWindow) {
105 showErrorMessageBox(qMessage);
106 } else {
107 const QMessageLogContext emptyContext;
108 previousMessageHandler(QtWarningMsg, emptyContext, message); // just in case we crash
109 m_initializationErrors += qMessage;
110 m_initializationErrors += QLatin1Char('\n');
111 }
112}
113
114void QDesigner::showErrorMessageBox(const QString &msg)
115{
116 // Manually suppress consecutive messages.
117 // This happens if for example sth is wrong with custom widget creation.
118 // The same warning will be displayed by Widget box D&D and form Drop
119 // while trying to create instance.
120 if (m_errorMessageDialog && m_lastErrorMessage == msg)
121 return;
122
123 if (!m_errorMessageDialog) {
124 m_lastErrorMessage.clear();
125 m_errorMessageDialog = new QErrorMessage(m_mainWindow);
126 const QString title = QCoreApplication::translate(context: "QDesigner", key: "%1 - warning").arg(a: QLatin1String(designerApplicationName));
127 m_errorMessageDialog->setWindowTitle(title);
128 m_errorMessageDialog->setMinimumSize(QSize(600, 250));
129 m_errorMessageDialog->setWindowFlags(m_errorMessageDialog->windowFlags() & ~Qt::WindowContextHelpButtonHint);
130 }
131 m_errorMessageDialog->showMessage(message: msg);
132 m_lastErrorMessage = msg;
133}
134
135QDesignerWorkbench *QDesigner::workbench() const
136{
137 return m_workbench;
138}
139
140QDesignerServer *QDesigner::server() const
141{
142 return m_server;
143}
144
145static void showHelp(QCommandLineParser &parser, const QString &errorMessage = QString())
146{
147 QString text;
148 QTextStream str(&text);
149 str << "<html><head/><body>";
150 if (!errorMessage.isEmpty())
151 str << "<p>" << errorMessage << "</p>";
152 str << "<pre>" << parser.helpText().toHtmlEscaped() << "</pre></body></html>";
153 QMessageBox box(errorMessage.isEmpty() ? QMessageBox::Information : QMessageBox::Warning,
154 QGuiApplication::applicationDisplayName(), text,
155 QMessageBox::Ok);
156 box.setTextInteractionFlags(Qt::TextBrowserInteraction);
157 box.exec();
158}
159
160struct Options
161{
162 QStringList files;
163 QString resourceDir{QLibraryInfo::location(QLibraryInfo::TranslationsPath)};
164 bool server{false};
165 quint16 clientPort{0};
166 bool enableInternalDynamicProperties{false};
167};
168
169static inline QDesigner::ParseArgumentsResult
170 parseDesignerCommandLineArguments(QCommandLineParser &parser, Options *options,
171 QString *errorMessage)
172{
173 parser.setApplicationDescription(QStringLiteral("Qt Designer ")
174 + QLatin1String(QT_VERSION_STR)
175 + QLatin1String("\n\nUI designer for QWidget-based applications."));
176 const QCommandLineOption helpOption = parser.addHelpOption();
177 parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
178 const QCommandLineOption serverOption(QStringLiteral("server"),
179 QStringLiteral("Server mode"));
180 parser.addOption(commandLineOption: serverOption);
181 const QCommandLineOption clientOption(QStringLiteral("client"),
182 QStringLiteral("Client mode"),
183 QStringLiteral("port"));
184 parser.addOption(commandLineOption: clientOption);
185 const QCommandLineOption resourceDirOption(QStringLiteral("resourcedir"),
186 QStringLiteral("Resource directory"),
187 QStringLiteral("directory"));
188 parser.addOption(commandLineOption: resourceDirOption);
189 const QCommandLineOption internalDynamicPropertyOption(QStringLiteral("enableinternaldynamicproperties"),
190 QStringLiteral("Enable internal dynamic properties"));
191 parser.addOption(commandLineOption: internalDynamicPropertyOption);
192 const QCommandLineOption noScalingOption(QStringLiteral("no-scaling"),
193 QStringLiteral("Disable High DPI scaling"));
194 parser.addOption(commandLineOption: noScalingOption);
195
196 parser.addPositionalArgument(QStringLiteral("files"),
197 QStringLiteral("The UI files to open."));
198
199 if (!parser.parse(arguments: QCoreApplication::arguments())) {
200 *errorMessage = parser.errorText();
201 return QDesigner::ParseArgumentsError;
202 }
203
204 if (parser.isSet(option: helpOption))
205 return QDesigner::ParseArgumentsHelpRequested;
206 options->server = parser.isSet(option: serverOption);
207 if (parser.isSet(option: clientOption)) {
208 bool ok;
209 options->clientPort = parser.value(option: clientOption).toUShort(ok: &ok);
210 if (!ok) {
211 *errorMessage = QStringLiteral("Non-numeric argument specified for -client");
212 return QDesigner::ParseArgumentsError;
213 }
214 }
215 if (parser.isSet(option: resourceDirOption))
216 options->resourceDir = parser.value(option: resourceDirOption);
217 options->enableInternalDynamicProperties = parser.isSet(option: internalDynamicPropertyOption);
218 options->files = parser.positionalArguments();
219 return QDesigner::ParseArgumentsSuccess;
220}
221
222QDesigner::ParseArgumentsResult QDesigner::parseCommandLineArguments()
223{
224 QString errorMessage;
225 Options options;
226 QCommandLineParser parser;
227 const ParseArgumentsResult result = parseDesignerCommandLineArguments(parser, options: &options, errorMessage: &errorMessage);
228 if (result != ParseArgumentsSuccess) {
229 showHelp(parser, errorMessage);
230 return result;
231 }
232 // initialize the sub components
233 if (options.clientPort)
234 m_client = new QDesignerClient(options.clientPort, this);
235 if (options.server) {
236 m_server = new QDesignerServer();
237 printf(format: "%d\n", m_server->serverPort());
238 fflush(stdout);
239 }
240 if (options.enableInternalDynamicProperties)
241 QDesignerPropertySheet::setInternalDynamicPropertiesEnabled(true);
242
243 const QString localSysName = QLocale::system().name();
244 QScopedPointer<QTranslator> designerTranslator(new QTranslator(this));
245 if (designerTranslator->load(QStringLiteral("designer_") + localSysName, directory: options.resourceDir)) {
246 installTranslator(messageFile: designerTranslator.take());
247 QScopedPointer<QTranslator> qtTranslator(new QTranslator(this));
248 if (qtTranslator->load(QStringLiteral("qt_") + localSysName, directory: options.resourceDir))
249 installTranslator(messageFile: qtTranslator.take());
250 }
251
252 m_workbench = new QDesignerWorkbench();
253
254 emit initialized();
255 previousMessageHandler = qInstallMessageHandler(designerMessageHandler); // Warn when loading faulty forms
256 Q_ASSERT(previousMessageHandler);
257
258 m_suppressNewFormShow = m_workbench->readInBackup();
259
260 if (!options.files.isEmpty()) {
261 const QStringList::const_iterator cend = options.files.constEnd();
262 for (QStringList::const_iterator it = options.files.constBegin(); it != cend; ++it) {
263 // Ensure absolute paths for recent file list to be unique
264 QString fileName = *it;
265 const QFileInfo fi(fileName);
266 if (fi.exists() && fi.isRelative())
267 fileName = fi.absoluteFilePath();
268 m_workbench->readInForm(fileName);
269 }
270 }
271 if ( m_workbench->formWindowCount())
272 m_suppressNewFormShow = true;
273
274 // Show up error box with parent now if something went wrong
275 if (m_initializationErrors.isEmpty()) {
276 if (!m_suppressNewFormShow && QDesignerSettings(m_workbench->core()).showNewFormOnStartup())
277 QTimer::singleShot(interval: 100, receiver: this, slot: &QDesigner::callCreateForm); // won't show anything if suppressed
278 } else {
279 showErrorMessageBox(msg: m_initializationErrors);
280 m_initializationErrors.clear();
281 }
282 return result;
283}
284
285bool QDesigner::event(QEvent *ev)
286{
287 bool eaten;
288 switch (ev->type()) {
289 case QEvent::FileOpen:
290 // Set it true first since, if it's a Qt 3 form, the messagebox from convert will fire the timer.
291 m_suppressNewFormShow = true;
292 if (!m_workbench->readInForm(fileName: static_cast<QFileOpenEvent *>(ev)->file()))
293 m_suppressNewFormShow = false;
294 eaten = true;
295 break;
296 case QEvent::Close: {
297 QCloseEvent *closeEvent = static_cast<QCloseEvent *>(ev);
298 closeEvent->setAccepted(m_workbench->handleClose());
299 if (closeEvent->isAccepted()) {
300 // We're going down, make sure that we don't get our settings saved twice.
301 if (m_mainWindow)
302 m_mainWindow->setCloseEventPolicy(MainWindowBase::AcceptCloseEvents);
303 eaten = QApplication::event(ev);
304 }
305 eaten = true;
306 break;
307 }
308 default:
309 eaten = QApplication::event(ev);
310 break;
311 }
312 return eaten;
313}
314
315void QDesigner::setMainWindow(MainWindowBase *tw)
316{
317 m_mainWindow = tw;
318}
319
320MainWindowBase *QDesigner::mainWindow() const
321{
322 return m_mainWindow;
323}
324
325void QDesigner::callCreateForm()
326{
327 if (!m_suppressNewFormShow)
328 m_workbench->actionManager()->createForm();
329}
330
331QT_END_NAMESPACE
332

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