28#include <QXmlStreamReader>
39#define QL1S(x) QLatin1String(x)
47 if (str.length() <=
maxlen)
54class KuitEntityResolver :
public QXmlStreamEntityResolver
65 QString value = entityMap.value(name);
134class KuitSemanticsStaticData
158 KuitEntityResolver xmlEntityResolver;
160 KuitSemanticsStaticData ();
163KuitSemanticsStaticData::KuitSemanticsStaticData ()
168 #define SETUP_TAG(tag, name, atts, subs) do { \
169 knownTags.insert(QString::fromLatin1(name), Kuit::Tag::tag); \
170 tagNames.insert(Kuit::Tag::tag, QString::fromLatin1(name)); \
172 using namespace Kuit::Att; \
173 tagAtts[Kuit::Tag::tag] << atts; \
176 using namespace Kuit::Tag; \
177 tagSubs[Kuit::Tag::tag] << subs << NumIntg << NumReal; \
183 Filename << Link << Application << Command << Resource << Icode << \
184 Shortcut << Interface << Emphasis << Placeholder << Email << \
187 SETUP_TAG(TopLong,
"kuit", Ctx, Title << Subtitle << Para);
193 INLINES << Note << Warning << Message << List);
199 SETUP_TAG(Filename,
"filename", None, Envar << Placeholder);
201 SETUP_TAG(Application,
"application", None, None);
202 SETUP_TAG(Command,
"command", Section, None);
203 SETUP_TAG(Resource,
"resource", None, None);
204 SETUP_TAG(Icode,
"icode", None, Envar << Placeholder);
206 SETUP_TAG(Shortcut,
"shortcut", None, None);
207 SETUP_TAG(Interface,
"interface", None, None);
208 SETUP_TAG(Emphasis,
"emphasis", Strong, None);
209 SETUP_TAG(Placeholder,
"placeholder", None, None);
210 SETUP_TAG(Email,
"email", Address, None);
212 SETUP_TAG(Message,
"message", None, None);
221 #define SETUP_ATT(att, name) do { \
222 knownAtts.insert(QString::fromLatin1(name), Kuit::Att::att); \
236 #define SETUP_FMT(fmt, name) do { \
237 knownFmts.insert(QString::fromLatin1(name), Kuit::Fmt::fmt); \
245 #define SETUP_ROL(rol, name, fmt, cues) do { \
246 knownRols.insert(QString::fromLatin1(name), Kuit::Rol::rol); \
247 defFmts[Kuit::Rol::rol][Kuit::Cue::None] = Kuit::Fmt::fmt; \
249 using namespace Kuit::Cue; \
250 rolCues[Kuit::Rol::rol] << cues; \
254 Button << Inmenu << Intoolbar);
256 Window << Menu << Tab << Group << Column << Row);
258 Slider << Spinbox << Listbox << Textbox << Chooser);
262 Inmenu << Inlistbox << Intable << Inrange << Intext);
265 << Tipoftheday << Credit << Shell);
268 #undef SETUP_ROLCUEFMT
269 #define SETUP_ROLCUEFMT(rol, cue, fmt) do { \
270 defFmts[Kuit::Rol::rol][Kuit::Cue::cue] = Kuit::Fmt::fmt; \
279 #define SETUP_CUE(cue, name) do { \
280 knownCues.insert(QString::fromLatin1(name), Kuit::Cue::cue); \
311 qtHtmlTagNames <<
QL1S(
"a") <<
QL1S(
"address") <<
QL1S(
"b") <<
QL1S(
"big") <<
QL1S(
"blockquote")
324 #define SETUP_TAG_NL(tag, nlead) do { \
325 leadingNewlines.insert(Kuit::Tag::tag, nlead); \
346 xmlEntities[QString::fromLatin1(
"nbsp")] =
QString(QChar(0xa0));
347 xmlEntityResolver.setEntities(xmlEntities);
356class KuitSemanticsPrivate
365 QString metaTr (
const char *ctxt,
const char *
id)
const;
368 void setFormattingPatterns ();
371 void setTextTransformData ();
404 typedef enum { Proper, Ignored, Dropout } Handling;
428 static void countWrappingNewlines (
const QString &
ptext,
453KuitSemanticsPrivate::KuitSemanticsPrivate (
const QString &
lang)
464 m_metaCat =
new KCatalog(QString::fromLatin1(
"kdelibs4"),
lang);
467 setFormattingPatterns();
470 setTextTransformData();
477QString KuitSemanticsPrivate::metaTr (
const char *ctxt,
const char *
id)
const
479 if (m_metaCat ==
NULL) {
480 return QString::fromLatin1(
id);
485void KuitSemanticsPrivate::setFormattingPatterns ()
487 using namespace Kuit;
491 #define SET_PATTERN(tag, atts, fmt, ctxt_ptrn) do { \
494 int akey = attSetKey(aset); \
495 QString pattern = metaTr(ctxt_ptrn); \
496 m_patterns[tag][akey][fmt] = pattern; \
498 if (fmt == Fmt::Plain && !m_patterns[tag][akey].contains(Fmt::Term)) { \
499 m_patterns[tag][akey][Fmt::Term] = pattern; \
505 #define I18N_NOOP2(ctxt, msg) ctxt, msg
510 #define XXXX_NOOP2(ctxt, msg) ctxt, msg
585 "%1 is the note label, %2 is the text",
590 "%1 is the note label, %2 is the text",
602 "<b>Warning</b>: %1"));
605 "%1 is the warning label, %2 is the text",
610 "%1 is the warning label, %2 is the text",
622 "<a href=\"%1\">%1</a>"));
625 "%1 is the URL, %2 is the descriptive text",
630 "%1 is the URL, %2 is the descriptive text",
632 "<a href=\"%1\">%2</a>"));
645 SET_PATTERN(Tag::Application, Att::None, Fmt::Plain,
649 SET_PATTERN(Tag::Application, Att::None, Fmt::Rich,
663 SET_PATTERN(Tag::Command, Att::Section, Fmt::Plain,
665 "%1 is the command name, %2 is its man section",
670 "%1 is the command name, %2 is its man section",
733 SET_PATTERN(Tag::Emphasis, Att::Strong, Fmt::Plain,
743 SET_PATTERN(Tag::Placeholder, Att::None, Fmt::Plain,
747 SET_PATTERN(Tag::Placeholder, Att::None, Fmt::Rich,
750 "<<i>%1</i>>"));
760 "<<a href=\"mailto:%1\">%1</a>>"));
763 "%1 is name, %2 is address",
768 "%1 is name, %2 is address",
770 "<a href=\"mailto:%2\">%1</a>"));
803void KuitSemanticsPrivate::setTextTransformData ()
807 #define I18N_NOOP2(ctxt, msg) metaTr(ctxt, msg)
828 #define SET_KEYNAME(rawname) do { \
830 QString normname = QString::fromLatin1(rawname).trimmed().toLower(); \
831 m_keyNames[normname] = metaTr("keyboard-key-name", rawname); \
836 #define I18N_NOOP2(ctxt, msg) msg
907 if (
ftext.isEmpty()) {
927Kuit::FmtVar KuitSemanticsPrivate::formatFromContextMarker (
972 if (
s->knownRols.contains(
rolname)) {
978 kDebug(173) << QString::fromLatin1(
"Unknown semantic role '@%1' in "
979 "context marker for message {%2}.")
986 if (
s->knownCues.contains(
cuename)) {
992 kDebug(173) << QString::fromLatin1(
"Unknown interface subcue ':%1' in "
993 "context marker for message {%2}.")
1000 if (
s->knownFmts.contains(
fmtname)) {
1007 if (
s->defFmts.contains(
rol)) {
1008 if (
s->defFmts[
rol].contains(
cue)) {
1020 kDebug(173) << QString::fromLatin1(
"Unknown visual format '/%1' in "
1021 "context marker for message {%2}.")
1035 int p =
tagRx.indexIn(text);
1038 if (
s->qtHtmlTagNames.contains(
tagname)) {
1041 p =
tagRx.indexIn(text, p +
tagRx.matchedLength());
1075 if (
s->knownTags.contains(
tagname)) {
1110#define ENTITY_SUBRX "[a-z]+|#[0-9]+|#x[0-9a-fA-F]+"
1124 text.append(
original.mid(0, p + 1));
1141 xml.setEntityResolver(&
s->xmlEntityResolver);
1144 while (!
xml.atEnd()) {
1147 if (
xml.isStartElement()) {
1152 for (
int i =
openEls.size() - 1; i >= 0; --i) {
1153 if (
openEls[i].handling == OpenEl::Proper) {
1164 if (
s->qtHtmlTagNames.contains(
oel.name)) {
1184 else if (
xml.isEndElement()) {
1191 return finalizeVisualText(
oel.formattedText,
fmtExp,
1204 else if (
xml.isCharacters()) {
1210 foreach (
const QChar &
c, text) {
1211 if (
s->xmlEntitiesInverse.contains(
c)) {
1222 if (
xml.hasError()) {
1223 kDebug(173) << QString::fromLatin1(
"Markup error in message {%1}: %2. Last tag parsed: %3")
1232KuitSemanticsPrivate::OpenEl
1244 oel.name =
xml.name().toString().toLower();
1255 if (
s->knownTags.contains(
oel.name)) {
1256 oel.tag =
s->knownTags[
oel.name];
1261 oel.handling = OpenEl::Proper;
1264 oel.handling = OpenEl::Dropout;
1265 kDebug(173) << QString::fromLatin1(
"Tag '%1' cannot be subtag of '%2' "
1267 .arg(
s->tagNames[
oel.tag],
s->tagNames[
etag],
1273 for (
int i = 0; i <
attnams.size(); ++i) {
1274 if (
s->knownAtts.contains(
attnams[i])) {
1276 if (
s->tagAtts[
oel.tag].contains(
att)) {
1281 kDebug(173) << QString::fromLatin1(
"Attribute '%1' cannot be used in "
1282 "tag '%2' in message {%3}.")
1288 kDebug(173) << QString::fromLatin1(
"Unknown semantic tag attribute '%1' "
1297 oel.handling = OpenEl::Dropout;
1300 oel.handling = OpenEl::Ignored;
1301 if (!
s->qtHtmlTagNames.contains(
oel.name)) {
1302 kDebug(173) << QString::fromLatin1(
"Tag '%1' is neither semantic nor HTML in "
1315 QString pattern = QString::fromLatin1(
"%1");
1318 if ( m_patterns.contains(tag)
1319 && m_patterns[tag].contains(akey)
1320 && m_patterns[tag][akey].contains(
fmt))
1322 pattern = m_patterns[tag][akey][
fmt];
1335 if (
oel.handling == OpenEl::Proper) {
1343 using namespace Kuit;
1347 if (
oel.tag == Tag::Link &&
oel.avals.contains(Att::Url)) {
1350 else if (
oel.tag == Tag::Command &&
oel.avals.contains(Att::Section)) {
1353 else if (
oel.tag == Tag::Email &&
oel.avals.contains(Att::Address)) {
1356 else if (
oel.tag == Tag::Note &&
oel.avals.contains(Att::Label)) {
1359 else if (
oel.tag == Tag::Warning &&
oel.avals.contains(Att::Label)) {
1368 if (!
ptext.isEmpty() &&
s->leadingNewlines.contains(
oel.tag)) {
1385 else if (
oel.handling == OpenEl::Ignored) {
1397 return oel.formattedText;
1401void KuitSemanticsPrivate::countWrappingNewlines (
const QString &text,
1404 int len = text.length();
1430 return QString::fromLatin1(
"%1").arg(
KGlobal::locale()->formatNumber(text,
false),
1434 return QDir::toNativeSeparators(text);
1447QString KuitSemanticsPrivate::finalizeVisualText (
const QString &
final,
1464 int p =
entRx.indexIn(text);
1468 plain.append(text.mid(0, p));
1469 text.remove(0, p +
ent.length() + 2);
1474 c = QChar(
ent.mid(2).toInt(&ok, 16));
1476 c = QChar(
ent.mid(1).toInt(&ok, 10));
1484 else if (
s->xmlEntities.contains(
ent)) {
1489 p =
entRx.indexIn(text);
1497 text = QString::fromLatin1(
"<html>") + text +
QLatin1String(
"</html>");
1530 if (
s->knownTags.contains(
tagname)) {
1538 pos +=
wrapRx.matchedLength();
1558 if (
s->knownTags.contains(
tagname)) {
1564 pos +=
nowrRx.matchedLength();
1575: d(
new KuitSemanticsPrivate(
lang))
1586 return d->format(text, ctxt);
1598 return (
p2 >
p1 &&
s->xmlEntities.contains(text.mid(
p1,
p2 -
p1)));
1602 int tlen = text.length();
1622 return s->qtHtmlTagNames.contains(text.mid(
p1,
p2 -
p1));
1623 }
else if (!
c.isLetter()) {
1635 int tlen = text.length();
1638 for (
int i = 0; i <
tlen; ++i) {
This class abstracts a gettext message catalog.
QString translate(const char *msgid) const
Retrieves a translation of the specified message id.
~KuitSemantics()
Destructor.
static bool mightBeRichText(const QString &text)
Poor man's version of Qt::mightBeRichText() (cannot link to QtGui).
KuitSemantics(const QString &lang)
Constructor.
static QString escape(const QString &text)
Convert &, ", ', <, > characters into XML entities &, <, >, ', ", respectively.
QString format(const QString &text, const QString &ctxt) const
Transforms the semantic markup in the given text into visual formatting.
#define K_GLOBAL_STATIC(TYPE, NAME)
This macro makes it easy to use non-POD types as global statics.
static QString shorten(const QString &str)
#define SETUP_ATT(att, name)
#define SET_PATTERN(tag, atts, fmt, ctxt_ptrn)
#define I18N_NOOP2(ctxt, msg)
#define SETUP_TAG(tag, name, atts, subs)
#define SETUP_CUE(cue, name)
#define SETUP_ROLCUEFMT(rol, cue, fmt)
#define SETUP_TAG_NL(tag, nlead)
#define SET_KEYNAME(rawname)
#define SETUP_ROL(rol, name, fmt, cues)
#define SETUP_FMT(fmt, name)
#define XXXX_NOOP2(ctxt, msg)
KLocale * locale()
Returns the global locale object.