1 /***************************************************************************
2 * Copyright (C) 2005-09 by the Quassel Project *
3 * devel@quassel-irc.org *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) version 3. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
21 #include <QApplication>
23 #include "qssparser.h"
25 QssParser::QssParser()
28 _palette = QApplication::palette();
30 // Init palette color roles
31 _paletteColorRoles["alternate-base"] = QPalette::AlternateBase;
32 _paletteColorRoles["background"] = QPalette::Background;
33 _paletteColorRoles["base"] = QPalette::Base;
34 _paletteColorRoles["bright-text"] = QPalette::BrightText;
35 _paletteColorRoles["button"] = QPalette::Button;
36 _paletteColorRoles["button-text"] = QPalette::ButtonText;
37 _paletteColorRoles["dark"] = QPalette::Dark;
38 _paletteColorRoles["foreground"] = QPalette::Foreground;
39 _paletteColorRoles["highlight"] = QPalette::Highlight;
40 _paletteColorRoles["highlighted-text"] = QPalette::HighlightedText;
41 _paletteColorRoles["light"] = QPalette::Light;
42 _paletteColorRoles["link"] = QPalette::Link;
43 _paletteColorRoles["link-visited"] = QPalette::LinkVisited;
44 _paletteColorRoles["mid"] = QPalette::Mid;
45 _paletteColorRoles["midlight"] = QPalette::Midlight;
46 _paletteColorRoles["shadow"] = QPalette::Shadow;
47 _paletteColorRoles["text"] = QPalette::Text;
48 _paletteColorRoles["tooltip-base"] = QPalette::ToolTipBase;
49 _paletteColorRoles["tooltip-text"] = QPalette::ToolTipText;
50 _paletteColorRoles["window"] = QPalette::Window;
51 _paletteColorRoles["window-text"] = QPalette::WindowText;
53 _uiStylePalette = QVector<QBrush>(UiStyle::NumRoles, QBrush());
55 _uiStyleColorRoles["marker-line"] = UiStyle::MarkerLine;
58 void QssParser::processStyleSheet(QString &ss) {
62 // Remove C-style comments /* */ or //
63 QRegExp commentRx("(//.*(\\n|$)|/\\*.*\\*/)");
64 commentRx.setMinimal(true);
67 // Palette definitions first, so we can apply roles later on
68 QRegExp paletterx("(Palette[^{]*)\\{([^}]+)\\}");
70 while((pos = paletterx.indexIn(ss, pos)) >= 0) {
71 parsePaletteBlock(paletterx.cap(1).trimmed(), paletterx.cap(2).trimmed());
72 ss.remove(pos, paletterx.matchedLength());
75 // Now we can parse the rest of our custom blocks
76 QRegExp blockrx("((?:ChatLine|ChatListItem|NickListItem)[^{]*)\\{([^}]+)\\}");
78 while((pos = blockrx.indexIn(ss, pos)) >= 0) {
79 //qDebug() << blockrx.cap(1) << blockrx.cap(2);
80 QString declaration = blockrx.cap(1).trimmed();
81 QString contents = blockrx.cap(2).trimmed();
83 if(declaration.startsWith("ChatLine"))
84 parseChatLineBlock(declaration, contents);
85 else if(declaration.startsWith("ChatListItem") || declaration.startsWith("NickListItem"))
86 parseListItemBlock(declaration, contents);
88 // TODO: add moar here
90 ss.remove(pos, blockrx.matchedLength());
94 /******** Parse a whole block: declaration { contents } *******/
96 void QssParser::parseChatLineBlock(const QString &decl, const QString &contents) {
97 quint64 fmtType = parseFormatType(decl);
98 if(fmtType == UiStyle::Invalid)
101 _formats[fmtType].merge(parseFormat(contents));
104 void QssParser::parseListItemBlock(const QString &decl, const QString &contents) {
105 quint32 fmtType = parseItemFormatType(decl);
106 if(fmtType == UiStyle::Invalid)
109 _listItemFormats[fmtType].merge(parseFormat(contents));
112 // Palette { ... } specifies the application palette
113 // ColorGroups can be specified like pseudo states, chaining is OR (contrary to normal CSS handling):
114 // Palette:inactive:disabled { ... } applies to both the Inactive and the Disabled state
115 void QssParser::parsePaletteBlock(const QString &decl, const QString &contents) {
116 QList<QPalette::ColorGroup> colorGroups;
118 // Check if we want to apply this palette definition for particular ColorGroups
119 QRegExp rx("Palette((:(normal|active|inactive|disabled))*)");
120 if(!rx.exactMatch(decl)) {
121 qWarning() << Q_FUNC_INFO << tr("Invalid block declaration: %1").arg(decl);
124 if(!rx.cap(1).isEmpty()) {
125 QStringList groups = rx.cap(1).split(':', QString::SkipEmptyParts);
126 foreach(QString g, groups) {
127 if((g == "normal" || g == "active") && !colorGroups.contains(QPalette::Active))
128 colorGroups.append(QPalette::Active);
129 else if(g == "inactive" && !colorGroups.contains(QPalette::Inactive))
130 colorGroups.append(QPalette::Inactive);
131 else if(g == "disabled" && !colorGroups.contains(QPalette::Disabled))
132 colorGroups.append(QPalette::Disabled);
136 // Now let's go through the roles
137 foreach(QString line, contents.split(';', QString::SkipEmptyParts)) {
138 int idx = line.indexOf(':');
140 qWarning() << Q_FUNC_INFO << tr("Invalid palette role assignment: %1").arg(line.trimmed());
143 QString rolestr = line.left(idx).trimmed();
144 QString brushstr = line.mid(idx + 1).trimmed();
146 if(_paletteColorRoles.contains(rolestr)) {
147 QBrush brush = parseBrush(brushstr);
148 if(colorGroups.count()) {
149 foreach(QPalette::ColorGroup group, colorGroups)
150 _palette.setBrush(group, _paletteColorRoles.value(rolestr), brush);
152 _palette.setBrush(_paletteColorRoles.value(rolestr), brush);
153 } else if(_uiStyleColorRoles.contains(rolestr)) {
154 _uiStylePalette[_uiStyleColorRoles.value(rolestr)] = parseBrush(brushstr);
156 qWarning() << Q_FUNC_INFO << tr("Unknown palette role name: %1").arg(rolestr);
160 /******** Determine format types from a block declaration ********/
162 quint64 QssParser::parseFormatType(const QString &decl) {
163 QRegExp rx("ChatLine(?:::(\\w+))?(?:#([\\w\\-]+))?(?:\\[([=-,\\\"\\w\\s]+)\\])?");
164 // $1: subelement; $2: msgtype; $3: conditionals
165 if(!rx.exactMatch(decl)) {
166 qWarning() << Q_FUNC_INFO << tr("Invalid block declaration: %1").arg(decl);
167 return UiStyle::Invalid;
169 QString subElement = rx.cap(1);
170 QString msgType = rx.cap(2);
171 QString conditions = rx.cap(3);
175 // First determine the subelement
176 if(!subElement.isEmpty()) {
177 if(subElement == "timestamp")
178 fmtType |= UiStyle::Timestamp;
179 else if(subElement == "sender")
180 fmtType |= UiStyle::Sender;
181 else if(subElement == "nick")
182 fmtType |= UiStyle::Nick;
183 else if(subElement == "contents")
184 fmtType |= UiStyle::Contents;
185 else if(subElement == "hostmask")
186 fmtType |= UiStyle::Hostmask;
187 else if(subElement == "modeflags")
188 fmtType |= UiStyle::ModeFlags;
189 else if(subElement == "url")
190 fmtType |= UiStyle::Url;
192 qWarning() << Q_FUNC_INFO << tr("Invalid subelement name in %1").arg(decl);
193 return UiStyle::Invalid;
197 // Now, figure out the message type
198 if(!msgType.isEmpty()) {
199 if(msgType == "plain")
200 fmtType |= UiStyle::PlainMsg;
201 else if(msgType == "notice")
202 fmtType |= UiStyle::NoticeMsg;
203 else if(msgType == "action")
204 fmtType |= UiStyle::ActionMsg;
205 else if(msgType == "nick")
206 fmtType |= UiStyle::NickMsg;
207 else if(msgType == "mode")
208 fmtType |= UiStyle::ModeMsg;
209 else if(msgType == "join")
210 fmtType |= UiStyle::JoinMsg;
211 else if(msgType == "part")
212 fmtType |= UiStyle::PartMsg;
213 else if(msgType == "quit")
214 fmtType |= UiStyle::QuitMsg;
215 else if(msgType == "kick")
216 fmtType |= UiStyle::KickMsg;
217 else if(msgType == "kill")
218 fmtType |= UiStyle::KillMsg;
219 else if(msgType == "server")
220 fmtType |= UiStyle::ServerMsg;
221 else if(msgType == "info")
222 fmtType |= UiStyle::InfoMsg;
223 else if(msgType == "error")
224 fmtType |= UiStyle::ErrorMsg;
225 else if(msgType == "daychange")
226 fmtType |= UiStyle::DayChangeMsg;
227 else if(msgType == "topic")
228 fmtType |= UiStyle::TopicMsg;
229 else if(msgType == "netsplit-join")
230 fmtType |= UiStyle::NetsplitJoinMsg;
231 else if(msgType == "netsplit-quit")
232 fmtType |= UiStyle::NetsplitQuitMsg;
233 else if(msgType == "invite")
234 fmtType |= UiStyle::InviteMsg;
236 qWarning() << Q_FUNC_INFO << tr("Invalid message type in %1").arg(decl);
240 // Next up: conditional (formats, labels, nickhash)
241 QRegExp condRx("\\s*([\\w\\-]+)\\s*=\\s*\"(\\w+)\"\\s*");
242 if(!conditions.isEmpty()) {
243 foreach(const QString &cond, conditions.split(',', QString::SkipEmptyParts)) {
244 if(!condRx.exactMatch(cond)) {
245 qWarning() << Q_FUNC_INFO << tr("Invalid condition %1").arg(cond);
246 return UiStyle::Invalid;
248 QString condName = condRx.cap(1);
249 QString condValue = condRx.cap(2);
250 if(condName == "label") {
251 quint64 labeltype = 0;
252 if(condValue == "highlight")
253 labeltype = UiStyle::Highlight;
254 else if(condValue == "selected")
255 labeltype = UiStyle::Selected;
257 qWarning() << Q_FUNC_INFO << tr("Invalid message label: %1").arg(condValue);
258 return UiStyle::Invalid;
260 fmtType |= (labeltype << 32);
261 } else if(condName == "sender") {
262 if(condValue == "self")
263 fmtType |= (quint64)UiStyle::OwnMsg << 32; // sender="self" is actually treated as a label
266 quint64 val = condValue.toUInt(&ok, 16);
268 qWarning() << Q_FUNC_INFO << tr("Invalid senderhash specification: %1").arg(condValue);
269 return UiStyle::Invalid;
272 qWarning() << Q_FUNC_INFO << tr("Senderhash can be at most \"0x0f\"!");
273 return UiStyle::Invalid;
275 fmtType |= ++val << 48;
277 } else if(condName == "format") {
278 if(condValue == "bold")
279 fmtType |= UiStyle::Bold;
280 else if(condValue == "italic")
281 fmtType |= UiStyle::Italic;
282 else if(condValue == "underline")
283 fmtType |= UiStyle::Underline;
284 else if(condValue == "reverse")
285 fmtType |= UiStyle::Reverse;
287 qWarning() << Q_FUNC_INFO << tr("Invalid format name: %1").arg(condValue);
288 return UiStyle::Invalid;
290 } else if(condName == "fg-color" || condName == "bg-color") {
292 quint8 col = condValue.toUInt(&ok, 16);
293 if(!ok || col > 0x0f) {
294 qWarning() << Q_FUNC_INFO << tr("Illegal IRC color specification (must be between 00 and 0f): %1").arg(condValue);
295 return UiStyle::Invalid;
297 if(condName == "fg-color")
298 fmtType |= 0x00400000 | (quint32)(col << 24);
300 fmtType |= 0x00800000 | (quint32)(col << 28);
302 qWarning() << Q_FUNC_INFO << tr("Unhandled condition: %1").arg(condName);
303 return UiStyle::Invalid;
311 // FIXME: Code duplication
312 quint32 QssParser::parseItemFormatType(const QString &decl) {
313 QRegExp rx("(Chat|Nick)ListItem(?:\\[([=-,\\\"\\w\\s]+)\\])?");
314 // $1: item type; $2: properties
315 if(!rx.exactMatch(decl)) {
316 qWarning() << Q_FUNC_INFO << tr("Invalid block declaration: %1").arg(decl);
317 return UiStyle::Invalid;
319 QString mainItemType = rx.cap(1);
320 QString properties = rx.cap(2);
324 // Next up: properties
326 if(!properties.isEmpty()) {
327 QHash<QString, QString> props;
328 QRegExp propRx("\\s*([\\w\\-]+)\\s*=\\s*\"([\\w\\-]+)\"\\s*");
329 foreach(const QString &prop, properties.split(',', QString::SkipEmptyParts)) {
330 if(!propRx.exactMatch(prop)) {
331 qWarning() << Q_FUNC_INFO << tr("Invalid proplist %1").arg(prop);
332 return UiStyle::Invalid;
334 props[propRx.cap(1)] = propRx.cap(2);
336 type = props.value("type");
337 state = props.value("state");
340 if(mainItemType == "Chat") {
341 fmtType |= UiStyle::BufferViewItem;
342 if(!type.isEmpty()) {
343 if(type == "network")
344 fmtType |= UiStyle::NetworkItem;
345 else if(type == "channel")
346 fmtType |= UiStyle::ChannelBufferItem;
347 else if(type == "query")
348 fmtType |= UiStyle::QueryBufferItem;
350 qWarning() << Q_FUNC_INFO << tr("Invalid chatlist item type %1").arg(type);
351 return UiStyle::Invalid;
354 if(!state.isEmpty()) {
355 if(state == "inactive")
356 fmtType |= UiStyle::InactiveBuffer;
357 else if(state == "channel-event")
358 fmtType |= UiStyle::ActiveBuffer;
359 else if(state == "unread-message")
360 fmtType |= UiStyle::UnreadBuffer;
361 else if(state == "highlighted")
362 fmtType |= UiStyle::HighlightedBuffer;
363 else if(state == "away")
364 fmtType |= UiStyle::UserAway;
366 qWarning() << Q_FUNC_INFO << tr("Invalid chatlist state %1").arg(state);
367 return UiStyle::Invalid;
371 fmtType |= UiStyle::NickViewItem;
372 if(!type.isEmpty()) {
374 fmtType |= UiStyle::IrcUserItem;
376 fmtType |= UiStyle::UserAway;
377 } else if(type == "category")
378 fmtType |= UiStyle::UserCategoryItem;
384 /******** Parse a whole format attribute block ********/
386 QTextCharFormat QssParser::parseFormat(const QString &qss) {
387 QTextCharFormat format;
389 foreach(QString line, qss.split(';', QString::SkipEmptyParts)) {
390 int idx = line.indexOf(':');
392 qWarning() << Q_FUNC_INFO << tr("Invalid property declaration: %1").arg(line.trimmed());
395 QString property = line.left(idx).trimmed();
396 QString value = line.mid(idx + 1).simplified();
398 if(property == "background" || property == "background-color")
399 format.setBackground(parseBrush(value));
400 else if(property == "foreground" || property == "color")
401 format.setForeground(parseBrush(value));
403 // font-related properties
404 else if(property.startsWith("font")) {
405 if(property == "font")
406 parseFont(value, &format);
407 else if(property == "font-style")
408 parseFontStyle(value, &format);
409 else if(property == "font-weight")
410 parseFontWeight(value, &format);
411 else if(property == "font-size")
412 parseFontSize(value, &format);
413 else if(property == "font-family")
414 parseFontFamily(value, &format);
416 qWarning() << Q_FUNC_INFO << tr("Invalid font property: %1").arg(line);
422 qWarning() << Q_FUNC_INFO << tr("Unknown ChatLine property: %1").arg(property);
429 /******** Brush ********/
431 QBrush QssParser::parseBrush(const QString &str, bool *ok) {
434 QColor c = parseColor(str);
441 if(str.startsWith("palette")) { // Palette color role
442 QRegExp rx("palette\\s*\\(\\s*([a-z-]+)\\s*\\)");
443 if(!rx.exactMatch(str)) {
444 qWarning() << Q_FUNC_INFO << tr("Invalid palette color role specification: %1").arg(str);
447 if(_paletteColorRoles.contains(rx.cap(1)))
448 return QBrush(_palette.brush(_paletteColorRoles.value(rx.cap(1))));
449 if(_uiStyleColorRoles.contains(rx.cap(1)))
450 return QBrush(_uiStylePalette.at(_uiStyleColorRoles.value(rx.cap(1))));
451 qWarning() << Q_FUNC_INFO << tr("Unknown palette color role: %1").arg(rx.cap(1));
454 } else if(str.startsWith("qlineargradient")) {
455 static QString rxFloat("\\s*(-?\\s*[0-9]*\\.?[0-9]+)\\s*");
456 QRegExp rx(QString("qlineargradient\\s*\\(\\s*x1:%1,\\s*y1:%1,\\s*x2:%1,\\s*y2:%1,(.+)\\)").arg(rxFloat));
457 if(!rx.exactMatch(str)) {
458 qWarning() << Q_FUNC_INFO << tr("Invalid gradient declaration: %1").arg(str);
461 qreal x1 = rx.cap(1).toDouble();
462 qreal y1 = rx.cap(2).toDouble();
463 qreal x2 = rx.cap(3).toDouble();
464 qreal y2 = rx.cap(4).toDouble();
465 QGradientStops stops = parseGradientStops(rx.cap(5).trimmed());
467 qWarning() << Q_FUNC_INFO << tr("Invalid gradient stops list: %1").arg(str);
470 QLinearGradient gradient(x1, y1, x2, y2);
471 gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
472 gradient.setStops(stops);
475 return QBrush(gradient);
477 } else if(str.startsWith("qconicalgradient")) {
478 static QString rxFloat("\\s*(-?\\s*[0-9]*\\.?[0-9]+)\\s*");
479 QRegExp rx(QString("qconicalgradient\\s*\\(\\s*cx:%1,\\s*cy:%1,\\s*angle:%1,(.+)\\)").arg(rxFloat));
480 if(!rx.exactMatch(str)) {
481 qWarning() << Q_FUNC_INFO << tr("Invalid gradient declaration: %1").arg(str);
484 qreal cx = rx.cap(1).toDouble();
485 qreal cy = rx.cap(2).toDouble();
486 qreal angle = rx.cap(3).toDouble();
487 QGradientStops stops = parseGradientStops(rx.cap(4).trimmed());
489 qWarning() << Q_FUNC_INFO << tr("Invalid gradient stops list: %1").arg(str);
492 QConicalGradient gradient(cx, cy, angle);
493 gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
494 gradient.setStops(stops);
497 return QBrush(gradient);
499 } else if(str.startsWith("qradialgradient")) {
500 static QString rxFloat("\\s*(-?\\s*[0-9]*\\.?[0-9]+)\\s*");
501 QRegExp rx(QString("qradialgradient\\s*\\(\\s*cx:%1,\\s*cy:%1,\\s*radius:%1,\\s*fx:%1,\\s*fy:%1,(.+)\\)").arg(rxFloat));
502 if(!rx.exactMatch(str)) {
503 qWarning() << Q_FUNC_INFO << tr("Invalid gradient declaration: %1").arg(str);
506 qreal cx = rx.cap(1).toDouble();
507 qreal cy = rx.cap(2).toDouble();
508 qreal radius = rx.cap(3).toDouble();
509 qreal fx = rx.cap(4).toDouble();
510 qreal fy = rx.cap(5).toDouble();
511 QGradientStops stops = parseGradientStops(rx.cap(6).trimmed());
513 qWarning() << Q_FUNC_INFO << tr("Invalid gradient stops list: %1").arg(str);
516 QRadialGradient gradient(cx, cy, radius, fx, fy);
517 gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
518 gradient.setStops(stops);
521 return QBrush(gradient);
527 QColor QssParser::parseColor(const QString &str) {
528 if(str.startsWith("rgba")) {
529 ColorTuple tuple = parseColorTuple(str.mid(4));
530 if(tuple.count() == 4)
531 return QColor(tuple.at(0), tuple.at(1), tuple.at(2), tuple.at(3));
532 } else if(str.startsWith("rgb")) {
533 ColorTuple tuple = parseColorTuple(str.mid(3));
534 if(tuple.count() == 3)
535 return QColor(tuple.at(0), tuple.at(1), tuple.at(2));
536 } else if(str.startsWith("hsva")) {
537 ColorTuple tuple = parseColorTuple(str.mid(4));
538 if(tuple.count() == 4) {
540 c.setHsvF(tuple.at(0), tuple.at(1), tuple.at(2), tuple.at(3));
543 } else if(str.startsWith("hsv")) {
544 ColorTuple tuple = parseColorTuple(str.mid(3));
545 if(tuple.count() == 3) {
547 c.setHsvF(tuple.at(0), tuple.at(1), tuple.at(2));
551 QRegExp rx("#?[0-9A-Fa-z]+");
552 if(rx.exactMatch(str))
558 // get a list of comma-separated int values or percentages (rel to 0-255)
559 QssParser::ColorTuple QssParser::parseColorTuple(const QString &str) {
561 QRegExp rx("\\(((\\s*[0-9]{1,3}%?\\s*)(,\\s*[0-9]{1,3}%?\\s*)*)\\)");
562 if(!rx.exactMatch(str.trimmed())) {
565 QStringList values = rx.cap(1).split(',');
566 foreach(QString v, values) {
571 if(v.endsWith('%')) {
575 val = (qreal)v.toUInt(&ok);
585 QGradientStops QssParser::parseGradientStops(const QString &str_) {
587 QGradientStops result;
588 static QString rxFloat("(0?\\.[0-9]+|[01])"); // values between 0 and 1
589 QRegExp rx(QString("\\s*,?\\s*stop:\\s*(%1)\\s+([^:]+)(,\\s*stop:|$)").arg(rxFloat));
591 while((idx = rx.indexIn(str)) == 0) {
592 qreal x = rx.cap(1).toDouble();
593 QColor c = parseColor(rx.cap(3));
595 return QGradientStops();
596 result << QGradientStop(x, c);
597 str.remove(0, rx.matchedLength() - rx.cap(4).length());
599 if(!str.trimmed().isEmpty())
600 return QGradientStops();
605 /******** Font Properties ********/
607 void QssParser::parseFont(const QString& value, QTextCharFormat* format) {
608 QRegExp rx("((?:(?:normal|italic|oblique|underline|bold|100|200|300|400|500|600|700|800|900) ){0,2}) ?(\\d+)(pt|px)? \"(.*)\"");
609 if(!rx.exactMatch(value)) {
610 qWarning() << Q_FUNC_INFO << tr("Invalid font specification: %1").arg(value);
613 format->setFontItalic(false);
614 format->setFontWeight(QFont::Normal);
615 QStringList proplist = rx.cap(1).split(' ', QString::SkipEmptyParts);
616 foreach(QString prop, proplist) {
618 format->setFontItalic(true);
619 else if(prop == "underline")
620 format->setFontUnderline(true);
621 //else if(prop == "oblique")
622 // format->setStyle(QFont::StyleOblique);
623 else if(prop == "bold")
624 format->setFontWeight(QFont::Bold);
626 int w = prop.toInt();
627 format->setFontWeight(qMin(w / 8, 99)); // taken from Qt's qss parser
631 if(rx.cap(3) == "px")
632 format->setProperty(QTextFormat::FontPixelSize, rx.cap(2).toInt());
634 format->setFontPointSize(rx.cap(2).toInt());
636 format->setFontFamily(rx.cap(4));
639 void QssParser::parseFontStyle(const QString& value, QTextCharFormat* format) {
640 if(value == "normal")
641 format->setFontItalic(false);
642 else if(value == "italic")
643 format->setFontItalic(true);
644 else if(value == "underline")
645 format->setFontUnderline(true);
646 //else if(value == "oblique")
647 // format->setStyle(QFont::StyleOblique);
649 qWarning() << Q_FUNC_INFO << tr("Invalid font style specification: %1").arg(value);
653 void QssParser::parseFontWeight(const QString& value, QTextCharFormat* format) {
654 if(value == "normal")
655 format->setFontWeight(QFont::Normal);
656 else if(value == "bold")
657 format->setFontWeight(QFont::Bold);
660 int w = value.toInt(&ok);
662 qWarning() << Q_FUNC_INFO << tr("Invalid font weight specification: %1").arg(value);
665 format->setFontWeight(qMin(w / 8, 99)); // taken from Qt's qss parser
669 void QssParser::parseFontSize(const QString& value, QTextCharFormat* format) {
670 QRegExp rx("(\\d+)(pt|px)");
671 if(!rx.exactMatch(value)) {
672 qWarning() << Q_FUNC_INFO << tr("Invalid font size specification: %1").arg(value);
675 if(rx.cap(2) == "px")
676 format->setProperty(QTextFormat::FontPixelSize, rx.cap(1).toInt());
678 format->setFontPointSize(rx.cap(1).toInt());
681 void QssParser::parseFontFamily(const QString& value, QTextCharFormat* format) {
682 QString family = value;
683 if(family.startsWith('"') && family.endsWith('"')) {
684 family = family.mid(1, family.length() - 2);
686 format->setFontFamily(family);