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 | |
44 | QT_BEGIN_NAMESPACE |
45 | |
46 | namespace 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 | |
362 | QT_END_NAMESPACE |
363 | |