1 /***************************************************************************
2 * Copyright (C) 2005-2012 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 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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;
59 void QssParser::processStyleSheet(QString &ss)
64 // Remove C-style comments /* */ or //
65 QRegExp commentRx("(//.*(\\n|$)|/\\*.*\\*/)");
66 commentRx.setMinimal(true);
69 // Palette definitions first, so we can apply roles later on
70 QRegExp paletterx("(Palette[^{]*)\\{([^}]+)\\}");
72 while ((pos = paletterx.indexIn(ss, pos)) >= 0) {
73 parsePaletteBlock(paletterx.cap(1).trimmed(), paletterx.cap(2).trimmed());
74 ss.remove(pos, paletterx.matchedLength());
77 // Now we can parse the rest of our custom blocks
78 QRegExp blockrx("((?:ChatLine|ChatListItem|NickListItem)[^{]*)\\{([^}]+)\\}");
80 while ((pos = blockrx.indexIn(ss, pos)) >= 0) {
81 //qDebug() << blockrx.cap(1) << blockrx.cap(2);
82 QString declaration = blockrx.cap(1).trimmed();
83 QString contents = blockrx.cap(2).trimmed();
85 if (declaration.startsWith("ChatLine"))
86 parseChatLineBlock(declaration, contents);
87 else if (declaration.startsWith("ChatListItem") || declaration.startsWith("NickListItem"))
88 parseListItemBlock(declaration, contents);
90 // TODO: add moar here
92 ss.remove(pos, blockrx.matchedLength());
97 /******** Parse a whole block: declaration { contents } *******/
99 void QssParser::parseChatLineBlock(const QString &decl, const QString &contents)
101 quint64 fmtType = parseFormatType(decl);
102 if (fmtType == UiStyle::Invalid)
105 _formats[fmtType].merge(parseFormat(contents));
109 void QssParser::parseListItemBlock(const QString &decl, const QString &contents)
111 quint32 fmtType = parseItemFormatType(decl);
112 if (fmtType == UiStyle::Invalid)
115 _listItemFormats[fmtType].merge(parseFormat(contents));
119 // Palette { ... } specifies the application palette
120 // ColorGroups can be specified like pseudo states, chaining is OR (contrary to normal CSS handling):
121 // Palette:inactive:disabled { ... } applies to both the Inactive and the Disabled state
122 void QssParser::parsePaletteBlock(const QString &decl, const QString &contents)
124 QList<QPalette::ColorGroup> colorGroups;
126 // Check if we want to apply this palette definition for particular ColorGroups
127 QRegExp rx("Palette((:(normal|active|inactive|disabled))*)");
128 if (!rx.exactMatch(decl)) {
129 qWarning() << Q_FUNC_INFO << tr("Invalid block declaration: %1").arg(decl);
132 if (!rx.cap(1).isEmpty()) {
133 QStringList groups = rx.cap(1).split(':', QString::SkipEmptyParts);
134 foreach(QString g, groups) {
135 if ((g == "normal" || g == "active") && !colorGroups.contains(QPalette::Active))
136 colorGroups.append(QPalette::Active);
137 else if (g == "inactive" && !colorGroups.contains(QPalette::Inactive))
138 colorGroups.append(QPalette::Inactive);
139 else if (g == "disabled" && !colorGroups.contains(QPalette::Disabled))
140 colorGroups.append(QPalette::Disabled);
144 // Now let's go through the roles
145 foreach(QString line, contents.split(';', QString::SkipEmptyParts)) {
146 int idx = line.indexOf(':');
148 qWarning() << Q_FUNC_INFO << tr("Invalid palette role assignment: %1").arg(line.trimmed());
151 QString rolestr = line.left(idx).trimmed();
152 QString brushstr = line.mid(idx + 1).trimmed();
154 if (_paletteColorRoles.contains(rolestr)) {
155 QBrush brush = parseBrush(brushstr);
156 if (colorGroups.count()) {
157 foreach(QPalette::ColorGroup group, colorGroups)
158 _palette.setBrush(group, _paletteColorRoles.value(rolestr), brush);
161 _palette.setBrush(_paletteColorRoles.value(rolestr), brush);
163 else if (_uiStyleColorRoles.contains(rolestr)) {
164 _uiStylePalette[_uiStyleColorRoles.value(rolestr)] = parseBrush(brushstr);
167 qWarning() << Q_FUNC_INFO << tr("Unknown palette role name: %1").arg(rolestr);
172 /******** Determine format types from a block declaration ********/
174 quint64 QssParser::parseFormatType(const QString &decl)
176 QRegExp rx("ChatLine(?:::(\\w+))?(?:#([\\w\\-]+))?(?:\\[([=-,\\\"\\w\\s]+)\\])?");
177 // $1: subelement; $2: msgtype; $3: conditionals
178 if (!rx.exactMatch(decl)) {
179 qWarning() << Q_FUNC_INFO << tr("Invalid block declaration: %1").arg(decl);
180 return UiStyle::Invalid;
182 QString subElement = rx.cap(1);
183 QString msgType = rx.cap(2);
184 QString conditions = rx.cap(3);
188 // First determine the subelement
189 if (!subElement.isEmpty()) {
190 if (subElement == "timestamp")
191 fmtType |= UiStyle::Timestamp;
192 else if (subElement == "sender")
193 fmtType |= UiStyle::Sender;
194 else if (subElement == "nick")
195 fmtType |= UiStyle::Nick;
196 else if (subElement == "contents")
197 fmtType |= UiStyle::Contents;
198 else if (subElement == "hostmask")
199 fmtType |= UiStyle::Hostmask;
200 else if (subElement == "modeflags")
201 fmtType |= UiStyle::ModeFlags;
202 else if (subElement == "url")
203 fmtType |= UiStyle::Url;
205 qWarning() << Q_FUNC_INFO << tr("Invalid subelement name in %1").arg(decl);
206 return UiStyle::Invalid;
210 // Now, figure out the message type
211 if (!msgType.isEmpty()) {
212 if (msgType == "plain")
213 fmtType |= UiStyle::PlainMsg;
214 else if (msgType == "notice")
215 fmtType |= UiStyle::NoticeMsg;
216 else if (msgType == "action")
217 fmtType |= UiStyle::ActionMsg;
218 else if (msgType == "nick")
219 fmtType |= UiStyle::NickMsg;
220 else if (msgType == "mode")
221 fmtType |= UiStyle::ModeMsg;
222 else if (msgType == "join")
223 fmtType |= UiStyle::JoinMsg;
224 else if (msgType == "part")
225 fmtType |= UiStyle::PartMsg;
226 else if (msgType == "quit")
227 fmtType |= UiStyle::QuitMsg;
228 else if (msgType == "kick")
229 fmtType |= UiStyle::KickMsg;
230 else if (msgType == "kill")
231 fmtType |= UiStyle::KillMsg;
232 else if (msgType == "server")
233 fmtType |= UiStyle::ServerMsg;
234 else if (msgType == "info")
235 fmtType |= UiStyle::InfoMsg;
236 else if (msgType == "error")
237 fmtType |= UiStyle::ErrorMsg;
238 else if (msgType == "daychange")
239 fmtType |= UiStyle::DayChangeMsg;
240 else if (msgType == "topic")
241 fmtType |= UiStyle::TopicMsg;
242 else if (msgType == "netsplit-join")
243 fmtType |= UiStyle::NetsplitJoinMsg;
244 else if (msgType == "netsplit-quit")
245 fmtType |= UiStyle::NetsplitQuitMsg;
246 else if (msgType == "invite")
247 fmtType |= UiStyle::InviteMsg;
249 qWarning() << Q_FUNC_INFO << tr("Invalid message type in %1").arg(decl);
253 // Next up: conditional (formats, labels, nickhash)
254 QRegExp condRx("\\s*([\\w\\-]+)\\s*=\\s*\"(\\w+)\"\\s*");
255 if (!conditions.isEmpty()) {
256 foreach(const QString &cond, conditions.split(',', QString::SkipEmptyParts)) {
257 if (!condRx.exactMatch(cond)) {
258 qWarning() << Q_FUNC_INFO << tr("Invalid condition %1").arg(cond);
259 return UiStyle::Invalid;
261 QString condName = condRx.cap(1);
262 QString condValue = condRx.cap(2);
263 if (condName == "label") {
264 quint64 labeltype = 0;
265 if (condValue == "highlight")
266 labeltype = UiStyle::Highlight;
267 else if (condValue == "selected")
268 labeltype = UiStyle::Selected;
270 qWarning() << Q_FUNC_INFO << tr("Invalid message label: %1").arg(condValue);
271 return UiStyle::Invalid;
273 fmtType |= (labeltype << 32);
275 else if (condName == "sender") {
276 if (condValue == "self")
277 fmtType |= (quint64) UiStyle::OwnMsg << 32; // sender="self" is actually treated as a label
280 quint64 val = condValue.toUInt(&ok, 16);
282 qWarning() << Q_FUNC_INFO << tr("Invalid senderhash specification: %1").arg(condValue);
283 return UiStyle::Invalid;
286 qWarning() << Q_FUNC_INFO << tr("Senderhash can be at most \"0x0f\"!");
287 return UiStyle::Invalid;
289 fmtType |= ++val << 48;
292 else if (condName == "format") {
293 if (condValue == "bold")
294 fmtType |= UiStyle::Bold;
295 else if (condValue == "italic")
296 fmtType |= UiStyle::Italic;
297 else if (condValue == "underline")
298 fmtType |= UiStyle::Underline;
299 else if (condValue == "reverse")
300 fmtType |= UiStyle::Reverse;
302 qWarning() << Q_FUNC_INFO << tr("Invalid format name: %1").arg(condValue);
303 return UiStyle::Invalid;
306 else if (condName == "fg-color" || condName == "bg-color") {
308 quint8 col = condValue.toUInt(&ok, 16);
309 if (!ok || col > 0x0f) {
310 qWarning() << Q_FUNC_INFO << tr("Illegal IRC color specification (must be between 00 and 0f): %1").arg(condValue);
311 return UiStyle::Invalid;
313 if (condName == "fg-color")
314 fmtType |= 0x00400000 | (quint32)(col << 24);
316 fmtType |= 0x00800000 | (quint32)(col << 28);
319 qWarning() << Q_FUNC_INFO << tr("Unhandled condition: %1").arg(condName);
320 return UiStyle::Invalid;
329 // FIXME: Code duplication
330 quint32 QssParser::parseItemFormatType(const QString &decl)
332 QRegExp rx("(Chat|Nick)ListItem(?:\\[([=-,\\\"\\w\\s]+)\\])?");
333 // $1: item type; $2: properties
334 if (!rx.exactMatch(decl)) {
335 qWarning() << Q_FUNC_INFO << tr("Invalid block declaration: %1").arg(decl);
336 return UiStyle::Invalid;
338 QString mainItemType = rx.cap(1);
339 QString properties = rx.cap(2);
343 // Next up: properties
345 if (!properties.isEmpty()) {
346 QHash<QString, QString> props;
347 QRegExp propRx("\\s*([\\w\\-]+)\\s*=\\s*\"([\\w\\-]+)\"\\s*");
348 foreach(const QString &prop, properties.split(',', QString::SkipEmptyParts)) {
349 if (!propRx.exactMatch(prop)) {
350 qWarning() << Q_FUNC_INFO << tr("Invalid proplist %1").arg(prop);
351 return UiStyle::Invalid;
353 props[propRx.cap(1)] = propRx.cap(2);
355 type = props.value("type");
356 state = props.value("state");
359 if (mainItemType == "Chat") {
360 fmtType |= UiStyle::BufferViewItem;
361 if (!type.isEmpty()) {
362 if (type == "network")
363 fmtType |= UiStyle::NetworkItem;
364 else if (type == "channel")
365 fmtType |= UiStyle::ChannelBufferItem;
366 else if (type == "query")
367 fmtType |= UiStyle::QueryBufferItem;
369 qWarning() << Q_FUNC_INFO << tr("Invalid chatlist item type %1").arg(type);
370 return UiStyle::Invalid;
373 if (!state.isEmpty()) {
374 if (state == "inactive")
375 fmtType |= UiStyle::InactiveBuffer;
376 else if (state == "channel-event")
377 fmtType |= UiStyle::ActiveBuffer;
378 else if (state == "unread-message")
379 fmtType |= UiStyle::UnreadBuffer;
380 else if (state == "highlighted")
381 fmtType |= UiStyle::HighlightedBuffer;
382 else if (state == "away")
383 fmtType |= UiStyle::UserAway;
385 qWarning() << Q_FUNC_INFO << tr("Invalid chatlist state %1").arg(state);
386 return UiStyle::Invalid;
391 fmtType |= UiStyle::NickViewItem;
392 if (!type.isEmpty()) {
393 if (type == "user") {
394 fmtType |= UiStyle::IrcUserItem;
396 fmtType |= UiStyle::UserAway;
398 else if (type == "category")
399 fmtType |= UiStyle::UserCategoryItem;
406 /******** Parse a whole format attribute block ********/
408 QTextCharFormat QssParser::parseFormat(const QString &qss)
410 QTextCharFormat format;
412 foreach(QString line, qss.split(';', QString::SkipEmptyParts)) {
413 int idx = line.indexOf(':');
415 qWarning() << Q_FUNC_INFO << tr("Invalid property declaration: %1").arg(line.trimmed());
418 QString property = line.left(idx).trimmed();
419 QString value = line.mid(idx + 1).simplified();
421 if (property == "background" || property == "background-color")
422 format.setBackground(parseBrush(value));
423 else if (property == "foreground" || property == "color")
424 format.setForeground(parseBrush(value));
426 // font-related properties
427 else if (property.startsWith("font")) {
428 if (property == "font")
429 parseFont(value, &format);
430 else if (property == "font-style")
431 parseFontStyle(value, &format);
432 else if (property == "font-weight")
433 parseFontWeight(value, &format);
434 else if (property == "font-size")
435 parseFontSize(value, &format);
436 else if (property == "font-family")
437 parseFontFamily(value, &format);
439 qWarning() << Q_FUNC_INFO << tr("Invalid font property: %1").arg(line);
445 qWarning() << Q_FUNC_INFO << tr("Unknown ChatLine property: %1").arg(property);
453 /******** Brush ********/
455 QBrush QssParser::parseBrush(const QString &str, bool *ok)
459 QColor c = parseColor(str);
466 if (str.startsWith("palette")) { // Palette color role
467 QRegExp rx("palette\\s*\\(\\s*([a-z-]+)\\s*\\)");
468 if (!rx.exactMatch(str)) {
469 qWarning() << Q_FUNC_INFO << tr("Invalid palette color role specification: %1").arg(str);
472 if (_paletteColorRoles.contains(rx.cap(1)))
473 return QBrush(_palette.brush(_paletteColorRoles.value(rx.cap(1))));
474 if (_uiStyleColorRoles.contains(rx.cap(1)))
475 return QBrush(_uiStylePalette.at(_uiStyleColorRoles.value(rx.cap(1))));
476 qWarning() << Q_FUNC_INFO << tr("Unknown palette color role: %1").arg(rx.cap(1));
479 else if (str.startsWith("qlineargradient")) {
480 static QString rxFloat("\\s*(-?\\s*[0-9]*\\.?[0-9]+)\\s*");
481 QRegExp rx(QString("qlineargradient\\s*\\(\\s*x1:%1,\\s*y1:%1,\\s*x2:%1,\\s*y2:%1,(.+)\\)").arg(rxFloat));
482 if (!rx.exactMatch(str)) {
483 qWarning() << Q_FUNC_INFO << tr("Invalid gradient declaration: %1").arg(str);
486 qreal x1 = rx.cap(1).toDouble();
487 qreal y1 = rx.cap(2).toDouble();
488 qreal x2 = rx.cap(3).toDouble();
489 qreal y2 = rx.cap(4).toDouble();
490 QGradientStops stops = parseGradientStops(rx.cap(5).trimmed());
491 if (!stops.count()) {
492 qWarning() << Q_FUNC_INFO << tr("Invalid gradient stops list: %1").arg(str);
495 QLinearGradient gradient(x1, y1, x2, y2);
496 gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
497 gradient.setStops(stops);
500 return QBrush(gradient);
502 else if (str.startsWith("qconicalgradient")) {
503 static QString rxFloat("\\s*(-?\\s*[0-9]*\\.?[0-9]+)\\s*");
504 QRegExp rx(QString("qconicalgradient\\s*\\(\\s*cx:%1,\\s*cy:%1,\\s*angle:%1,(.+)\\)").arg(rxFloat));
505 if (!rx.exactMatch(str)) {
506 qWarning() << Q_FUNC_INFO << tr("Invalid gradient declaration: %1").arg(str);
509 qreal cx = rx.cap(1).toDouble();
510 qreal cy = rx.cap(2).toDouble();
511 qreal angle = rx.cap(3).toDouble();
512 QGradientStops stops = parseGradientStops(rx.cap(4).trimmed());
513 if (!stops.count()) {
514 qWarning() << Q_FUNC_INFO << tr("Invalid gradient stops list: %1").arg(str);
517 QConicalGradient gradient(cx, cy, angle);
518 gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
519 gradient.setStops(stops);
522 return QBrush(gradient);
524 else if (str.startsWith("qradialgradient")) {
525 static QString rxFloat("\\s*(-?\\s*[0-9]*\\.?[0-9]+)\\s*");
526 QRegExp rx(QString("qradialgradient\\s*\\(\\s*cx:%1,\\s*cy:%1,\\s*radius:%1,\\s*fx:%1,\\s*fy:%1,(.+)\\)").arg(rxFloat));
527 if (!rx.exactMatch(str)) {
528 qWarning() << Q_FUNC_INFO << tr("Invalid gradient declaration: %1").arg(str);
531 qreal cx = rx.cap(1).toDouble();
532 qreal cy = rx.cap(2).toDouble();
533 qreal radius = rx.cap(3).toDouble();
534 qreal fx = rx.cap(4).toDouble();
535 qreal fy = rx.cap(5).toDouble();
536 QGradientStops stops = parseGradientStops(rx.cap(6).trimmed());
537 if (!stops.count()) {
538 qWarning() << Q_FUNC_INFO << tr("Invalid gradient stops list: %1").arg(str);
541 QRadialGradient gradient(cx, cy, radius, fx, fy);
542 gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
543 gradient.setStops(stops);
546 return QBrush(gradient);
553 QColor QssParser::parseColor(const QString &str)
555 if (str.startsWith("rgba")) {
556 ColorTuple tuple = parseColorTuple(str.mid(4));
557 if (tuple.count() == 4)
558 return QColor(tuple.at(0), tuple.at(1), tuple.at(2), tuple.at(3));
560 else if (str.startsWith("rgb")) {
561 ColorTuple tuple = parseColorTuple(str.mid(3));
562 if (tuple.count() == 3)
563 return QColor(tuple.at(0), tuple.at(1), tuple.at(2));
565 else if (str.startsWith("hsva")) {
566 ColorTuple tuple = parseColorTuple(str.mid(4));
567 if (tuple.count() == 4) {
569 c.setHsvF(tuple.at(0), tuple.at(1), tuple.at(2), tuple.at(3));
573 else if (str.startsWith("hsv")) {
574 ColorTuple tuple = parseColorTuple(str.mid(3));
575 if (tuple.count() == 3) {
577 c.setHsvF(tuple.at(0), tuple.at(1), tuple.at(2));
582 QRegExp rx("#?[0-9A-Fa-z]+");
583 if (rx.exactMatch(str))
590 // get a list of comma-separated int values or percentages (rel to 0-255)
591 QssParser::ColorTuple QssParser::parseColorTuple(const QString &str)
594 QRegExp rx("\\(((\\s*[0-9]{1,3}%?\\s*)(,\\s*[0-9]{1,3}%?\\s*)*)\\)");
595 if (!rx.exactMatch(str.trimmed())) {
598 QStringList values = rx.cap(1).split(',');
599 foreach(QString v, values) {
604 if (v.endsWith('%')) {
608 val = (qreal)v.toUInt(&ok);
619 QGradientStops QssParser::parseGradientStops(const QString &str_)
622 QGradientStops result;
623 static QString rxFloat("(0?\\.[0-9]+|[01])"); // values between 0 and 1
624 QRegExp rx(QString("\\s*,?\\s*stop:\\s*(%1)\\s+([^:]+)(,\\s*stop:|$)").arg(rxFloat));
626 while ((idx = rx.indexIn(str)) == 0) {
627 qreal x = rx.cap(1).toDouble();
628 QColor c = parseColor(rx.cap(3));
630 return QGradientStops();
631 result << QGradientStop(x, c);
632 str.remove(0, rx.matchedLength() - rx.cap(4).length());
634 if (!str.trimmed().isEmpty())
635 return QGradientStops();
641 /******** Font Properties ********/
643 void QssParser::parseFont(const QString &value, QTextCharFormat *format)
645 QRegExp rx("((?:(?:normal|italic|oblique|underline|bold|100|200|300|400|500|600|700|800|900) ){0,2}) ?(\\d+)(pt|px)? \"(.*)\"");
646 if (!rx.exactMatch(value)) {
647 qWarning() << Q_FUNC_INFO << tr("Invalid font specification: %1").arg(value);
650 format->setFontItalic(false);
651 format->setFontWeight(QFont::Normal);
652 QStringList proplist = rx.cap(1).split(' ', QString::SkipEmptyParts);
653 foreach(QString prop, proplist) {
654 if (prop == "italic")
655 format->setFontItalic(true);
656 else if (prop == "underline")
657 format->setFontUnderline(true);
658 //else if(prop == "oblique")
659 // format->setStyle(QFont::StyleOblique);
660 else if (prop == "bold")
661 format->setFontWeight(QFont::Bold);
663 int w = prop.toInt();
664 format->setFontWeight(qMin(w / 8, 99)); // taken from Qt's qss parser
668 if (rx.cap(3) == "px")
669 format->setProperty(QTextFormat::FontPixelSize, rx.cap(2).toInt());
671 format->setFontPointSize(rx.cap(2).toInt());
673 format->setFontFamily(rx.cap(4));
677 void QssParser::parseFontStyle(const QString &value, QTextCharFormat *format)
679 if (value == "normal")
680 format->setFontItalic(false);
681 else if (value == "italic")
682 format->setFontItalic(true);
683 else if (value == "underline")
684 format->setFontUnderline(true);
685 //else if(value == "oblique")
686 // format->setStyle(QFont::StyleOblique);
688 qWarning() << Q_FUNC_INFO << tr("Invalid font style specification: %1").arg(value);
693 void QssParser::parseFontWeight(const QString &value, QTextCharFormat *format)
695 if (value == "normal")
696 format->setFontWeight(QFont::Normal);
697 else if (value == "bold")
698 format->setFontWeight(QFont::Bold);
701 int w = value.toInt(&ok);
703 qWarning() << Q_FUNC_INFO << tr("Invalid font weight specification: %1").arg(value);
706 format->setFontWeight(qMin(w / 8, 99)); // taken from Qt's qss parser
711 void QssParser::parseFontSize(const QString &value, QTextCharFormat *format)
713 QRegExp rx("(\\d+)(pt|px)");
714 if (!rx.exactMatch(value)) {
715 qWarning() << Q_FUNC_INFO << tr("Invalid font size specification: %1").arg(value);
718 if (rx.cap(2) == "px")
719 format->setProperty(QTextFormat::FontPixelSize, rx.cap(1).toInt());
721 format->setFontPointSize(rx.cap(1).toInt());
725 void QssParser::parseFontFamily(const QString &value, QTextCharFormat *format)
727 QString family = value;
728 if (family.startsWith('"') && family.endsWith('"')) {
729 family = family.mid(1, family.length() - 2);
731 format->setFontFamily(family);