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