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 "deviceprofile_p.h"
30
31#include <QtDesigner/abstractformeditor.h>
32#include <widgetfactory_p.h>
33#include <qdesigner_utils_p.h>
34
35#include <QtWidgets/qapplication.h>
36#include <QtGui/qfont.h>
37#include <QtWidgets/qdesktopwidget.h>
38#include <QtWidgets/qstyle.h>
39#include <QtWidgets/qstylefactory.h>
40#include <QtWidgets/qapplication.h>
41
42#include <QtCore/qshareddata.h>
43#include <QtCore/qtextstream.h>
44
45#include <QtCore/qxmlstream.h>
46
47
48static const char *dpiXPropertyC = "_q_customDpiX";
49static const char *dpiYPropertyC = "_q_customDpiY";
50
51// XML serialization
52static const char *xmlVersionC="1.0";
53static const char *rootElementC="deviceprofile";
54static const char *nameElementC = "name";
55static const char *fontFamilyElementC = "fontfamily";
56static const char *fontPointSizeElementC = "fontpointsize";
57static const char *dPIXElementC = "dpix";
58static const char *dPIYElementC = "dpiy";
59static const char *styleElementC = "style";
60
61/* DeviceProfile:
62 * For preview purposes (preview, widget box, new form dialog), the
63 * QDesignerFormBuilder applies the settings to the form main container
64 * (Point being here that the DPI must be set directly for size calculations
65 * to be correct).
66 * For editing purposes, FormWindow applies the settings to the form container
67 * as not to interfere with the font settings of the form main container.
68 * In addition, the widgetfactory maintains the system settings style
69 * and applies it when creating widgets. */
70
71QT_BEGIN_NAMESPACE
72
73namespace qdesigner_internal {
74
75// ---------------- DeviceProfileData
76class DeviceProfileData : public QSharedData {
77public:
78 DeviceProfileData() = default;
79 void fromSystem();
80 void clear();
81
82 QString m_fontFamily;
83 QString m_style;
84 QString m_name;
85 int m_fontPointSize = -1;
86 int m_dpiX = -1;
87 int m_dpiY = -1;
88};
89
90void DeviceProfileData::clear()
91{
92 m_fontPointSize = -1;
93 m_dpiX = 0;
94 m_dpiY = 0;
95 m_name.clear();
96 m_style.clear();
97}
98
99void DeviceProfileData::fromSystem()
100{
101 const QFont appFont = QApplication::font();
102 m_fontFamily = appFont.family();
103 m_fontPointSize = appFont.pointSize();
104 DeviceProfile::systemResolution(dpiX: &m_dpiX, dpiY: &m_dpiY);
105 m_style.clear();
106}
107
108// ---------------- DeviceProfile
109DeviceProfile::DeviceProfile() :
110 m_d(new DeviceProfileData)
111{
112}
113
114DeviceProfile::DeviceProfile(const DeviceProfile &o) :
115 m_d(o.m_d)
116
117{
118}
119
120DeviceProfile& DeviceProfile::operator=(const DeviceProfile &o)
121{
122 m_d.operator=(o: o.m_d);
123 return *this;
124}
125
126DeviceProfile::~DeviceProfile() = default;
127
128void DeviceProfile::clear()
129{
130 m_d->clear();
131}
132
133bool DeviceProfile::isEmpty() const
134{
135 return m_d->m_name.isEmpty();
136}
137
138QString DeviceProfile::fontFamily() const
139{
140 return m_d->m_fontFamily;
141}
142
143void DeviceProfile::setFontFamily(const QString &f)
144{
145 m_d->m_fontFamily = f;
146}
147
148int DeviceProfile::fontPointSize() const
149{
150 return m_d->m_fontPointSize;
151}
152
153void DeviceProfile::setFontPointSize(int p)
154{
155 m_d->m_fontPointSize = p;
156}
157
158QString DeviceProfile::style() const
159{
160 return m_d->m_style;
161}
162
163void DeviceProfile::setStyle(const QString &s)
164{
165 m_d->m_style = s;
166}
167
168int DeviceProfile::dpiX() const
169{
170 return m_d->m_dpiX;
171}
172
173void DeviceProfile::setDpiX(int d)
174{
175 m_d->m_dpiX = d;
176}
177
178int DeviceProfile::dpiY() const
179{
180 return m_d->m_dpiY;
181}
182
183void DeviceProfile::setDpiY(int d)
184{
185 m_d->m_dpiY = d;
186}
187
188void DeviceProfile::fromSystem()
189{
190 m_d->fromSystem();
191}
192
193QString DeviceProfile::name() const
194{
195 return m_d->m_name;
196}
197
198void DeviceProfile::setName(const QString &n)
199{
200 m_d->m_name = n;
201}
202
203void DeviceProfile::systemResolution(int *dpiX, int *dpiY)
204{
205 const QDesktopWidget *dw = qApp->desktop();
206 *dpiX = dw->logicalDpiX();
207 *dpiY = dw->logicalDpiY();
208}
209
210class FriendlyWidget : public QWidget {
211 friend class DeviceProfile;
212};
213
214void DeviceProfile::widgetResolution(const QWidget *w, int *dpiX, int *dpiY)
215{
216 const FriendlyWidget *fw = static_cast<const FriendlyWidget*>(w);
217 *dpiX = fw->metric(QPaintDevice::PdmDpiX);
218 *dpiY = fw->metric(QPaintDevice::PdmDpiY);
219}
220
221QString DeviceProfile::toString() const
222{
223 const DeviceProfileData &d = *m_d;
224 QString rc;
225 QTextStream(&rc) << "DeviceProfile:name=" << d.m_name << " Font=" << d.m_fontFamily << ' '
226 << d.m_fontPointSize << " Style=" << d.m_style << " DPI=" << d.m_dpiX << ',' << d.m_dpiY;
227 return rc;
228}
229
230// Apply font to widget
231static void applyFont(const QString &family, int size, DeviceProfile::ApplyMode am, QWidget *widget)
232{
233 QFont currentFont = widget->font();
234 if (currentFont.pointSize() == size && currentFont.family() == family)
235 return;
236 switch (am) {
237 case DeviceProfile::ApplyFormParent:
238 // Invisible form parent: Apply all
239 widget->setFont(QFont(family, size));
240 break;
241 case DeviceProfile::ApplyPreview: {
242 // Preview: Apply only subproperties that have not been changed by designer properties
243 bool apply = false;
244 const uint resolve = currentFont.resolve();
245 if (!(resolve & QFont::FamilyResolved)) {
246 currentFont.setFamily(family);
247 apply = true;
248 }
249 if (!(resolve & QFont::SizeResolved)) {
250 currentFont.setPointSize(size);
251 apply = true;
252 }
253 if (apply)
254 widget->setFont(currentFont);
255 }
256 break;
257 }
258}
259
260void DeviceProfile::applyDPI(int dpiX, int dpiY, QWidget *widget)
261{
262 int sysDPIX, sysDPIY; // Set dynamic variables in case values are different from system DPI
263 systemResolution(dpiX: &sysDPIX, dpiY: &sysDPIY);
264 if (dpiX != sysDPIX && dpiY != sysDPIY) {
265 widget->setProperty(name: dpiXPropertyC, value: QVariant(dpiX));
266 widget->setProperty(name: dpiYPropertyC, value: QVariant(dpiY));
267 }
268}
269
270void DeviceProfile::apply(const QDesignerFormEditorInterface *core, QWidget *widget, ApplyMode am) const
271{
272 if (isEmpty())
273 return;
274
275 const DeviceProfileData &d = *m_d;
276
277 if (!d.m_fontFamily.isEmpty())
278 applyFont(family: d.m_fontFamily, size: d.m_fontPointSize, am, widget);
279
280 applyDPI(dpiX: d.m_dpiX, dpiY: d.m_dpiY, widget);
281
282 if (!d.m_style.isEmpty()) {
283 if (WidgetFactory *wf = qobject_cast<qdesigner_internal::WidgetFactory *>(object: core->widgetFactory()))
284 wf->applyStyleTopLevel(styleName: d.m_style, w: widget);
285 }
286}
287
288bool DeviceProfile::equals(const DeviceProfile& rhs) const
289{
290 const DeviceProfileData &d = *m_d;
291 const DeviceProfileData &rhs_d = *rhs.m_d;
292 return d.m_fontPointSize == rhs_d.m_fontPointSize &&
293 d.m_dpiX == rhs_d.m_dpiX && d.m_dpiY == rhs_d.m_dpiY && d.m_fontFamily == rhs_d.m_fontFamily &&
294 d.m_style == rhs_d.m_style && d.m_name == rhs_d.m_name;
295}
296
297static inline void writeElement(QXmlStreamWriter &writer, const QString &element, const QString &cdata)
298{
299 writer.writeStartElement(qualifiedName: element);
300 writer.writeCharacters(text: cdata);
301 writer.writeEndElement();
302}
303
304QString DeviceProfile::toXml() const
305{
306 const DeviceProfileData &d = *m_d;
307 QString rc;
308 QXmlStreamWriter writer(&rc);
309 writer.writeStartDocument(version: QLatin1String(xmlVersionC));
310 writer.writeStartElement(qualifiedName: QLatin1String(rootElementC));
311 writeElement(writer, element: QLatin1String(nameElementC), cdata: d.m_name);
312
313 if (!d.m_fontFamily.isEmpty())
314 writeElement(writer, element: QLatin1String(fontFamilyElementC), cdata: d.m_fontFamily);
315 if (d.m_fontPointSize >= 0)
316 writeElement(writer, element: QLatin1String(fontPointSizeElementC), cdata: QString::number(d.m_fontPointSize));
317 if (d.m_dpiX > 0)
318 writeElement(writer, element: QLatin1String(dPIXElementC), cdata: QString::number(d.m_dpiX));
319 if (d.m_dpiY > 0)
320 writeElement(writer, element: QLatin1String(dPIYElementC), cdata: QString::number(d.m_dpiY));
321 if (!d.m_style.isEmpty())
322 writeElement(writer, element: QLatin1String(styleElementC), cdata: d.m_style);
323
324 writer.writeEndElement();
325 writer.writeEndDocument();
326 return rc;
327}
328
329/* Switch stages when encountering a start element (state table) */
330enum ParseStage { ParseBeginning, ParseWithinRoot,
331 ParseName, ParseFontFamily, ParseFontPointSize, ParseDPIX, ParseDPIY, ParseStyle,
332 ParseError };
333
334static ParseStage nextStage(ParseStage currentStage, const QStringRef &startElement)
335{
336 switch (currentStage) {
337 case ParseBeginning:
338 if (startElement == QLatin1String(rootElementC))
339 return ParseWithinRoot;
340 break;
341 case ParseWithinRoot:
342 case ParseName:
343 case ParseFontFamily:
344 case ParseFontPointSize:
345 case ParseDPIX:
346 case ParseDPIY:
347 case ParseStyle:
348 if (startElement == QLatin1String(nameElementC))
349 return ParseName;
350 if (startElement == QLatin1String(fontFamilyElementC))
351 return ParseFontFamily;
352 if (startElement == QLatin1String(fontPointSizeElementC))
353 return ParseFontPointSize;
354 if (startElement == QLatin1String(dPIXElementC))
355 return ParseDPIX;
356 if (startElement == QLatin1String(dPIYElementC))
357 return ParseDPIY;
358 if (startElement == QLatin1String(styleElementC))
359 return ParseStyle;
360 break;
361 case ParseError:
362 break;
363 }
364 return ParseError;
365}
366
367static bool readIntegerElement(QXmlStreamReader &reader, int *v)
368{
369 const QString e = reader.readElementText();
370 bool ok;
371 *v = e.toInt(ok: &ok);
372 //: Reading a number for an embedded device profile
373 if (!ok)
374 reader.raiseError(message: QApplication::translate(context: "DeviceProfile", key: "'%1' is not a number.").arg(a: e));
375 return ok;
376}
377
378bool DeviceProfile::fromXml(const QString &xml, QString *errorMessage)
379{
380 DeviceProfileData &d = *m_d;
381 d.fromSystem();
382
383 QXmlStreamReader reader(xml);
384
385 ParseStage ps = ParseBeginning;
386 QXmlStreamReader::TokenType tt = QXmlStreamReader::NoToken;
387 int iv = 0;
388 do {
389 tt = reader.readNext();
390 if (tt == QXmlStreamReader::StartElement) {
391 ps = nextStage(currentStage: ps, startElement: reader.name());
392 switch (ps) {
393 case ParseBeginning:
394 case ParseWithinRoot:
395 break;
396 case ParseError:
397 reader.raiseError(message: QApplication::translate(context: "DeviceProfile", key: "An invalid tag <%1> was encountered.").arg(a: reader.name().toString()));
398 tt = QXmlStreamReader::Invalid;
399 break;
400 case ParseName:
401 d.m_name = reader.readElementText();
402 break;
403 case ParseFontFamily:
404 d.m_fontFamily = reader.readElementText();
405 break;
406 case ParseFontPointSize:
407 if (readIntegerElement(reader, v: &iv)) {
408 d.m_fontPointSize = iv;
409 } else {
410 tt = QXmlStreamReader::Invalid;
411 }
412 break;
413 case ParseDPIX:
414 if (readIntegerElement(reader, v: &iv)) {
415 d.m_dpiX = iv;
416 } else {
417 tt = QXmlStreamReader::Invalid;
418 }
419 break;
420 case ParseDPIY:
421 if (readIntegerElement(reader, v: &iv)) {
422 d.m_dpiY = iv;
423 } else {
424 tt = QXmlStreamReader::Invalid;
425 }
426 break;
427 case ParseStyle:
428 d.m_style = reader.readElementText();
429 break;
430 }
431 }
432 } while (tt != QXmlStreamReader::Invalid && tt != QXmlStreamReader::EndDocument);
433
434 if (reader.hasError()) {
435 *errorMessage = reader.errorString();
436 return false;
437 }
438
439 return true;
440}
441}
442
443QT_END_NAMESPACE
444
445

source code of qttools/src/designer/src/lib/shared/deviceprofile.cpp