/**************************************************************************** ** ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtQuick1 module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this ** file. Please review the following information to ensure the GNU Lesser ** General Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU General ** Public License version 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of this ** file. Please review the following information to ensure the GNU General ** Public License version 3.0 requirements will be met: ** http://www.gnu.org/copyleft/gpl.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include "private/qdeclarativestyledtext_p.h" /* QDeclarativeStyledText supports few tags: - bold - italic
- new line The opening and closing tags must be correctly nested. */ QT_BEGIN_NAMESPACE class QDeclarativeStyledTextPrivate { public: QDeclarativeStyledTextPrivate(const QString &t, QTextLayout &l) : text(t), layout(l), baseFont(layout.font()) {} void parse(); bool parseTag(const QChar *&ch, const QString &textIn, QString &textOut, QTextCharFormat &format); bool parseCloseTag(const QChar *&ch, const QString &textIn); void parseEntity(const QChar *&ch, const QString &textIn, QString &textOut); bool parseFontAttributes(const QChar *&ch, const QString &textIn, QTextCharFormat &format); QPair parseAttribute(const QChar *&ch, const QString &textIn); QStringRef parseValue(const QChar *&ch, const QString &textIn); inline void skipSpace(const QChar *&ch) { while (ch->isSpace() && !ch->isNull()) ++ch; } QString text; QTextLayout &layout; QFont baseFont; static const QChar lessThan; static const QChar greaterThan; static const QChar equals; static const QChar singleQuote; static const QChar doubleQuote; static const QChar slash; static const QChar ampersand; }; const QChar QDeclarativeStyledTextPrivate::lessThan(QLatin1Char('<')); const QChar QDeclarativeStyledTextPrivate::greaterThan(QLatin1Char('>')); const QChar QDeclarativeStyledTextPrivate::equals(QLatin1Char('=')); const QChar QDeclarativeStyledTextPrivate::singleQuote(QLatin1Char('\'')); const QChar QDeclarativeStyledTextPrivate::doubleQuote(QLatin1Char('\"')); const QChar QDeclarativeStyledTextPrivate::slash(QLatin1Char('/')); const QChar QDeclarativeStyledTextPrivate::ampersand(QLatin1Char('&')); QDeclarativeStyledText::QDeclarativeStyledText(const QString &string, QTextLayout &layout) : d(new QDeclarativeStyledTextPrivate(string, layout)) { } QDeclarativeStyledText::~QDeclarativeStyledText() { delete d; } void QDeclarativeStyledText::parse(const QString &string, QTextLayout &layout) { if (string.isEmpty()) return; QDeclarativeStyledText styledText(string, layout); styledText.d->parse(); } void QDeclarativeStyledTextPrivate::parse() { QList ranges; QStack formatStack; QString drawText; drawText.reserve(text.count()); int textStart = 0; int textLength = 0; int rangeStart = 0; const QChar *ch = text.constData(); while (!ch->isNull()) { if (*ch == lessThan) { if (textLength) drawText.append(QStringRef(&text, textStart, textLength)); if (rangeStart != drawText.length() && formatStack.count()) { QTextLayout::FormatRange formatRange; formatRange.format = formatStack.top(); formatRange.start = rangeStart; formatRange.length = drawText.length() - rangeStart; ranges.append(formatRange); } rangeStart = drawText.length(); ++ch; if (*ch == slash) { ++ch; if (parseCloseTag(ch, text)) { if (formatStack.count()) formatStack.pop(); } } else { QTextCharFormat format; if (formatStack.count()) format = formatStack.top(); if (parseTag(ch, text, drawText, format)) formatStack.push(format); } textStart = ch - text.constData() + 1; textLength = 0; } else if (*ch == ampersand) { ++ch; drawText.append(QStringRef(&text, textStart, textLength)); parseEntity(ch, text, drawText); textStart = ch - text.constData() + 1; textLength = 0; } else { ++textLength; } if (!ch->isNull()) ++ch; } if (textLength) drawText.append(QStringRef(&text, textStart, textLength)); if (rangeStart != drawText.length() && formatStack.count()) { QTextLayout::FormatRange formatRange; formatRange.format = formatStack.top(); formatRange.start = rangeStart; formatRange.length = drawText.length() - rangeStart; ranges.append(formatRange); } layout.setText(drawText); layout.setAdditionalFormats(ranges); } bool QDeclarativeStyledTextPrivate::parseTag(const QChar *&ch, const QString &textIn, QString &textOut, QTextCharFormat &format) { skipSpace(ch); int tagStart = ch - textIn.constData(); int tagLength = 0; while (!ch->isNull()) { if (*ch == greaterThan) { QStringRef tag(&textIn, tagStart, tagLength); const QChar char0 = tag.at(0); if (char0 == QLatin1Char('b')) { if (tagLength == 1) format.setFontWeight(QFont::Bold); else if (tagLength == 2 && tag.at(1) == QLatin1Char('r')) { textOut.append(QChar(QChar::LineSeparator)); return false; } } else if (char0 == QLatin1Char('i')) { if (tagLength == 1) format.setFontItalic(true); } return true; } else if (ch->isSpace()) { // may have params. QStringRef tag(&textIn, tagStart, tagLength); if (tag == QLatin1String("font")) return parseFontAttributes(ch, textIn, format); if (*ch == greaterThan || ch->isNull()) continue; } else if (*ch != slash){ tagLength++; } ++ch; } return false; } bool QDeclarativeStyledTextPrivate::parseCloseTag(const QChar *&ch, const QString &textIn) { skipSpace(ch); int tagStart = ch - textIn.constData(); int tagLength = 0; while (!ch->isNull()) { if (*ch == greaterThan) { QStringRef tag(&textIn, tagStart, tagLength); const QChar char0 = tag.at(0); if (char0 == QLatin1Char('b')) { if (tagLength == 1) return true; else if (tag.at(1) == QLatin1Char('r') && tagLength == 2) return true; } else if (char0 == QLatin1Char('i')) { if (tagLength == 1) return true; } else if (tag == QLatin1String("font")) { return true; } return false; } else if (!ch->isSpace()){ tagLength++; } ++ch; } return false; } void QDeclarativeStyledTextPrivate::parseEntity(const QChar *&ch, const QString &textIn, QString &textOut) { int entityStart = ch - textIn.constData(); int entityLength = 0; while (!ch->isNull()) { if (*ch == QLatin1Char(';')) { QStringRef entity(&textIn, entityStart, entityLength); if (entity == QLatin1String("gt")) textOut += QChar(62); else if (entity == QLatin1String("lt")) textOut += QChar(60); else if (entity == QLatin1String("amp")) textOut += QChar(38); return; } ++entityLength; ++ch; } } bool QDeclarativeStyledTextPrivate::parseFontAttributes(const QChar *&ch, const QString &textIn, QTextCharFormat &format) { bool valid = false; QPair attr; do { attr = parseAttribute(ch, textIn); if (attr.first == QLatin1String("color")) { valid = true; format.setForeground(QColor(attr.second.toString())); } else if (attr.first == QLatin1String("size")) { valid = true; int size = attr.second.toString().toInt(); if (attr.second.at(0) == QLatin1Char('-') || attr.second.at(0) == QLatin1Char('+')) size += 3; if (size >= 1 && size <= 7) { static const qreal scaling[] = { 0.7, 0.8, 1.0, 1.2, 1.5, 2.0, 2.4 }; format.setFontPointSize(baseFont.pointSize() * scaling[size-1]); } } } while (!ch->isNull() && !attr.first.isEmpty()); return valid; } QPair QDeclarativeStyledTextPrivate::parseAttribute(const QChar *&ch, const QString &textIn) { skipSpace(ch); int attrStart = ch - textIn.constData(); int attrLength = 0; while (!ch->isNull()) { if (*ch == greaterThan) { break; } else if (*ch == equals) { ++ch; if (*ch != singleQuote && *ch != doubleQuote) { while (*ch != greaterThan && !ch->isNull()) ++ch; break; } ++ch; if (!attrLength) break; QStringRef attr(&textIn, attrStart, attrLength); QStringRef val = parseValue(ch, textIn); if (!val.isEmpty()) return QPair(attr,val); break; } else { ++attrLength; } ++ch; } return QPair(); } QStringRef QDeclarativeStyledTextPrivate::parseValue(const QChar *&ch, const QString &textIn) { int valStart = ch - textIn.constData(); int valLength = 0; while (*ch != singleQuote && *ch != doubleQuote && !ch->isNull()) { ++valLength; ++ch; } if (ch->isNull()) return QStringRef(); ++ch; // skip quote return QStringRef(&textIn, valStart, valLength); } QT_END_NAMESPACE