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 "fontpropertymanager.h"
30#include "qtpropertymanager.h"
31#include "designerpropertymanager.h"
32#include "qtpropertybrowserutils_p.h"
33
34#include <qdesigner_utils_p.h>
35
36#include <QtCore/qcoreapplication.h>
37#include <QtCore/qvariant.h>
38#include <QtCore/qstring.h>
39#include <QtCore/qdebug.h>
40#include <QtCore/qfile.h>
41#include <QtCore/qtextstream.h>
42#include <QtCore/qxmlstream.h>
43
44QT_BEGIN_NAMESPACE
45
46namespace qdesigner_internal {
47
48 static const char *aliasingC[] = {
49 QT_TRANSLATE_NOOP("FontPropertyManager", "PreferDefault"),
50 QT_TRANSLATE_NOOP("FontPropertyManager", "NoAntialias"),
51 QT_TRANSLATE_NOOP("FontPropertyManager", "PreferAntialias")
52 };
53
54 FontPropertyManager::FontPropertyManager()
55 {
56 const int nameCount = sizeof(aliasingC)/sizeof(const char *);
57 for (int i = 0; i < nameCount; i++)
58 m_aliasingEnumNames.push_back(t: QCoreApplication::translate(context: "FontPropertyManager", key: aliasingC[i]));
59
60 QString errorMessage;
61 if (!readFamilyMapping(rc: &m_familyMappings, errorMessage: &errorMessage)) {
62 designerWarning(message: errorMessage);
63 }
64
65 }
66
67 void FontPropertyManager::preInitializeProperty(QtProperty *property,
68 int type,
69 ResetMap &resetMap)
70 {
71 if (m_createdFontProperty) {
72 PropertyToSubPropertiesMap::iterator it = m_propertyToFontSubProperties.find(akey: m_createdFontProperty);
73 if (it == m_propertyToFontSubProperties.end())
74 it = m_propertyToFontSubProperties.insert(akey: m_createdFontProperty, avalue: PropertyList());
75 const int index = it.value().size();
76 m_fontSubPropertyToFlag.insert(akey: property, avalue: index);
77 it.value().push_back(t: property);
78 m_fontSubPropertyToProperty[property] = m_createdFontProperty;
79 resetMap[property] = true;
80 }
81
82 if (type == QVariant::Font)
83 m_createdFontProperty = property;
84 }
85
86 // Map the font family names to display names retrieved from the XML configuration
87 static QStringList designerFamilyNames(QStringList families, const FontPropertyManager::NameMap &nm)
88 {
89 if (nm.isEmpty())
90 return families;
91
92 const auto ncend = nm.constEnd();
93 for (auto it = families.begin(), end = families.end(); it != end; ++it) {
94 const auto nit = nm.constFind(akey: *it);
95 if (nit != ncend)
96 *it = nit.value();
97 }
98 return families;
99 }
100
101 void FontPropertyManager::postInitializeProperty(QtVariantPropertyManager *vm,
102 QtProperty *property,
103 int type,
104 int enumTypeId)
105 {
106 if (type != QVariant::Font)
107 return;
108
109 // This will cause a recursion
110 QtVariantProperty *antialiasing = vm->addProperty(propertyType: enumTypeId, name: QCoreApplication::translate(context: "FontPropertyManager", key: "Antialiasing"));
111 const QFont font = qvariant_cast<QFont>(v: vm->variantProperty(property)->value());
112
113 antialiasing->setAttribute(QStringLiteral("enumNames"), value: m_aliasingEnumNames);
114 antialiasing->setValue(antialiasingToIndex(antialias: font.styleStrategy()));
115 property->addSubProperty(property: antialiasing);
116
117 m_propertyToAntialiasing[property] = antialiasing;
118 m_antialiasingToProperty[antialiasing] = property;
119 // Fiddle family names
120 if (!m_familyMappings.isEmpty()) {
121 const PropertyToSubPropertiesMap::iterator it = m_propertyToFontSubProperties.find(akey: m_createdFontProperty);
122 QtVariantProperty *familyProperty = vm->variantProperty(property: it.value().constFirst());
123 const QString enumNamesAttribute = QStringLiteral("enumNames");
124 QStringList plainFamilyNames = familyProperty->attributeValue(attribute: enumNamesAttribute).toStringList();
125 // Did someone load fonts or something?
126 if (m_designerFamilyNames.size() != plainFamilyNames.size())
127 m_designerFamilyNames = designerFamilyNames(families: plainFamilyNames, nm: m_familyMappings);
128 familyProperty->setAttribute(attribute: enumNamesAttribute, value: m_designerFamilyNames);
129 }
130 // Next
131 m_createdFontProperty = nullptr;
132 }
133
134 bool FontPropertyManager::uninitializeProperty(QtProperty *property)
135 {
136 const PropertyToPropertyMap::iterator ait = m_propertyToAntialiasing.find(akey: property);
137 if (ait != m_propertyToAntialiasing.end()) {
138 QtProperty *antialiasing = ait.value();
139 m_antialiasingToProperty.remove(akey: antialiasing);
140 m_propertyToAntialiasing.erase(it: ait);
141 delete antialiasing;
142 }
143
144 PropertyToSubPropertiesMap::iterator sit = m_propertyToFontSubProperties.find(akey: property);
145 if (sit == m_propertyToFontSubProperties.end())
146 return false;
147
148 m_propertyToFontSubProperties.erase(it: sit);
149 m_fontSubPropertyToFlag.remove(akey: property);
150 m_fontSubPropertyToProperty.remove(akey: property);
151
152 return true;
153 }
154
155 void FontPropertyManager::slotPropertyDestroyed(QtProperty *property)
156 {
157 removeAntialiasingProperty(property);
158 }
159
160 void FontPropertyManager::removeAntialiasingProperty(QtProperty *property)
161 {
162 const PropertyToPropertyMap::iterator ait = m_antialiasingToProperty.find(akey: property);
163 if (ait == m_antialiasingToProperty.end())
164 return;
165 m_propertyToAntialiasing[ait.value()] = 0;
166 m_antialiasingToProperty.erase(it: ait);
167 }
168
169 bool FontPropertyManager::resetFontSubProperty(QtVariantPropertyManager *vm, QtProperty *property)
170 {
171 const PropertyToPropertyMap::iterator it = m_fontSubPropertyToProperty.find(akey: property);
172 if (it == m_fontSubPropertyToProperty.end())
173 return false;
174
175 QtVariantProperty *fontProperty = vm->variantProperty(property: it.value());
176
177 QVariant v = fontProperty->value();
178 QFont font = qvariant_cast<QFont>(v);
179 unsigned mask = font.resolve();
180 const unsigned flag = fontFlag(idx: m_fontSubPropertyToFlag.value(akey: property));
181
182 mask &= ~flag;
183 font.resolve(mask);
184 v.setValue(font);
185 fontProperty->setValue(v);
186 return true;
187 }
188
189 int FontPropertyManager::antialiasingToIndex(QFont::StyleStrategy antialias)
190 {
191 switch (antialias) {
192 case QFont::PreferDefault: return 0;
193 case QFont::NoAntialias: return 1;
194 case QFont::PreferAntialias: return 2;
195 default: break;
196 }
197 return 0;
198 }
199
200 QFont::StyleStrategy FontPropertyManager::indexToAntialiasing(int idx)
201 {
202 switch (idx) {
203 case 0: return QFont::PreferDefault;
204 case 1: return QFont::NoAntialias;
205 case 2: return QFont::PreferAntialias;
206 }
207 return QFont::PreferDefault;
208 }
209
210 unsigned FontPropertyManager::fontFlag(int idx)
211 {
212 switch (idx) {
213 case 0: return QFont::FamilyResolved;
214 case 1: return QFont::SizeResolved;
215 case 2: return QFont::WeightResolved;
216 case 3: return QFont::StyleResolved;
217 case 4: return QFont::UnderlineResolved;
218 case 5: return QFont::StrikeOutResolved;
219 case 6: return QFont::KerningResolved;
220 case 7: return QFont::StyleStrategyResolved;
221 }
222 return 0;
223 }
224
225 int FontPropertyManager::valueChanged(QtVariantPropertyManager *vm, QtProperty *property, const QVariant &value)
226 {
227 QtProperty *antialiasingProperty = m_antialiasingToProperty.value(akey: property, adefaultValue: 0);
228 if (!antialiasingProperty) {
229 if (m_propertyToFontSubProperties.contains(akey: property)) {
230 updateModifiedState(property, value);
231 }
232 return DesignerPropertyManager::NoMatch;
233 }
234
235 QtVariantProperty *fontProperty = vm->variantProperty(property: antialiasingProperty);
236 const QFont::StyleStrategy newValue = indexToAntialiasing(idx: value.toInt());
237
238 QFont font = qvariant_cast<QFont>(v: fontProperty->value());
239 const QFont::StyleStrategy oldValue = font.styleStrategy();
240 if (newValue == oldValue)
241 return DesignerPropertyManager::Unchanged;
242
243 font.setStyleStrategy(newValue);
244 fontProperty->setValue(QVariant::fromValue(value: font));
245 return DesignerPropertyManager::Changed;
246 }
247
248 void FontPropertyManager::updateModifiedState(QtProperty *property, const QVariant &value)
249 {
250 const PropertyToSubPropertiesMap::iterator it = m_propertyToFontSubProperties.find(akey: property);
251 if (it == m_propertyToFontSubProperties.end())
252 return;
253
254 const PropertyList &subProperties = it.value();
255
256 QFont font = qvariant_cast<QFont>(v: value);
257 const unsigned mask = font.resolve();
258
259 const int count = subProperties.size();
260 for (int index = 0; index < count; index++) {
261 const unsigned flag = fontFlag(idx: index);
262 subProperties.at(i: index)->setModified(mask & flag);
263 }
264 }
265
266 void FontPropertyManager::setValue(QtVariantPropertyManager *vm, QtProperty *property, const QVariant &value)
267 {
268 updateModifiedState(property, value);
269
270 if (QtProperty *antialiasingProperty = m_propertyToAntialiasing.value(akey: property, adefaultValue: 0)) {
271 QtVariantProperty *antialiasing = vm->variantProperty(property: antialiasingProperty);
272 if (antialiasing) {
273 QFont font = qvariant_cast<QFont>(v: value);
274 antialiasing->setValue(antialiasingToIndex(antialias: font.styleStrategy()));
275 }
276 }
277 }
278
279 /* Parse a mappings file of the form:
280 * <fontmappings>
281 * <mapping><family>DejaVu Sans</family><display>DejaVu Sans [CE]</display></mapping>
282 * ... which is used to display on which platforms fonts are available.*/
283
284 static const char *rootTagC = "fontmappings";
285 static const char *mappingTagC = "mapping";
286 static const char *familyTagC = "family";
287 static const char *displayTagC = "display";
288
289 static QString msgXmlError(const QXmlStreamReader &r, const QString& fileName)
290 {
291 return QString::fromUtf8(str: "An error has been encountered at line %1 of %2: %3:").arg(a: r.lineNumber()).arg(args: fileName, args: r.errorString());
292 }
293
294 /* Switch stages when encountering a start element (state table) */
295 enum ParseStage { ParseBeginning, ParseWithinRoot, ParseWithinMapping, ParseWithinFamily,
296 ParseWithinDisplay, ParseError };
297
298 static ParseStage nextStage(ParseStage currentStage, const QStringRef &startElement)
299 {
300 switch (currentStage) {
301 case ParseBeginning:
302 return startElement == QLatin1String(rootTagC) ? ParseWithinRoot : ParseError;
303 case ParseWithinRoot:
304 case ParseWithinDisplay: // Next mapping, was in <display>
305 return startElement == QLatin1String(mappingTagC) ? ParseWithinMapping : ParseError;
306 case ParseWithinMapping:
307 return startElement == QLatin1String(familyTagC) ? ParseWithinFamily : ParseError;
308 case ParseWithinFamily:
309 return startElement == QLatin1String(displayTagC) ? ParseWithinDisplay : ParseError;
310 case ParseError:
311 break;
312 }
313 return ParseError;
314 }
315
316 bool FontPropertyManager::readFamilyMapping(NameMap *rc, QString *errorMessage)
317 {
318 rc->clear();
319 const QString fileName = QStringLiteral(":/qt-project.org/propertyeditor/fontmapping.xml");
320 QFile file(fileName);
321 if (!file.open(flags: QIODevice::ReadOnly)) {
322 *errorMessage = QString::fromUtf8(str: "Unable to open %1: %2").arg(args: fileName, args: file.errorString());
323 return false;
324 }
325
326 QXmlStreamReader reader(&file);
327 QXmlStreamReader::TokenType token;
328
329 QString family;
330 ParseStage stage = ParseBeginning;
331 do {
332 token = reader.readNext();
333 switch (token) {
334 case QXmlStreamReader::Invalid:
335 *errorMessage = msgXmlError(r: reader, fileName);
336 return false;
337 case QXmlStreamReader::StartElement:
338 stage = nextStage(currentStage: stage, startElement: reader.name());
339 switch (stage) {
340 case ParseError:
341 reader.raiseError(message: QString::fromUtf8(str: "Unexpected element <%1>.").arg(a: reader.name().toString()));
342 *errorMessage = msgXmlError(r: reader, fileName);
343 return false;
344 case ParseWithinFamily:
345 family = reader.readElementText();
346 break;
347 case ParseWithinDisplay:
348 rc->insert(akey: family, avalue: reader.readElementText());
349 break;
350 default:
351 break;
352 }
353 default:
354 break;
355 }
356 } while (token != QXmlStreamReader::EndDocument);
357 return true;
358 }
359
360}
361
362QT_END_NAMESPACE
363

source code of qttools/src/designer/src/components/propertyeditor/fontpropertymanager.cpp