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.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// Qt-Security score:significant reason:default
4
5// designer
6#include "qdesigner.h"
11#include "mainwindow.h"
12#include <QtDesigner/abstractintegration.h>
13#include <QtDesigner/abstractformeditor.h>
14#include <qdesigner_propertysheet_p.h>
15
16#include <QtGui/qevent.h>
17#include <QtWidgets/qmessagebox.h>
18#include <QtGui/qicon.h>
19#include <QtWidgets/qerrormessage.h>
20#include <QtCore/qmetaobject.h>
21#include <QtCore/qdir.h>
22#include <QtCore/qfile.h>
23#include <QtCore/qlibraryinfo.h>
24#include <QtCore/qlocale.h>
25#include <QtCore/qtextstream.h>
26#include <QtCore/qtimer.h>
27#include <QtCore/qtranslator.h>
28#include <QtCore/qfileinfo.h>
29#include <QtCore/qdebug.h>
30#include <QtCore/qcommandlineparser.h>
31#include <QtCore/qcommandlineoption.h>
32#include <QtCore/qvariant.h>
33
34#include <QtDesigner/QDesignerComponents>
35
36QT_BEGIN_NAMESPACE
37
38using namespace Qt::StringLiterals;
39
40static constexpr auto designerApplicationName = "Designer"_L1;
41static constexpr auto designerDisplayName = "Qt Widgets Designer"_L1;
42static constexpr auto designerWarningPrefix = "Designer: "_L1;
44
45static void designerMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
46{
47 // Only Designer warnings are displayed as box
48 QDesigner *designerApp = qDesigner;
49 if (type != QtWarningMsg || !designerApp || !msg.startsWith(designerWarningPrefix)) {
50 previousMessageHandler(type, context, msg);
51 return;
52 }
53 designerApp->showErrorMessage(msg);
54}
55
56QDesigner::QDesigner(int &argc, char **argv)
58{
59 setOrganizationName(u"QtProject"_s);
60 QGuiApplication::setApplicationDisplayName(designerDisplayName);
61 setApplicationName(designerApplicationName);
62 QDesignerComponents::initializeResources();
63
64#if !defined(Q_OS_MACOS) && !defined(Q_OS_WIN)
65 setWindowIcon(QIcon(u":/qt-project.org/designer/images/designer.png"_s));
66#endif
67}
68
70{
71 delete m_workbench;
72 delete m_server;
73 delete m_client;
74}
75
76void QDesigner::showErrorMessage(const QString &message)
77{
78 // strip the prefix
79 const QString qMessage =
80 message.right(message.size() - int(designerWarningPrefix.size()));
81 // If there is no main window yet, just store the message.
82 // The QErrorMessage would otherwise be hidden by the main window.
83 if (m_mainWindow) {
84 showErrorMessageBox(qMessage);
85 } else {
86 const QMessageLogContext emptyContext;
87 previousMessageHandler(QtWarningMsg, emptyContext, message); // just in case we crash
88 m_initializationErrors += qMessage;
89 m_initializationErrors += u'\n';
90 }
91}
92
93void QDesigner::showErrorMessageBox(const QString &msg)
94{
95 // Manually suppress consecutive messages.
96 // This happens if for example sth is wrong with custom widget creation.
97 // The same warning will be displayed by Widget box D&D and form Drop
98 // while trying to create instance.
99 if (m_errorMessageDialog && m_lastErrorMessage == msg)
100 return;
101
102 if (!m_errorMessageDialog) {
103 m_lastErrorMessage.clear();
104 m_errorMessageDialog = new QErrorMessage(m_mainWindow);
105 const QString title = QCoreApplication::translate("QDesigner", "%1 - warning").arg(designerApplicationName);
106 m_errorMessageDialog->setWindowTitle(title);
107 m_errorMessageDialog->setMinimumSize(QSize(600, 250));
108 }
109 m_errorMessageDialog->showMessage(msg);
110 m_lastErrorMessage = msg;
111}
112
114{
115 return m_workbench;
116}
117
118static void showHelp(QCommandLineParser &parser, const QString &errorMessage = QString())
119{
120 QString text;
121 QTextStream str(&text);
122 str << "<html><head/><body>";
123 if (!errorMessage.isEmpty())
124 str << "<p>" << errorMessage << "</p>";
125 str << "<pre>" << parser.helpText().toHtmlEscaped() << "</pre></body></html>";
126 QMessageBox box(errorMessage.isEmpty() ? QMessageBox::Information : QMessageBox::Warning,
127 QGuiApplication::applicationDisplayName(), text,
128 QMessageBox::Ok);
129 box.setTextInteractionFlags(Qt::TextBrowserInteraction);
130 box.exec();
131}
132
133static inline QDesigner::ParseArgumentsResult
134 parseDesignerCommandLineArguments(QCommandLineParser &parser, Options *options,
135 QString *errorMessage)
136{
137 parser.setApplicationDescription(u"Qt Widgets Designer " QT_VERSION_STR "\n\nUI designer for QWidget-based applications."_s);
138 const QCommandLineOption helpOption = parser.addHelpOption();
139 parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
140 const QCommandLineOption localSocketClientOption(u"local-client"_s,
141 u"Local socket client mode"_s,
142 u"server_name"_s);
143 parser.addOption(localSocketClientOption);
144 const QCommandLineOption tcpServerOption(u"server"_s, u"TCP Server mode (deprecated)"_s);
145 parser.addOption(tcpServerOption);
146 const QCommandLineOption tcpClientOption(u"client"_s, u"TCP Client mode (deprecated)"_s,
147 u"port"_s);
148 parser.addOption(tcpClientOption);
149 const QCommandLineOption webHelpOption(u"web-help"_s, u"Use the Web documentation"_s);
150 parser.addOption(webHelpOption);
151 const QCommandLineOption pythonHelpOption(u"python-help"_s, u"Use the Python documentation"_s);
152 parser.addOption(pythonHelpOption);
153 const QCommandLineOption resourceDirOption(u"resourcedir"_s,
154 u"Resource directory"_s,
155 u"directory"_s);
156 parser.addOption(resourceDirOption);
157 const QCommandLineOption internalDynamicPropertyOption(u"enableinternaldynamicproperties"_s,
158 u"Enable internal dynamic properties"_s);
159 parser.addOption(internalDynamicPropertyOption);
160 const QCommandLineOption pluginPathsOption(u"plugin-path"_s,
161 u"Default plugin path list"_s,
162 u"path"_s);
163 parser.addOption(pluginPathsOption);
164
165 const QCommandLineOption qtVersionOption(u"qt-version"_s,
166 u"Qt Version for writing .ui files"_s,
167 u"version"_s);
168 parser.addOption(qtVersionOption);
169
170 parser.addPositionalArgument(u"files"_s,
171 u"The UI files to open."_s);
172
173 if (!parser.parse(QCoreApplication::arguments())) {
174 *errorMessage = parser.errorText();
175 return QDesigner::ParseArgumentsError;
176 }
177
178 if (parser.isSet(helpOption))
179 return QDesigner::ParseArgumentsHelpRequested;
180 // There is no way to retrieve the complete help text from QCommandLineParser,
181 // so, call process() to display it.
182 if (parser.isSet(u"help-all"_s))
183 parser.process(QCoreApplication::arguments()); // exits
184 options->server = parser.isSet(tcpServerOption);
185 if (parser.isSet(localSocketClientOption)) {
186 options->localSocketServerName = parser.value(localSocketClientOption);
187 } else if (parser.isSet(tcpClientOption)) {
188 bool ok = false;
189 options->tcpClientPort = parser.value(tcpClientOption).toUShort(&ok);
190 if (!ok) {
191 *errorMessage = u"Non-numeric argument specified for -client"_s;
192 return QDesigner::ParseArgumentsError;
193 }
194 }
195 if (parser.isSet(webHelpOption))
197 else if (parser.isSet(pythonHelpOption))
199 if (parser.isSet(resourceDirOption))
200 options->resourceDir = parser.value(resourceDirOption);
201 const auto pluginPathValues = parser.values(pluginPathsOption);
202 for (const auto &pluginPath : pluginPathValues)
203 options->pluginPaths.append(pluginPath.split(QDir::listSeparator(), Qt::SkipEmptyParts));
204
205 if (parser.isSet(qtVersionOption))
206 options->qtVersion = QVersionNumber::fromString(parser.value(qtVersionOption));
207
208 options->enableInternalDynamicProperties = parser.isSet(internalDynamicPropertyOption);
209 options->files = parser.positionalArguments();
210 return QDesigner::ParseArgumentsSuccess;
211}
212
213#if QT_CONFIG(translation)
214static bool loadTranslation(const QStringList &trPaths, std::unique_ptr<QTranslator> &tr,
215 const QString &catalog)
216{
217 for (const QString &trPath : trPaths) {
218 if (tr->load(QLocale(), catalog, u"_"_s, trPath))
219 return true;
220 }
221 return false;
222}
223#endif // QT_CONFIG(translation)
224
226{
227 QString errorMessage;
228 Options options;
229 QCommandLineParser parser;
230 const ParseArgumentsResult result = parseDesignerCommandLineArguments(parser, &options, &errorMessage);
231 if (result != ParseArgumentsSuccess) {
232 showHelp(parser, errorMessage);
233 return result;
234 }
235 // initialize the sub components
236 if (!options.localSocketServerName.isEmpty())
237 m_client = new QDesignerLocalSocketClient(options.localSocketServerName, this);
238 else if (options.tcpClientPort != 0)
239 m_client = new QDesignerTcpClient(options.tcpClientPort, this);
240 if (options.server) {
241 auto *server = new QDesignerTcpServer();
242 m_server = server;
243 printf("%d\n", server->serverPort());
244 fflush(stdout);
245 }
246 if (options.enableInternalDynamicProperties)
247 QDesignerPropertySheet::setInternalDynamicPropertiesEnabled(true);
248
249#if QT_CONFIG(translation)
250 const QStringList &trPaths = options.resourceDir.isEmpty()
251 ? QLibraryInfo::paths(QLibraryInfo::TranslationsPath)
252 : QStringList{ options.resourceDir };
253 auto designerTranslator = std::make_unique<QTranslator>(this);
254 auto qtTranslator = std::make_unique<QTranslator>(this);
255 if (loadTranslation(trPaths, designerTranslator, u"designer"_s)
256 && loadTranslation(trPaths, qtTranslator, u"qtbase"_s)) {
257 installTranslator(designerTranslator.release());
258 installTranslator(qtTranslator.release());
259 }
260#endif // QT_CONFIG(translation)
261
262 m_workbench = new QDesignerWorkbench(options);
263
264 emit initialized();
265 previousMessageHandler = qInstallMessageHandler(designerMessageHandler); // Warn when loading faulty forms
266 Q_ASSERT(previousMessageHandler);
267
268 bool suppressNewFormShow = m_workbench->readInBackup();
269
270 for (auto fileName : std::as_const(options.files)) {
271 // Ensure absolute paths for recent file list to be unique
272 const QFileInfo fi(fileName);
273 if (fi.exists() && fi.isRelative())
274 fileName = fi.absoluteFilePath();
275 m_workbench->readInForm(fileName);
276 }
277
278 if (m_workbench->formWindowCount() > 0)
279 suppressNewFormShow = true;
280
281 if (options.qtVersion.has_value()) {
282#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
283 m_workbench->core()->integration()->setQtVersion(options.qtVersion.value());
284#else
285 auto version = QVariant::fromValue(options.qtVersion.value());
286 m_workbench->core()->integration()->setProperty("qtVersion", version);
287#endif
288 }
289
290 // Show up error box with parent now if something went wrong
291 if (m_initializationErrors.isEmpty()) {
292 if (!isServerOrClientEnabled() && !suppressNewFormShow)
293 m_workbench->showNewForm();
294 } else {
295 showErrorMessageBox(m_initializationErrors);
296 m_initializationErrors.clear();
297 }
298 return result;
299}
300
302{
303 return m_server || m_client;
304}
305
306bool QDesigner::event(QEvent *ev)
307{
308 bool eaten = false;
309 switch (ev->type()) {
310 case QEvent::FileOpen:
311 m_workbench->readInForm(static_cast<QFileOpenEvent *>(ev)->file());
312 m_workbench->requestActivate();
313 eaten = true;
314 break;
315 case QEvent::Close: {
316 auto *closeEvent = static_cast<QCloseEvent *>(ev);
317 closeEvent->setAccepted(m_workbench->handleClose());
318 if (closeEvent->isAccepted()) {
319 // We're going down, make sure that we don't get our settings saved twice.
320 if (m_mainWindow)
321 m_mainWindow->setCloseEventPolicy(MainWindowBase::AcceptCloseEvents);
322 QApplication::event(ev);
323 }
324 eaten = true;
325 break;
326 }
327 default:
328 eaten = QApplication::event(ev);
329 break;
330 }
331 return eaten;
332}
333
335{
336 m_mainWindow = tw;
337}
338
340{
341 return m_mainWindow;
342}
343
344QT_END_NAMESPACE
QDesignerFormEditorInterface * core() const
ParseArgumentsResult parseCommandLineArguments()
QDesigner(int &argc, char **argv)
Definition qdesigner.cpp:56
bool event(QEvent *ev) override
\reimp
void setMainWindow(MainWindowBase *tw)
QDesignerWorkbench * workbench() const
bool isServerOrClientEnabled() const
~QDesigner() override
Definition qdesigner.cpp:69
MainWindowBase * mainWindow() const
HelpClientType
Definition helpclient.h:17
static constexpr auto designerWarningPrefix
Definition qdesigner.cpp:42
static QDesigner::ParseArgumentsResult parseDesignerCommandLineArguments(QCommandLineParser &parser, Options *options, QString *errorMessage)
static void showHelp(QCommandLineParser &parser, const QString &errorMessage=QString())
static void designerMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
Definition qdesigner.cpp:45
static constexpr auto designerApplicationName
Definition qdesigner.cpp:40
static QtMessageHandler previousMessageHandler
Definition qdesigner.cpp:43
static constexpr auto designerDisplayName
Definition qdesigner.cpp:41
#define qDesigner
Definition qdesigner.h:19
bool server
Definition qdesigner.h:36
bool enableInternalDynamicProperties
Definition qdesigner.h:38
HelpClientType helpMode
Definition qdesigner.h:39