Changeset View
Changeset View
Standalone View
Standalone View
kcms/keyboard/xkb_rules.cpp
Show All 25 Lines | |||||
26 | #include <QRegExp> | 26 | #include <QRegExp> | ||
27 | #include <QTextDocument> // for Qt::escape | 27 | #include <QTextDocument> // for Qt::escape | ||
28 | #include <QXmlAttributes> | 28 | #include <QXmlAttributes> | ||
29 | 29 | | |||
30 | #include <QtConcurrent> | 30 | #include <QtConcurrent> | ||
31 | //#include <libintl.h> | 31 | //#include <libintl.h> | ||
32 | //#include <locale.h> | 32 | //#include <locale.h> | ||
33 | 33 | | |||
34 | #include "x11_helper.h" | 34 | //#include "x11_helper.h" | ||
35 | 35 | | |||
36 | // for findXkbRuleFile | 36 | // for findXkbRuleFile | ||
37 | #include <QX11Info> | 37 | #include <QX11Info> | ||
38 | #include <X11/Xlib.h> | | |||
39 | #include <X11/Xatom.h> | | |||
40 | #include <X11/XKBlib.h> | 38 | #include <X11/XKBlib.h> | ||
39 | #include <X11/Xatom.h> | ||||
40 | #include <X11/Xlib.h> | ||||
41 | #include <X11/extensions/XKBrules.h> | 41 | #include <X11/extensions/XKBrules.h> | ||
42 | #include <fixx11h.h> | | |||
43 | #include <config-workspace.h> | 42 | #include <config-workspace.h> | ||
43 | #include <fixx11h.h> | ||||
44 | 44 | | |||
45 | 45 | class RulesHandler : public QXmlDefaultHandler { | |||
46 | | ||||
47 | class RulesHandler : public QXmlDefaultHandler | | |||
48 | { | | |||
49 | public: | 46 | public: | ||
50 | RulesHandler(Rules* rules_, bool fromExtras_): | 47 | RulesHandler(XkbRules* rules_, bool fromExtras_) | ||
51 | rules(rules_), | 48 | : rules(rules_) | ||
52 | fromExtras(fromExtras_){} | 49 | , fromExtras(fromExtras_) | ||
50 | { | ||||
51 | } | ||||
53 | 52 | | |||
54 | bool startElement(const QString &namespaceURI, const QString &localName, | 53 | bool startElement(const QString& namespaceURI, const QString& localName, | ||
55 | const QString &qName, const QXmlAttributes &attributes) override; | 54 | const QString& qName, const QXmlAttributes& attributes) override; | ||
56 | bool endElement(const QString &namespaceURI, const QString &localName, | 55 | bool endElement(const QString& namespaceURI, const QString& localName, | ||
57 | const QString &qName) override; | 56 | const QString& qName) override; | ||
58 | bool characters(const QString &str) override; | 57 | bool characters(const QString& str) override; | ||
59 | // bool fatalError(const QXmlParseException &exception); | 58 | // bool fatalError(const QXmlParseException &exception); | ||
60 | // QString errorString() const; | 59 | // QString errorString() const; | ||
61 | 60 | | |||
62 | private: | 61 | private: | ||
63 | // QString getString(const QString& text); | 62 | // QString getString(const QString& text); | ||
64 | 63 | | |||
65 | QStringList path; | 64 | QStringList path; | ||
66 | Rules* rules; | 65 | XkbRules* rules; | ||
67 | const bool fromExtras; | 66 | const bool fromExtras; | ||
68 | }; | 67 | }; | ||
69 | 68 | | |||
70 | static QString translate_xml_item(const QString& itemText) | 69 | static QString translate_xml_item(const QString& itemText) | ||
71 | { | 70 | { | ||
72 | if (itemText.isEmpty()) { // i18n warns on empty input strings | 71 | if (itemText.isEmpty()) { // i18n warns on empty input strings | ||
73 | return itemText; | 72 | return itemText; | ||
74 | } | 73 | } | ||
75 | //messages are already extracted from the source XML files by xkb | 74 | //messages are already extracted from the source XML files by xkb | ||
76 | //the characters '<' and '>' (but not '"') are HTML-escaped in the xkeyboard-config translation files, so we need to convert them before/after looking up the translation | 75 | //the characters '<' and '>' (but not '"') are HTML-escaped in the xkeyboard-config translation files, so we need to convert them before/after looking up the translation | ||
77 | //note that we cannot use QString::toHtmlEscaped() here because that would convert '"' as well | 76 | //note that we cannot use QString::toHtmlEscaped() here because that would convert '"' as well | ||
78 | QString msgid(itemText); | 77 | QString msgid(itemText); | ||
79 | return i18nd("xkeyboard-config", msgid.replace(QLatin1Literal("<"), QLatin1Literal("<")).replace(QLatin1Literal(">"), QLatin1Literal(">")).toUtf8()).replace(QLatin1Literal("<"), QLatin1Literal("<")).replace(QLatin1Literal(">"), QLatin1Literal(">")); | 78 | return i18nd("xkeyboard-config", msgid.replace(QLatin1Literal("<"), QLatin1Literal("<")).replace(QLatin1Literal(">"), QLatin1Literal(">")).toUtf8()).replace(QLatin1Literal("<"), QLatin1Literal("<")).replace(QLatin1Literal(">"), QLatin1Literal(">")); | ||
80 | } | 79 | } | ||
81 | 80 | | |||
82 | static QString translate_description(ConfigItem* item) | 81 | static QString translate_description(ConfigItem* item) | ||
83 | { | 82 | { | ||
84 | return item->description.isEmpty() | 83 | return item->description.isEmpty() | ||
85 | ? item->name : translate_xml_item(item->description); | 84 | ? item->name | ||
85 | : translate_xml_item(item->description); | ||||
86 | } | 86 | } | ||
87 | 87 | | |||
88 | static bool notEmpty(const ConfigItem* item) | 88 | static bool notEmpty(const ConfigItem* item) | ||
89 | { | 89 | { | ||
90 | return ! item->name.isEmpty(); | 90 | return !item->name.isEmpty(); | ||
91 | } | 91 | } | ||
92 | 92 | | |||
93 | template<class T> | 93 | template <class T> | ||
94 | void removeEmptyItems(QList<T*>& list) | 94 | void removeEmptyItems(QList<T*>& list) | ||
95 | { | 95 | { | ||
96 | #ifdef __GNUC__ | 96 | #ifdef __GNUC__ | ||
97 | #if __GNUC__ == 4 && (__GNUC_MINOR__ == 8 && __GNUC_PATCHLEVEL__ < 3) || (__GNUC_MINOR__ == 7 && __GNUC_PATCHLEVEL__ < 4) | 97 | #if __GNUC__ == 4 && (__GNUC_MINOR__ == 8 && __GNUC_PATCHLEVEL__ < 3) || (__GNUC_MINOR__ == 7 && __GNUC_PATCHLEVEL__ < 4) | ||
98 | #warning Compiling with a workaround for GCC < 4.8.3 || GCC < 4.7.4 http://gcc.gnu.org/bugzilla/show_bug.cgi?id=58800 | 98 | #warning Compiling with a workaround for GCC < 4.8.3 || GCC < 4.7.4 http://gcc.gnu.org/bugzilla/show_bug.cgi?id=58800 | ||
99 | Q_FOREACH(T* x, list) { | 99 | Q_FOREACH (T* x, list) { | ||
100 | ConfigItem *y = static_cast<ConfigItem*>(x); | 100 | ConfigItem* y = static_cast<ConfigItem*>(x); | ||
101 | if (y->name.isEmpty()) { | 101 | if (y->name.isEmpty()) { | ||
102 | list.removeAll(x); | 102 | list.removeAll(x); | ||
103 | } | 103 | } | ||
104 | } | 104 | } | ||
105 | #else | 105 | #else | ||
106 | QtConcurrent::blockingFilter(list, notEmpty); | 106 | QtConcurrent::blockingFilter(list, notEmpty); | ||
107 | #endif | 107 | #endif | ||
108 | #endif | 108 | #endif | ||
109 | } | 109 | } | ||
110 | 110 | | |||
111 | static | 111 | static void postProcess(XkbRules* rules) | ||
112 | void postProcess(Rules* rules) | | |||
113 | { | 112 | { | ||
114 | //TODO remove elements with empty names to safeguard us | 113 | //TODO remove elements with empty names to safeguard us | ||
115 | removeEmptyItems(rules->layoutInfos); | 114 | removeEmptyItems(rules->layoutInfos); | ||
116 | removeEmptyItems(rules->modelInfos); | 115 | removeEmptyItems(rules->modelInfos); | ||
117 | removeEmptyItems(rules->optionGroupInfos); | 116 | removeEmptyItems(rules->optionGroupInfos); | ||
118 | 117 | | |||
119 | // setlocale(LC_ALL, ""); | 118 | // setlocale(LC_ALL, ""); | ||
120 | // bindtextdomain("xkeyboard-config", LOCALE_DIR); | 119 | // bindtextdomain("xkeyboard-config", LOCALE_DIR); | ||
121 | foreach(ModelInfo* modelInfo, rules->modelInfos) { | 120 | foreach (ModelInfo* modelInfo, rules->modelInfos) { | ||
122 | modelInfo->vendor = translate_xml_item(modelInfo->vendor); | 121 | modelInfo->vendor = translate_xml_item(modelInfo->vendor); | ||
123 | modelInfo->description = translate_description(modelInfo); | 122 | modelInfo->description = translate_description(modelInfo); | ||
124 | } | 123 | } | ||
125 | 124 | | |||
126 | foreach(LayoutInfo* layoutInfo, rules->layoutInfos) { | 125 | foreach (LayoutInfo* layoutInfo, rules->layoutInfos) { | ||
127 | layoutInfo->description = translate_description(layoutInfo); | 126 | layoutInfo->description = translate_description(layoutInfo); | ||
128 | 127 | | |||
129 | removeEmptyItems(layoutInfo->variantInfos); | 128 | removeEmptyItems(layoutInfo->variantInfos); | ||
130 | foreach(VariantInfo* variantInfo, layoutInfo->variantInfos) { | 129 | foreach (VariantInfo* variantInfo, layoutInfo->variantInfos) { | ||
131 | variantInfo->description = translate_description(variantInfo); | 130 | variantInfo->description = translate_description(variantInfo); | ||
132 | } | 131 | } | ||
133 | } | 132 | } | ||
134 | foreach(OptionGroupInfo* optionGroupInfo, rules->optionGroupInfos) { | 133 | foreach (OptionGroupInfo* optionGroupInfo, rules->optionGroupInfos) { | ||
135 | optionGroupInfo->description = translate_description(optionGroupInfo); | 134 | optionGroupInfo->description = translate_description(optionGroupInfo); | ||
136 | 135 | | |||
137 | removeEmptyItems(optionGroupInfo->optionInfos); | 136 | removeEmptyItems(optionGroupInfo->optionInfos); | ||
138 | foreach(OptionInfo* optionInfo, optionGroupInfo->optionInfos) { | 137 | foreach (OptionInfo* optionInfo, optionGroupInfo->optionInfos) { | ||
139 | optionInfo->description = translate_description(optionInfo); | 138 | optionInfo->description = translate_description(optionInfo); | ||
140 | } | 139 | } | ||
141 | } | 140 | } | ||
142 | } | 141 | } | ||
143 | 142 | | |||
144 | 143 | XkbRules::XkbRules() | |||
145 | Rules::Rules(): | 144 | : version(QStringLiteral("1.0")) | ||
146 | version(QStringLiteral("1.0")) | | |||
147 | { | 145 | { | ||
148 | } | 146 | } | ||
149 | 147 | | |||
150 | QString Rules::getRulesName() | 148 | QString XkbRules::getRulesName() | ||
151 | { | 149 | { | ||
152 | if (!QX11Info::isPlatformX11()) { | 150 | if (!QX11Info::isPlatformX11()) { | ||
153 | return QString(); | 151 | return QString(); | ||
154 | } | 152 | } | ||
155 | XkbRF_VarDefsRec vd; | 153 | XkbRF_VarDefsRec vd; | ||
156 | char *tmp = NULL; | 154 | char* tmp = nullptr; | ||
157 | 155 | | |||
158 | if (XkbRF_GetNamesProp(QX11Info::display(), &tmp, &vd) && tmp != NULL ) { | 156 | if (XkbRF_GetNamesProp(QX11Info::display(), &tmp, &vd) && tmp != nullptr) { | ||
159 | // qCDebug(KCM_KEYBOARD) << "namesprop" << tmp ; | 157 | // qCDebug(KCM_KEYBOARD) << "namesprop" << tmp ; | ||
160 | const QString name(tmp); | 158 | const QString name(tmp); | ||
161 | XFree(tmp); | 159 | XFree(tmp); | ||
162 | return name; | 160 | return name; | ||
163 | } | 161 | } | ||
164 | 162 | | |||
165 | return {}; | 163 | return {}; | ||
166 | } | 164 | } | ||
167 | 165 | | |||
168 | QString Rules::findXkbDir() | 166 | QString XkbRules::findXkbDir() | ||
169 | { | 167 | { | ||
170 | return QStringLiteral(XKBDIR); | 168 | return QStringLiteral(XKBDIR); | ||
171 | } | 169 | } | ||
172 | 170 | | |||
173 | static QString findXkbRulesFile() | 171 | static QString findXkbRulesFile() | ||
174 | { | 172 | { | ||
175 | QString rulesFile; | 173 | QString rulesFile; | ||
176 | QString rulesName = Rules::getRulesName(); | 174 | QString rulesName = XkbRules::getRulesName(); | ||
177 | 175 | | |||
178 | const QString xkbDir = Rules::findXkbDir(); | 176 | const QString xkbDir = XkbRules::findXkbDir(); | ||
179 | if ( ! rulesName.isNull() ) { | 177 | if (!rulesName.isNull()) { | ||
180 | rulesFile = QStringLiteral("%1/rules/%2.xml").arg(xkbDir, rulesName); | 178 | rulesFile = QStringLiteral("%1/rules/%2.xml").arg(xkbDir, rulesName); | ||
181 | } else { | 179 | } else { | ||
182 | // default to evdev | 180 | // default to evdev | ||
183 | rulesFile = QStringLiteral("%1/rules/evdev.xml").arg(xkbDir); | 181 | rulesFile = QStringLiteral("%1/rules/evdev.xml").arg(xkbDir); | ||
184 | } | 182 | } | ||
185 | 183 | | |||
186 | return rulesFile; | 184 | return rulesFile; | ||
187 | } | 185 | } | ||
188 | 186 | | |||
189 | static | 187 | static void mergeRules(XkbRules* rules, XkbRules* extraRules) | ||
190 | void mergeRules(Rules* rules, Rules* extraRules) | | |||
191 | { | 188 | { | ||
192 | rules->modelInfos.append( extraRules->modelInfos ); | 189 | rules->modelInfos.append(extraRules->modelInfos); | ||
193 | rules->optionGroupInfos.append( extraRules->optionGroupInfos ); // need to iterate and merge? | 190 | rules->optionGroupInfos.append(extraRules->optionGroupInfos); // need to iterate and merge? | ||
194 | 191 | | |||
195 | QList<LayoutInfo*> layoutsToAdd; | 192 | QList<LayoutInfo*> layoutsToAdd; | ||
196 | foreach(LayoutInfo* extraLayoutInfo, extraRules->layoutInfos) { | 193 | foreach (LayoutInfo* extraLayoutInfo, extraRules->layoutInfos) { | ||
197 | LayoutInfo* layoutInfo = findByName(rules->layoutInfos, extraLayoutInfo->name); | 194 | LayoutInfo* layoutInfo = findByName(rules->layoutInfos, extraLayoutInfo->name); | ||
198 | if( layoutInfo != NULL ) { | 195 | if (layoutInfo != nullptr) { | ||
199 | layoutInfo->variantInfos.append( extraLayoutInfo->variantInfos ); | 196 | layoutInfo->variantInfos.append(extraLayoutInfo->variantInfos); | ||
200 | layoutInfo->languages.append( extraLayoutInfo->languages ); | 197 | layoutInfo->languages.append(extraLayoutInfo->languages); | ||
201 | } | 198 | } else { | ||
202 | else { | | |||
203 | layoutsToAdd.append(extraLayoutInfo); | 199 | layoutsToAdd.append(extraLayoutInfo); | ||
204 | } | 200 | } | ||
205 | } | 201 | } | ||
206 | rules->layoutInfos.append(layoutsToAdd); | 202 | rules->layoutInfos.append(layoutsToAdd); | ||
207 | qCDebug(KCM_KEYBOARD) << "Merged from extra rules:" << extraRules->layoutInfos.size() << "layouts," << extraRules->modelInfos.size() << "models," << extraRules->optionGroupInfos.size() << "option groups"; | 203 | qCDebug(KCM_KEYBOARD) << "Merged from extra rules:" << extraRules->layoutInfos.size() << "layouts," << extraRules->modelInfos.size() << "models," << extraRules->optionGroupInfos.size() << "option groups"; | ||
208 | 204 | | |||
209 | // base rules now own the objects - remove them from extra rules so that it does not try to delete them | 205 | // base rules now own the objects - remove them from extra rules so that it does not try to delete them | ||
210 | extraRules->layoutInfos.clear(); | 206 | extraRules->layoutInfos.clear(); | ||
211 | extraRules->modelInfos.clear(); | 207 | extraRules->modelInfos.clear(); | ||
212 | extraRules->optionGroupInfos.clear(); | 208 | extraRules->optionGroupInfos.clear(); | ||
213 | } | 209 | } | ||
214 | 210 | | |||
211 | const char XkbRules::XKB_OPTION_GROUP_SEPARATOR = ':'; | ||||
215 | 212 | | |||
216 | const char Rules::XKB_OPTION_GROUP_SEPARATOR = ':'; | 213 | XkbRules* XkbRules::readRules(ExtrasFlag extrasFlag) | ||
217 | | ||||
218 | Rules* Rules::readRules(ExtrasFlag extrasFlag) | | |||
219 | { | 214 | { | ||
220 | Rules* rules = new Rules(); | 215 | XkbRules* rules = new XkbRules(); | ||
221 | QString rulesFile = findXkbRulesFile(); | 216 | QString rulesFile = findXkbRulesFile(); | ||
222 | if( ! readRules(rules, rulesFile, false) ) { | 217 | if (!readRules(rules, rulesFile, false)) { | ||
223 | delete rules; | 218 | delete rules; | ||
224 | return NULL; | 219 | return nullptr; | ||
225 | } | 220 | } | ||
226 | if( extrasFlag == Rules::READ_EXTRAS ) { | 221 | if (extrasFlag == XkbRules::READ_EXTRAS) { | ||
227 | QRegExp regex(QStringLiteral("\\.xml$")); | 222 | QRegExp regex(QStringLiteral("\\.xml$")); | ||
228 | Rules* rulesExtra = new Rules(); | 223 | XkbRules* rulesExtra = new XkbRules(); | ||
229 | QString extraRulesFile = rulesFile.replace(regex, QStringLiteral(".extras.xml")); | 224 | QString extraRulesFile = rulesFile.replace(regex, QStringLiteral(".extras.xml")); | ||
230 | if( readRules(rulesExtra, extraRulesFile, true) ) { // not fatal if it fails | 225 | if (readRules(rulesExtra, extraRulesFile, true)) { // not fatal if it fails | ||
231 | mergeRules(rules, rulesExtra); | 226 | mergeRules(rules, rulesExtra); | ||
232 | } | 227 | } | ||
233 | delete rulesExtra; | 228 | delete rulesExtra; | ||
234 | } | 229 | } | ||
235 | return rules; | 230 | return rules; | ||
236 | } | 231 | } | ||
237 | 232 | | |||
238 | 233 | XkbRules* XkbRules::readRules(XkbRules* rules, const QString& filename, bool fromExtras) | |||
239 | Rules* Rules::readRules(Rules* rules, const QString& filename, bool fromExtras) | | |||
240 | { | 234 | { | ||
241 | QFile file(filename); | 235 | QFile file(filename); | ||
242 | if( !file.open(QFile::ReadOnly | QFile::Text) ) { | 236 | if (!file.open(QFile::ReadOnly | QFile::Text)) { | ||
243 | qCCritical(KCM_KEYBOARD) << "Cannot open the rules file" << file.fileName(); | 237 | qCCritical(KCM_KEYBOARD) << "Cannot open the rules file" << file.fileName(); | ||
244 | return NULL; | 238 | return nullptr; | ||
245 | } | 239 | } | ||
246 | 240 | | |||
247 | RulesHandler rulesHandler(rules, fromExtras); | 241 | RulesHandler rulesHandler(rules, fromExtras); | ||
248 | 242 | | |||
249 | QXmlSimpleReader reader; | 243 | QXmlSimpleReader reader; | ||
250 | reader.setContentHandler(&rulesHandler); | 244 | reader.setContentHandler(&rulesHandler); | ||
251 | reader.setErrorHandler(&rulesHandler); | 245 | reader.setErrorHandler(&rulesHandler); | ||
252 | 246 | | |||
253 | QXmlInputSource xmlInputSource(&file); | 247 | QXmlInputSource xmlInputSource(&file); | ||
254 | 248 | | |||
255 | qCDebug(KCM_KEYBOARD) << "Parsing xkb rules from" << file.fileName(); | 249 | qCDebug(KCM_KEYBOARD) << "Parsing xkb rules from" << file.fileName(); | ||
256 | 250 | | |||
257 | if( ! reader.parse(xmlInputSource) ) { | 251 | if (!reader.parse(xmlInputSource)) { | ||
258 | qCCritical(KCM_KEYBOARD) << "Failed to parse the rules file" << file.fileName(); | 252 | qCCritical(KCM_KEYBOARD) << "Failed to parse the rules file" << file.fileName(); | ||
259 | return NULL; | 253 | return nullptr; | ||
260 | } | 254 | } | ||
261 | 255 | | |||
262 | postProcess(rules); | 256 | postProcess(rules); | ||
263 | 257 | | |||
264 | return rules; | 258 | return rules; | ||
265 | } | 259 | } | ||
266 | 260 | | |||
267 | bool RulesHandler::startElement(const QString &/*namespaceURI*/, const QString &/*localName*/, | 261 | bool RulesHandler::startElement(const QString& /*namespaceURI*/, const QString& /*localName*/, | ||
268 | const QString &qName, const QXmlAttributes &attributes) | 262 | const QString& qName, const QXmlAttributes& attributes) | ||
269 | { | 263 | { | ||
270 | path << QString(qName); | 264 | path << QString(qName); | ||
271 | 265 | | |||
272 | QString strPath = path.join(QStringLiteral("/")); | 266 | QString strPath = path.join(QStringLiteral("/")); | ||
273 | if( strPath.endsWith(QLatin1String("layoutList/layout/configItem")) ) { | 267 | if (strPath.endsWith(QLatin1String("layoutList/layout/configItem"))) { | ||
274 | rules->layoutInfos << new LayoutInfo(fromExtras); | 268 | rules->layoutInfos << new LayoutInfo(fromExtras); | ||
275 | } | 269 | } else if (strPath.endsWith(QLatin1String("layoutList/layout/variantList/variant"))) { | ||
276 | else if( strPath.endsWith(QLatin1String("layoutList/layout/variantList/variant")) ) { | | |||
277 | rules->layoutInfos.last()->variantInfos << new VariantInfo(fromExtras); | 270 | rules->layoutInfos.last()->variantInfos << new VariantInfo(fromExtras); | ||
278 | } | 271 | } else if (strPath.endsWith(QLatin1String("modelList/model"))) { | ||
279 | else if( strPath.endsWith(QLatin1String("modelList/model")) ) { | | |||
280 | rules->modelInfos << new ModelInfo(); | 272 | rules->modelInfos << new ModelInfo(); | ||
281 | } | 273 | } else if (strPath.endsWith(QLatin1String("optionList/group"))) { | ||
282 | else if( strPath.endsWith(QLatin1String("optionList/group")) ) { | | |||
283 | rules->optionGroupInfos << new OptionGroupInfo(); | 274 | rules->optionGroupInfos << new OptionGroupInfo(); | ||
284 | rules->optionGroupInfos.last()->exclusive = (attributes.value(QStringLiteral("allowMultipleSelection")) != QLatin1String("true")); | 275 | rules->optionGroupInfos.last()->exclusive = (attributes.value(QStringLiteral("allowMultipleSelection")) != QLatin1String("true")); | ||
285 | } | 276 | } else if (strPath.endsWith(QLatin1String("optionList/group/option"))) { | ||
286 | else if( strPath.endsWith(QLatin1String("optionList/group/option")) ) { | | |||
287 | rules->optionGroupInfos.last()->optionInfos << new OptionInfo(); | 277 | rules->optionGroupInfos.last()->optionInfos << new OptionInfo(); | ||
288 | } | 278 | } else if (strPath == ("xkbConfigRegistry") && !attributes.value(QStringLiteral("version")).isEmpty()) { | ||
289 | else if( strPath == ("xkbConfigRegistry") && ! attributes.value(QStringLiteral("version")).isEmpty() ) { | | |||
290 | rules->version = attributes.value(QStringLiteral("version")); | 279 | rules->version = attributes.value(QStringLiteral("version")); | ||
291 | qCDebug(KCM_KEYBOARD) << "xkbConfigRegistry version" << rules->version; | 280 | qCDebug(KCM_KEYBOARD) << "xkbConfigRegistry version" << rules->version; | ||
292 | } | 281 | } | ||
293 | return true; | 282 | return true; | ||
294 | } | 283 | } | ||
295 | 284 | | |||
296 | bool RulesHandler::endElement(const QString &/*namespaceURI*/, const QString &/*localName*/, const QString &/*qName*/) | 285 | bool RulesHandler::endElement(const QString& /*namespaceURI*/, const QString& /*localName*/, const QString& /*qName*/) | ||
297 | { | 286 | { | ||
298 | path.removeLast(); | 287 | path.removeLast(); | ||
299 | return true; | 288 | return true; | ||
300 | } | 289 | } | ||
301 | 290 | | |||
302 | bool RulesHandler::characters(const QString &str) | 291 | bool RulesHandler::characters(const QString& str) | ||
303 | { | 292 | { | ||
304 | if( !str.trimmed().isEmpty() ) { | 293 | if (!str.trimmed().isEmpty()) { | ||
305 | QString strPath = path.join(QStringLiteral("/")); | 294 | QString strPath = path.join(QStringLiteral("/")); | ||
306 | if( strPath.endsWith(QLatin1String("layoutList/layout/configItem/name")) ) { | 295 | if (strPath.endsWith(QLatin1String("layoutList/layout/configItem/name"))) { | ||
307 | if( rules->layoutInfos.last() != NULL ) { | 296 | if (rules->layoutInfos.last() != nullptr) { | ||
308 | rules->layoutInfos.last()->name = str.trimmed(); | 297 | rules->layoutInfos.last()->name = str.trimmed(); | ||
309 | // qCDebug(KCM_KEYBOARD) << "name:" << str; | 298 | // qCDebug(KCM_KEYBOARD) << "name:" << str; | ||
310 | } | 299 | } | ||
311 | // skipping invalid entry | 300 | // skipping invalid entry | ||
312 | } | 301 | } else if (strPath.endsWith(QLatin1String("layoutList/layout/configItem/description"))) { | ||
313 | else if( strPath.endsWith(QLatin1String("layoutList/layout/configItem/description")) ) { | | |||
314 | rules->layoutInfos.last()->description = str.trimmed(); | 302 | rules->layoutInfos.last()->description = str.trimmed(); | ||
315 | // qCDebug(KCM_KEYBOARD) << "descr:" << str; | 303 | // qCDebug(KCM_KEYBOARD) << "descr:" << str; | ||
316 | } | 304 | } else if (strPath.endsWith(QLatin1String("layoutList/layout/configItem/languageList/iso639Id"))) { | ||
317 | else if( strPath.endsWith(QLatin1String("layoutList/layout/configItem/languageList/iso639Id")) ) { | | |||
318 | rules->layoutInfos.last()->languages << str.trimmed(); | 305 | rules->layoutInfos.last()->languages << str.trimmed(); | ||
319 | // qCDebug(KCM_KEYBOARD) << "\tlang:" << str; | 306 | // qCDebug(KCM_KEYBOARD) << "\tlang:" << str; | ||
320 | } | 307 | } else if (strPath.endsWith(QLatin1String("layoutList/layout/variantList/variant/configItem/name"))) { | ||
321 | else if( strPath.endsWith(QLatin1String("layoutList/layout/variantList/variant/configItem/name")) ) { | | |||
322 | rules->layoutInfos.last()->variantInfos.last()->name = str.trimmed(); | 308 | rules->layoutInfos.last()->variantInfos.last()->name = str.trimmed(); | ||
323 | // qCDebug(KCM_KEYBOARD) << "\tvariant name:" << str; | 309 | // qCDebug(KCM_KEYBOARD) << "\tvariant name:" << str; | ||
324 | } | 310 | } else if (strPath.endsWith(QLatin1String("layoutList/layout/variantList/variant/configItem/description"))) { | ||
325 | else if( strPath.endsWith(QLatin1String("layoutList/layout/variantList/variant/configItem/description")) ) { | | |||
326 | rules->layoutInfos.last()->variantInfos.last()->description = str.trimmed(); | 311 | rules->layoutInfos.last()->variantInfos.last()->description = str.trimmed(); | ||
327 | // qCDebug(KCM_KEYBOARD) << "\tvariant descr:" << str; | 312 | // qCDebug(KCM_KEYBOARD) << "\tvariant descr:" << str; | ||
328 | } | 313 | } else if (strPath.endsWith(QLatin1String("layoutList/layout/variantList/variant/configItem/languageList/iso639Id"))) { | ||
329 | else if( strPath.endsWith(QLatin1String("layoutList/layout/variantList/variant/configItem/languageList/iso639Id")) ) { | | |||
330 | rules->layoutInfos.last()->variantInfos.last()->languages << str.trimmed(); | 314 | rules->layoutInfos.last()->variantInfos.last()->languages << str.trimmed(); | ||
331 | // qCDebug(KCM_KEYBOARD) << "\tvlang:" << str; | 315 | // qCDebug(KCM_KEYBOARD) << "\tvlang:" << str; | ||
332 | } | 316 | } else if (strPath.endsWith(QLatin1String("modelList/model/configItem/name"))) { | ||
333 | else if( strPath.endsWith(QLatin1String("modelList/model/configItem/name")) ) { | | |||
334 | rules->modelInfos.last()->name = str.trimmed(); | 317 | rules->modelInfos.last()->name = str.trimmed(); | ||
335 | // qCDebug(KCM_KEYBOARD) << "name:" << str; | 318 | // qCDebug(KCM_KEYBOARD) << "name:" << str; | ||
336 | } | 319 | } else if (strPath.endsWith(QLatin1String("modelList/model/configItem/description"))) { | ||
337 | else if( strPath.endsWith(QLatin1String("modelList/model/configItem/description")) ) { | | |||
338 | rules->modelInfos.last()->description = str.trimmed(); | 320 | rules->modelInfos.last()->description = str.trimmed(); | ||
339 | // qCDebug(KCM_KEYBOARD) << "\tdescr:" << str; | 321 | // qCDebug(KCM_KEYBOARD) << "\tdescr:" << str; | ||
340 | } | 322 | } else if (strPath.endsWith(QLatin1String("modelList/model/configItem/vendor"))) { | ||
341 | else if( strPath.endsWith(QLatin1String("modelList/model/configItem/vendor")) ) { | | |||
342 | rules->modelInfos.last()->vendor = str.trimmed(); | 323 | rules->modelInfos.last()->vendor = str.trimmed(); | ||
343 | // qCDebug(KCM_KEYBOARD) << "\tvendor:" << str; | 324 | // qCDebug(KCM_KEYBOARD) << "\tvendor:" << str; | ||
344 | } | 325 | } else if (strPath.endsWith(QLatin1String("optionList/group/configItem/name"))) { | ||
345 | else if( strPath.endsWith(QLatin1String("optionList/group/configItem/name")) ) { | | |||
346 | rules->optionGroupInfos.last()->name = str.trimmed(); | 326 | rules->optionGroupInfos.last()->name = str.trimmed(); | ||
347 | // qCDebug(KCM_KEYBOARD) << "name:" << str; | 327 | // qCDebug(KCM_KEYBOARD) << "name:" << str; | ||
348 | } | 328 | } else if (strPath.endsWith(QLatin1String("optionList/group/configItem/description"))) { | ||
349 | else if( strPath.endsWith(QLatin1String("optionList/group/configItem/description")) ) { | | |||
350 | rules->optionGroupInfos.last()->description = str.trimmed(); | 329 | rules->optionGroupInfos.last()->description = str.trimmed(); | ||
351 | // qCDebug(KCM_KEYBOARD) << "\tdescr:" << str; | 330 | // qCDebug(KCM_KEYBOARD) << "\tdescr:" << str; | ||
352 | } | 331 | } else if (strPath.endsWith(QLatin1String("optionList/group/option/configItem/name"))) { | ||
353 | else if( strPath.endsWith(QLatin1String("optionList/group/option/configItem/name")) ) { | | |||
354 | rules->optionGroupInfos.last()->optionInfos.last()->name = str.trimmed(); | 332 | rules->optionGroupInfos.last()->optionInfos.last()->name = str.trimmed(); | ||
355 | // qCDebug(KCM_KEYBOARD) << "name:" << str; | 333 | // qCDebug(KCM_KEYBOARD) << "name:" << str; | ||
356 | } | 334 | } else if (strPath.endsWith(QLatin1String("optionList/group/option/configItem/description"))) { | ||
357 | else if( strPath.endsWith(QLatin1String("optionList/group/option/configItem/description")) ) { | | |||
358 | rules->optionGroupInfos.last()->optionInfos.last()->description = str.trimmed(); | 335 | rules->optionGroupInfos.last()->optionInfos.last()->description = str.trimmed(); | ||
359 | // qCDebug(KCM_KEYBOARD) << "\tdescr:" << str; | 336 | // qCDebug(KCM_KEYBOARD) << "\tdescr:" << str; | ||
360 | } | 337 | } | ||
361 | } | 338 | } | ||
362 | return true; | 339 | return true; | ||
363 | } | 340 | } | ||
364 | 341 | | |||
365 | bool LayoutInfo::isLanguageSupportedByLayout(const QString& lang) const | 342 | bool LayoutInfo::isLanguageSupportedByLayout(const QString& lang) const | ||
366 | { | 343 | { | ||
367 | if( languages.contains(lang) || isLanguageSupportedByVariants(lang) ) | 344 | if (languages.contains(lang) || isLanguageSupportedByVariants(lang)) | ||
368 | return true; | 345 | return true; | ||
369 | 346 | | |||
370 | // // return yes if no languages found in layout or its variants | 347 | // // return yes if no languages found in layout or its variants | ||
371 | // if( languages.empty() ) { | 348 | // if( languages.empty() ) { | ||
372 | // foreach(const VariantInfo* info, variantInfos) { | 349 | // foreach(const VariantInfo* info, variantInfos) { | ||
373 | // if( ! info->languages.empty() ) | 350 | // if( ! info->languages.empty() ) | ||
374 | // return false; | 351 | // return false; | ||
375 | // } | 352 | // } | ||
376 | // return true; | 353 | // return true; | ||
377 | // } | 354 | // } | ||
378 | 355 | | |||
379 | return false; | 356 | return false; | ||
380 | } | 357 | } | ||
381 | 358 | | |||
382 | bool LayoutInfo::isLanguageSupportedByVariants(const QString& lang) const | 359 | bool LayoutInfo::isLanguageSupportedByVariants(const QString& lang) const | ||
383 | { | 360 | { | ||
384 | foreach(const VariantInfo* info, variantInfos) { | 361 | foreach (const VariantInfo* info, variantInfos) { | ||
385 | if( info->languages.contains(lang) ) | 362 | if (info->languages.contains(lang)) | ||
386 | return true; | 363 | return true; | ||
387 | } | 364 | } | ||
388 | return false; | 365 | return false; | ||
389 | } | 366 | } | ||
390 | 367 | | |||
391 | bool LayoutInfo::isLanguageSupportedByDefaultVariant(const QString& lang) const | 368 | bool LayoutInfo::isLanguageSupportedByDefaultVariant(const QString& lang) const | ||
392 | { | 369 | { | ||
393 | if( languages.contains(lang) ) | 370 | if (languages.contains(lang)) | ||
394 | return true; | 371 | return true; | ||
395 | 372 | | |||
396 | if( languages.empty() && isLanguageSupportedByVariants(lang) ) | 373 | if (languages.empty() && isLanguageSupportedByVariants(lang)) | ||
397 | return true; | 374 | return true; | ||
398 | 375 | | |||
399 | return false; | 376 | return false; | ||
400 | } | 377 | } | ||
401 | 378 | | |||
402 | bool LayoutInfo::isLanguageSupportedByVariant(const VariantInfo* variantInfo, const QString& lang) const | 379 | bool LayoutInfo::isLanguageSupportedByVariant(const VariantInfo* variantInfo, const QString& lang) const | ||
403 | { | 380 | { | ||
404 | if( variantInfo->languages.contains(lang) ) | 381 | if (variantInfo->languages.contains(lang)) | ||
405 | return true; | 382 | return true; | ||
406 | 383 | | |||
407 | // if variant has no languages try to "inherit" them from layout | 384 | // if variant has no languages try to "inherit" them from layout | ||
408 | if( variantInfo->languages.empty() && languages.contains(lang) ) | 385 | if (variantInfo->languages.empty() && languages.contains(lang)) | ||
409 | return true; | 386 | return true; | ||
410 | 387 | | |||
411 | return false; | 388 | return false; | ||
412 | } | 389 | } | ||
413 | | ||||
414 | #ifdef NEW_GEOMETRY | | |||
415 | | ||||
416 | Rules::GeometryId Rules::getGeometryId(const QString& model) { | | |||
417 | QString xkbDir = Rules::findXkbDir(); | | |||
418 | QString rulesName = Rules::getRulesName(); | | |||
419 | QString ruleFileName = QStringLiteral("%1/rules/%2").arg(xkbDir, rulesName); | | |||
420 | QFile ruleFile(ruleFileName); | | |||
421 | | ||||
422 | GeometryId defaultGeoId(QStringLiteral("pc"), QStringLiteral("pc104")); | | |||
423 | | ||||
424 | if ( ! ruleFile.open(QIODevice::ReadOnly | QIODevice::Text) ){ | | |||
425 | qCCritical(KCM_KEYBOARD) << "Unable to open file" << ruleFileName; | | |||
426 | return defaultGeoId; | | |||
427 | } | | |||
428 | | ||||
429 | QString modelGeoId = model; | | |||
430 | bool inTable = false; | | |||
431 | QTextStream in(&ruleFile); | | |||
432 | | ||||
433 | while (!in.atEnd()) { | | |||
434 | QString line = in.readLine().trimmed(); | | |||
435 | | ||||
436 | if( line.isEmpty() || QRegExp(QStringLiteral("^\\s*//")).indexIn(line) != -1 ) | | |||
437 | continue; | | |||
438 | | ||||
439 | QRegExp modelGroupRegex(QStringLiteral("!\\s*(\\$[a-zA-Z0-9_]+)\\s*=(.*)")); | | |||
440 | | ||||
441 | if( modelGroupRegex.indexIn(line) != -1 ) { | | |||
442 | QStringList parts = modelGroupRegex.capturedTexts(); | | |||
443 | QString groupName = parts[1]; | | |||
444 | QStringList models = parts[2].split(QRegExp(QStringLiteral("\\s+")), QString::SkipEmptyParts); | | |||
445 | | ||||
446 | // qCDebug(KCM_KEYBOARD) << "modelGroup definition" << groupName << ":" << models; | | |||
447 | if( models.contains(model) ) { | | |||
448 | modelGeoId = groupName; | | |||
449 | } | | |||
450 | continue; | | |||
451 | } | | |||
452 | | ||||
453 | | ||||
454 | if( inTable ) { | | |||
455 | QRegExp modelTableEntry (QStringLiteral("\\s*(\\$?[a-zA-Z0-9_]+|\\*)\\s*=\\s*([a-zA-Z0-9_]+)\\(([a-zA-Z0-9_%]+)\\)")); | | |||
456 | if( modelTableEntry.indexIn(line) == -1 ) { | | |||
457 | if( QRegExp(QStringLiteral("^!\\s*")).indexIn(line) != -1 ) | | |||
458 | break; | | |||
459 | | ||||
460 | qCWarning(KCM_KEYBOARD) << "could not parse geometry line" << line; | | |||
461 | continue; | | |||
462 | } | | |||
463 | | ||||
464 | QStringList parts = modelTableEntry.capturedTexts(); | | |||
465 | QString modelName = parts[1]; | | |||
466 | QString fileName = parts[2]; | | |||
467 | QString geoName = parts[3]; | | |||
468 | if( geoName == QLatin1String("%m") ) { | | |||
469 | geoName = model; | | |||
470 | } | | |||
471 | if( modelName == QLatin1String("*") ) { | | |||
472 | defaultGeoId = GeometryId(fileName, geoName); | | |||
473 | } | | |||
474 | | ||||
475 | // qCDebug(KCM_KEYBOARD) << "geo entry" << modelName << fileName << geoName; | | |||
476 | | ||||
477 | if( modelName == model ) { | | |||
478 | return GeometryId(fileName, geoName); | | |||
479 | } | | |||
480 | | ||||
481 | continue; | | |||
482 | } | | |||
483 | | ||||
484 | QRegExp modelTableHeader (QStringLiteral("!\\s+model\\s*=\\s*geometry")); | | |||
485 | if( modelTableHeader.indexIn(line) != -1 ) { | | |||
486 | inTable = true; | | |||
487 | continue; | | |||
488 | } | | |||
489 | | ||||
490 | } | | |||
491 | | ||||
492 | return defaultGeoId; | | |||
493 | } | | |||
494 | | ||||
495 | #endif | |