KDECore
kuitsemantics.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2007 Chusslove Illich <caslav.ilic@gmx.net> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License as published by the Free Software Foundation; either 00007 version 2 of the License, or (at your option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include <kuitsemantics_p.h> 00021 00022 #include <config.h> 00023 00024 #include <QHash> 00025 #include <QSet> 00026 #include <QRegExp> 00027 #include <QStack> 00028 #include <QXmlStreamReader> 00029 #include <QStringList> 00030 #include <QPair> 00031 #include <QDir> 00032 00033 #include <kdebug.h> 00034 #include <kglobal.h> 00035 #include <kcatalog_p.h> 00036 #include <kuitformats_p.h> 00037 #include <klocale.h> 00038 00039 #define QL1S(x) QLatin1String(x) 00040 00041 // Truncates string, for output of long messages. 00042 // (But don't truncate too much otherwise it's impossible to determine 00043 // which message is faulty if many messages start with the same beginning). 00044 static QString shorten (const QString &str) 00045 { 00046 const int maxlen = 80; 00047 if (str.length() <= maxlen) 00048 return str; 00049 else 00050 return str.left(maxlen).append(QLatin1String("...")); 00051 } 00052 00053 // ----------------------------------------------------------------------------- 00054 // All the tag, attribute, and context marker element enums. 00055 namespace Kuit { 00056 00057 namespace Tag { // tag names 00058 typedef enum { 00059 None, 00060 TopLong, TopShort, 00061 Title, Subtitle, Para, List, Item, Note, Warning, Link, 00062 Filename, Application, Command, Resource, Icode, Bcode, Shortcut, 00063 Interface, Emphasis, Placeholder, Email, Numid, Envar, Message, Nl, 00064 NumIntg, NumReal // internal helpers for numbers, not part of DTD 00065 } Var; 00066 } 00067 00068 namespace Att { // tag attribute names 00069 typedef enum { 00070 None, 00071 Ctx, Url, Address, Section, Label, Strong, 00072 Width, Fill // internal helpers for numbers, not part of DTD 00073 } Var; 00074 } 00075 00076 namespace Rol { // semantic roles 00077 typedef enum { 00078 None, 00079 Action, Title, Option, Label, Item, Info 00080 } Var; 00081 } 00082 00083 namespace Cue { // interface subcues 00084 typedef enum { 00085 None, 00086 Button, Inmenu, Intoolbar, 00087 Window, Menu, Tab, Group, Column, Row, 00088 Slider, Spinbox, Listbox, Textbox, Chooser, 00089 Check, Radio, 00090 Inlistbox, Intable, Inrange, Intext, 00091 Tooltip, Whatsthis, Status, Progress, Tipoftheday, Credit, Shell 00092 } Var; 00093 } 00094 00095 namespace Fmt { // visual formats 00096 typedef enum { 00097 None, Plain, Rich, Term 00098 } Var; 00099 } 00100 00101 typedef Tag::Var TagVar; 00102 typedef Att::Var AttVar; 00103 typedef Rol::Var RolVar; 00104 typedef Cue::Var CueVar; 00105 typedef Fmt::Var FmtVar; 00106 } 00107 00108 // ----------------------------------------------------------------------------- 00109 // All the global data. 00110 00111 class KuitSemanticsStaticData 00112 { 00113 public: 00114 00115 QHash<QString, Kuit::TagVar> knownTags; 00116 QHash<QString, Kuit::AttVar> knownAtts; 00117 QHash<QString, Kuit::FmtVar> knownFmts; 00118 QHash<QString, Kuit::RolVar> knownRols; 00119 QHash<QString, Kuit::CueVar> knownCues; 00120 00121 QHash<Kuit::TagVar, QSet<Kuit::TagVar> > tagSubs; 00122 QHash<Kuit::TagVar, QSet<Kuit::AttVar> > tagAtts; 00123 QHash<Kuit::RolVar, QSet<Kuit::CueVar> > rolCues; 00124 00125 QHash<Kuit::RolVar, QHash<Kuit::CueVar, Kuit::FmtVar> > defFmts; 00126 00127 QHash<Kuit::TagVar, QString> tagNames; 00128 00129 QSet<QString> qtHtmlTagNames; 00130 00131 QHash<Kuit::TagVar, int> leadingNewlines; 00132 00133 QHash<QString, QString> xmlEntities; 00134 QHash<QString, QString> xmlEntitiesInverse; 00135 00136 KuitSemanticsStaticData (); 00137 }; 00138 00139 KuitSemanticsStaticData::KuitSemanticsStaticData () 00140 { 00141 // Setup known tag names, attributes, and subtags. 00142 // A "lax" version of the DTD. 00143 #undef SETUP_TAG 00144 #define SETUP_TAG(tag, name, atts, subs) do { \ 00145 knownTags.insert(QString::fromLatin1(name), Kuit::Tag::tag); \ 00146 tagNames.insert(Kuit::Tag::tag, QString::fromLatin1(name)); \ 00147 { \ 00148 using namespace Kuit::Att; \ 00149 tagAtts[Kuit::Tag::tag] << atts; \ 00150 } \ 00151 { \ 00152 using namespace Kuit::Tag; \ 00153 tagSubs[Kuit::Tag::tag] << subs << NumIntg << NumReal; \ 00154 } \ 00155 } while (0) 00156 00157 #undef INLINES 00158 #define INLINES \ 00159 Filename << Link << Application << Command << Resource << Icode << \ 00160 Shortcut << Interface << Emphasis << Placeholder << Email << \ 00161 Numid << Envar << Nl 00162 00163 SETUP_TAG(TopLong, "kuit", Ctx, Title << Subtitle << Para); 00164 SETUP_TAG(TopShort, "kuil", Ctx, INLINES << Note << Warning << Message); 00165 00166 SETUP_TAG(Title, "title", None, INLINES); 00167 SETUP_TAG(Subtitle, "subtitle", None, INLINES); 00168 SETUP_TAG(Para, "para", None, 00169 INLINES << Note << Warning << Message << List); 00170 SETUP_TAG(List, "list", None, Item); 00171 SETUP_TAG(Item, "item", None, INLINES << Note << Warning << Message); 00172 00173 SETUP_TAG(Note, "note", Label, INLINES); 00174 SETUP_TAG(Warning, "warning", Label, INLINES); 00175 SETUP_TAG(Filename, "filename", None, Envar << Placeholder); 00176 SETUP_TAG(Link, "link", Url, None); 00177 SETUP_TAG(Application, "application", None, None); 00178 SETUP_TAG(Command, "command", Section, None); 00179 SETUP_TAG(Resource, "resource", None, None); 00180 SETUP_TAG(Icode, "icode", None, Envar << Placeholder); 00181 SETUP_TAG(Bcode, "bcode", None, None); 00182 SETUP_TAG(Shortcut, "shortcut", None, None); 00183 SETUP_TAG(Interface, "interface", None, None); 00184 SETUP_TAG(Emphasis, "emphasis", Strong, None); 00185 SETUP_TAG(Placeholder, "placeholder", None, None); 00186 SETUP_TAG(Email, "email", Address, None); 00187 SETUP_TAG(Envar, "envar", None, None); 00188 SETUP_TAG(Message, "message", None, None); 00189 SETUP_TAG(Numid, "numid", None, None); 00190 SETUP_TAG(Nl, "nl", None, None); 00191 // Internal, not part of DTD. 00192 SETUP_TAG(NumIntg, KUIT_NUMINTG, Width << Fill, None); 00193 SETUP_TAG(NumReal, KUIT_NUMREAL, Width << Fill, None); 00194 00195 // Setup known attribute names. 00196 #undef SETUP_ATT 00197 #define SETUP_ATT(att, name) do { \ 00198 knownAtts.insert(QString::fromLatin1(name), Kuit::Att::att); \ 00199 } while (0) 00200 SETUP_ATT(Ctx, "ctx"); 00201 SETUP_ATT(Url, "url"); 00202 SETUP_ATT(Address, "address"); 00203 SETUP_ATT(Section, "section"); 00204 SETUP_ATT(Label, "label"); 00205 SETUP_ATT(Strong, "strong"); 00206 // Internal, not part of DTD. 00207 SETUP_ATT(Width, "width"); 00208 SETUP_ATT(Fill, "fill"); 00209 00210 // Setup known format names. 00211 #undef SETUP_FMT 00212 #define SETUP_FMT(fmt, name) do { \ 00213 knownFmts.insert(QString::fromLatin1(name), Kuit::Fmt::fmt); \ 00214 } while (0) 00215 SETUP_FMT(Plain, "plain"); 00216 SETUP_FMT(Rich, "rich"); 00217 SETUP_FMT(Term, "term"); 00218 00219 // Setup known role names, their default format and subcues. 00220 #undef SETUP_ROL 00221 #define SETUP_ROL(rol, name, fmt, cues) do { \ 00222 knownRols.insert(QString::fromLatin1(name), Kuit::Rol::rol); \ 00223 defFmts[Kuit::Rol::rol][Kuit::Cue::None] = Kuit::Fmt::fmt; \ 00224 { \ 00225 using namespace Kuit::Cue; \ 00226 rolCues[Kuit::Rol::rol] << cues; \ 00227 } \ 00228 } while (0) 00229 SETUP_ROL(Action, "action", Plain, 00230 Button << Inmenu << Intoolbar); 00231 SETUP_ROL(Title, "title", Plain, 00232 Window << Menu << Tab << Group << Column << Row); 00233 SETUP_ROL(Label, "label", Plain, 00234 Slider << Spinbox << Listbox << Textbox << Chooser); 00235 SETUP_ROL(Option, "option", Plain, 00236 Check << Radio); 00237 SETUP_ROL(Item, "item", Plain, 00238 Inmenu << Inlistbox << Intable << Inrange << Intext); 00239 SETUP_ROL(Info, "info", Rich, 00240 Tooltip << Whatsthis << Kuit::Cue::Status << Progress 00241 << Tipoftheday << Credit << Shell); 00242 00243 // Setup override formats by subcue. 00244 #undef SETUP_ROLCUEFMT 00245 #define SETUP_ROLCUEFMT(rol, cue, fmt) do { \ 00246 defFmts[Kuit::Rol::rol][Kuit::Cue::cue] = Kuit::Fmt::fmt; \ 00247 } while (0) 00248 SETUP_ROLCUEFMT(Info, Status, Plain); 00249 SETUP_ROLCUEFMT(Info, Progress, Plain); 00250 SETUP_ROLCUEFMT(Info, Credit, Plain); 00251 SETUP_ROLCUEFMT(Info, Shell, Term); 00252 00253 // Setup known subcue names. 00254 #undef SETUP_CUE 00255 #define SETUP_CUE(cue, name) do { \ 00256 knownCues.insert(QString::fromLatin1(name), Kuit::Cue::cue); \ 00257 } while (0) 00258 SETUP_CUE(Button, "button"); 00259 SETUP_CUE(Inmenu, "inmenu"); 00260 SETUP_CUE(Intoolbar, "intoolbar"); 00261 SETUP_CUE(Window, "window"); 00262 SETUP_CUE(Menu, "menu"); 00263 SETUP_CUE(Tab, "tab"); 00264 SETUP_CUE(Group, "group"); 00265 SETUP_CUE(Column, "column"); 00266 SETUP_CUE(Row, "row"); 00267 SETUP_CUE(Slider, "slider"); 00268 SETUP_CUE(Spinbox, "spinbox"); 00269 SETUP_CUE(Listbox, "listbox"); 00270 SETUP_CUE(Textbox, "textbox"); 00271 SETUP_CUE(Chooser, "chooser"); 00272 SETUP_CUE(Check, "check"); 00273 SETUP_CUE(Radio, "radio"); 00274 SETUP_CUE(Inlistbox, "inlistbox"); 00275 SETUP_CUE(Intable, "intable"); 00276 SETUP_CUE(Inrange, "inrange"); 00277 SETUP_CUE(Intext, "intext"); 00278 SETUP_CUE(Tooltip, "tooltip"); 00279 SETUP_CUE(Whatsthis, "whatsthis"); 00280 SETUP_CUE(Status, "status"); 00281 SETUP_CUE(Progress, "progress"); 00282 SETUP_CUE(Tipoftheday, "tipoftheday"); 00283 SETUP_CUE(Credit, "credit"); 00284 SETUP_CUE(Shell, "shell"); 00285 00286 // Collect all Qt's rich text engine HTML tags, for some checks later. 00287 qtHtmlTagNames << QL1S("a") << QL1S("address") << QL1S("b") << QL1S("big") << QL1S("blockquote") 00288 << QL1S("body") << QL1S("br") << QL1S("center") << QL1S("cita") << QL1S("code") 00289 << QL1S("dd") << QL1S("dfn") << QL1S("div") << QL1S("dl") << QL1S("dt") << QL1S("em") 00290 << QL1S("font") << QL1S("h1") << QL1S("h2") << QL1S("h3") << QL1S("h4") << QL1S("h5") 00291 << QL1S("h6") << QL1S("head") << QL1S("hr") << QL1S("html") << QL1S("i") << QL1S("img") 00292 << QL1S("kbd") << QL1S("meta") << QL1S("li") << QL1S("nobr") << QL1S("ol") << QL1S("p") 00293 << QL1S("pre") << QL1S("qt") << QL1S("s") << QL1S("samp") << QL1S("small") << QL1S("span") 00294 << QL1S("strong") << QL1S("sup") << QL1S("sub") << QL1S("table") << QL1S("tbody") 00295 << QL1S("td") << QL1S("tfoot") << QL1S("th") << QL1S("thead") << QL1S("title") 00296 << QL1S("tr") << QL1S("tt") << QL1S("u") << QL1S("ul") << QL1S("var"); 00297 00298 // Tags that format with number of leading newlines. 00299 #undef SETUP_TAG_NL 00300 #define SETUP_TAG_NL(tag, nlead) do { \ 00301 leadingNewlines.insert(Kuit::Tag::tag, nlead); \ 00302 } while (0) 00303 SETUP_TAG_NL(Title, 2); 00304 SETUP_TAG_NL(Subtitle, 2); 00305 SETUP_TAG_NL(Para, 2); 00306 SETUP_TAG_NL(List, 1); 00307 SETUP_TAG_NL(Bcode, 1); 00308 SETUP_TAG_NL(Item, 1); 00309 00310 // Known XML entities, direct/inverse mapping. 00311 xmlEntities[QString::fromLatin1("lt")] = QString(QLatin1Char('<')); 00312 xmlEntities[QString::fromLatin1("gt")] = QString(QLatin1Char('>')); 00313 xmlEntities[QString::fromLatin1("amp")] = QString(QLatin1Char('&')); 00314 xmlEntities[QString::fromLatin1("apos")] = QString(QLatin1Char('\'')); 00315 xmlEntities[QString::fromLatin1("quot")] = QString(QLatin1Char('"')); 00316 xmlEntitiesInverse[QString(QLatin1Char('<'))] = QString::fromLatin1("lt"); 00317 xmlEntitiesInverse[QString(QLatin1Char('>'))] = QString::fromLatin1("gt"); 00318 xmlEntitiesInverse[QString(QLatin1Char('&'))] = QString::fromLatin1("amp"); 00319 xmlEntitiesInverse[QString(QLatin1Char('\''))] = QString::fromLatin1("apos"); 00320 xmlEntitiesInverse[QString(QLatin1Char('"'))] = QString::fromLatin1("quot"); 00321 } 00322 00323 K_GLOBAL_STATIC(KuitSemanticsStaticData, semanticsStaticData) 00324 00325 00326 // ----------------------------------------------------------------------------- 00327 // The KuitSemanticsPrivate methods, they do the work. 00328 00329 class KuitSemanticsPrivate 00330 { 00331 public: 00332 00333 KuitSemanticsPrivate (const QString &lang_); 00334 00335 QString format (const QString &text, const QString &ctxt) const; 00336 00337 // Get metatranslation (formatting patterns, etc.) 00338 QString metaTr (const char *ctxt, const char *id) const; 00339 00340 // Set visual formatting patterns for text in semantic tags. 00341 void setFormattingPatterns (); 00342 00343 // Set data used in transformation of text within semantic tags. 00344 void setTextTransformData (); 00345 00346 // Compute integer hash key from the set of attributes. 00347 static int attSetKey (const QSet<Kuit::AttVar> &aset = QSet<Kuit::AttVar>()); 00348 00349 // Determine visual format by parsing the context marker. 00350 static Kuit::FmtVar formatFromContextMarker (const QString &ctxmark, 00351 const QString &text); 00352 // Determine visual format by parsing tags. 00353 static Kuit::FmtVar formatFromTags (const QString &text); 00354 00355 // Apply appropriate top tag is to the text. 00356 static QString equipTopTag (const QString &text, Kuit::TagVar &toptag); 00357 00358 // Formats the semantic into visual text. 00359 QString semanticToVisualText (const QString &text, 00360 Kuit::FmtVar fmtExp, 00361 Kuit::FmtVar fmtImp) const; 00362 00363 // Final touches to the formatted text. 00364 QString finalizeVisualText (const QString &final, 00365 Kuit::FmtVar fmt, 00366 bool hadQtTag = false, 00367 bool hadAnyHtmlTag = false) const; 00368 00369 // In case of markup errors, try to make result not look too bad. 00370 QString salvageMarkup (const QString &text, Kuit::FmtVar fmt) const; 00371 00372 // Data for XML parsing state. 00373 class OpenEl 00374 { 00375 public: 00376 00377 typedef enum { Proper, Ignored, Dropout } Handling; 00378 00379 Kuit::TagVar tag; 00380 QString name; 00381 QHash<Kuit::AttVar, QString> avals; 00382 int akey; 00383 QString astr; 00384 Handling handling; 00385 QString formattedText; 00386 }; 00387 00388 // Gather data about current element for the parse state. 00389 KuitSemanticsPrivate::OpenEl parseOpenEl (const QXmlStreamReader &xml, 00390 Kuit::TagVar etag, 00391 const QString &text) const; 00392 00393 // Select visual pattern for given tag+attributes+format combination. 00394 QString visualPattern (Kuit::TagVar tag, int akey, Kuit::FmtVar fmt) const; 00395 00396 // Format text of the element. 00397 QString formatSubText (const QString &ptext, const OpenEl &oel, 00398 Kuit::FmtVar fmt, int numctx) const; 00399 00400 // Count number of newlines at start and at end of text. 00401 static void countWrappingNewlines (const QString &ptext, 00402 int &numle, int &numtr); 00403 00404 // Modifies text for some tags. 00405 QString modifyTagText (const QString &text, Kuit::TagVar tag, 00406 const QHash<Kuit::AttVar, QString> &avals, 00407 int numctx, Kuit::FmtVar fmt) const; 00408 00409 private: 00410 00411 QString m_lang; 00412 00413 QHash<Kuit::TagVar, 00414 QHash<int, // attribute set key 00415 QHash<Kuit::FmtVar, QString> > > m_patterns; 00416 00417 QHash<Kuit::FmtVar, QString> m_comboKeyDelim; 00418 QHash<Kuit::FmtVar, QString> m_guiPathDelim; 00419 00420 QHash<QString, QString> m_keyNames; 00421 00422 // For fetching metatranslations. 00423 KCatalog *m_metaCat; 00424 }; 00425 00426 KuitSemanticsPrivate::KuitSemanticsPrivate (const QString &lang) 00427 : m_metaCat(NULL) 00428 { 00429 m_lang = lang; 00430 00431 // NOTE: This function draws translation from raw message catalogs 00432 // because full i18n system is not available at this point (this 00433 // function is called within the initialization of the i18n system), 00434 // Also, pattern/transformation strings are "metastrings", not 00435 // fully proper i18n strings on their own. 00436 00437 m_metaCat = new KCatalog(QString::fromLatin1("kdelibs4"), lang); 00438 00439 // Get formatting patterns for all tag/att/fmt combinations. 00440 setFormattingPatterns(); 00441 00442 // Get data for tag text transformations. 00443 setTextTransformData(); 00444 00445 // Catalog not needed any more. 00446 delete m_metaCat; 00447 m_metaCat = NULL; 00448 } 00449 00450 QString KuitSemanticsPrivate::metaTr (const char *ctxt, const char *id) const 00451 { 00452 if (m_metaCat == NULL) { 00453 return QString::fromLatin1(id); 00454 } 00455 return m_metaCat->translate(ctxt, id); 00456 } 00457 00458 void KuitSemanticsPrivate::setFormattingPatterns () 00459 { 00460 using namespace Kuit; 00461 00462 // Macro to expedite setting the patterns. 00463 #undef SET_PATTERN 00464 #define SET_PATTERN(tag, atts, fmt, ctxt_ptrn) do { \ 00465 QSet<AttVar> aset; \ 00466 aset << atts; \ 00467 int akey = attSetKey(aset); \ 00468 QString pattern = metaTr(ctxt_ptrn); \ 00469 m_patterns[tag][akey][fmt] = pattern; \ 00470 /* Make Term pattern same as Plain, unless explicitly given. */ \ 00471 if (fmt == Fmt::Plain && !m_patterns[tag][akey].contains(Fmt::Term)) { \ 00472 m_patterns[tag][akey][Fmt::Term] = pattern; \ 00473 } \ 00474 } while (0) 00475 00476 // Normal I18N_NOOP2 removes context, but below we need both. 00477 #undef I18N_NOOP2 00478 #define I18N_NOOP2(ctxt, msg) ctxt, msg 00479 00480 // Some of the formatting patterns are intentionally not exposed for 00481 // localization. 00482 #undef XXXX_NOOP2 00483 #define XXXX_NOOP2(ctxt, msg) ctxt, msg 00484 00485 // NOTE: The following "i18n:" comments are oddly placed in order that 00486 // xgettext extracts them properly. 00487 00488 // -------> Title 00489 SET_PATTERN(Tag::Title, Att::None, Fmt::Plain, 00490 I18N_NOOP2("@title/plain", 00491 // i18n: The following messages, with msgctxt "@tag/modifier", 00492 // are KUIT patterns for formatting the text found inside semantic tags. 00493 // For review of the KUIT semantic markup, see the article on Techbase: 00494 // http://techbase.kde.org/Development/Tutorials/Localization/i18n_Semantics 00495 // The "/modifier" tells if the pattern is used for plain text, or rich text 00496 // which can use HTML tags. 00497 // You may be in general satisfied with the patterns as they are in the 00498 // original. Some things you may think about changing: 00499 // - the proper quotes, those used in msgid are English-standard 00500 // - the <i> and <b> tags, does your language script work well with them? 00501 "== %1 ==")); 00502 SET_PATTERN(Tag::Title, Att::None, Fmt::Rich, 00503 I18N_NOOP2("@title/rich", 00504 // i18n: KUIT pattern, see the comment to the first of these entries above. 00505 "<h2>%1</h2>")); 00506 00507 // -------> Subtitle 00508 SET_PATTERN(Tag::Subtitle, Att::None, Fmt::Plain, 00509 I18N_NOOP2("@subtitle/plain", 00510 // i18n: KUIT pattern, see the comment to the first of these entries above. 00511 "~ %1 ~")); 00512 SET_PATTERN(Tag::Subtitle, Att::None, Fmt::Rich, 00513 I18N_NOOP2("@subtitle/rich", 00514 // i18n: KUIT pattern, see the comment to the first of these entries above. 00515 "<h3>%1</h3>")); 00516 00517 // -------> Para 00518 SET_PATTERN(Tag::Para, Att::None, Fmt::Plain, 00519 XXXX_NOOP2("@para/plain", 00520 // i18n: KUIT pattern, see the comment to the first of these entries above. 00521 "%1")); 00522 SET_PATTERN(Tag::Para, Att::None, Fmt::Rich, 00523 XXXX_NOOP2("@para/rich", 00524 // i18n: KUIT pattern, see the comment to the first of these entries above. 00525 "<p>%1</p>")); 00526 00527 // -------> List 00528 SET_PATTERN(Tag::List, Att::None, Fmt::Plain, 00529 XXXX_NOOP2("@list/plain", 00530 // i18n: KUIT pattern, see the comment to the first of these entries above. 00531 "%1")); 00532 SET_PATTERN(Tag::List, Att::None, Fmt::Rich, 00533 XXXX_NOOP2("@list/rich", 00534 // i18n: KUIT pattern, see the comment to the first of these entries above. 00535 "<ul>%1</ul>")); 00536 00537 // -------> Item 00538 SET_PATTERN(Tag::Item, Att::None, Fmt::Plain, 00539 I18N_NOOP2("@item/plain", 00540 // i18n: KUIT pattern, see the comment to the first of these entries above. 00541 " * %1")); 00542 SET_PATTERN(Tag::Item, Att::None, Fmt::Rich, 00543 I18N_NOOP2("@item/rich", 00544 // i18n: KUIT pattern, see the comment to the first of these entries above. 00545 "<li>%1</li>")); 00546 00547 // -------> Note 00548 SET_PATTERN(Tag::Note, Att::None, Fmt::Plain, 00549 I18N_NOOP2("@note/plain", 00550 // i18n: KUIT pattern, see the comment to the first of these entries above. 00551 "Note: %1")); 00552 SET_PATTERN(Tag::Note, Att::None, Fmt::Rich, 00553 I18N_NOOP2("@note/rich", 00554 // i18n: KUIT pattern, see the comment to the first of these entries above. 00555 "<i>Note</i>: %1")); 00556 SET_PATTERN(Tag::Note, Att::Label, Fmt::Plain, 00557 I18N_NOOP2("@note-with-label/plain\n" 00558 "%1 is the note label, %2 is the text", 00559 // i18n: KUIT pattern, see the comment to the first of these entries above. 00560 "%1: %2")); 00561 SET_PATTERN(Tag::Note, Att::Label, Fmt::Rich, 00562 I18N_NOOP2("@note-with-label/rich\n" 00563 "%1 is the note label, %2 is the text", 00564 // i18n: KUIT pattern, see the comment to the first of these entries above. 00565 "<i>%1</i>: %2")); 00566 00567 // -------> Warning 00568 SET_PATTERN(Tag::Warning, Att::None, Fmt::Plain, 00569 I18N_NOOP2("@warning/plain", 00570 // i18n: KUIT pattern, see the comment to the first of these entries above. 00571 "WARNING: %1")); 00572 SET_PATTERN(Tag::Warning, Att::None, Fmt::Rich, 00573 I18N_NOOP2("@warning/rich", 00574 // i18n: KUIT pattern, see the comment to the first of these entries above. 00575 "<b>Warning</b>: %1")); 00576 SET_PATTERN(Tag::Warning, Att::Label, Fmt::Plain, 00577 I18N_NOOP2("@warning-with-label/plain\n" 00578 "%1 is the warning label, %2 is the text", 00579 // i18n: KUIT pattern, see the comment to the first of these entries above. 00580 "%1: %2")); 00581 SET_PATTERN(Tag::Warning, Att::Label, Fmt::Rich, 00582 I18N_NOOP2("@warning-with-label/rich\n" 00583 "%1 is the warning label, %2 is the text", 00584 // i18n: KUIT pattern, see the comment to the first of these entries above. 00585 "<b>%1</b>: %2")); 00586 00587 // -------> Link 00588 SET_PATTERN(Tag::Link, Att::None, Fmt::Plain, 00589 XXXX_NOOP2("@link/plain", 00590 // i18n: KUIT pattern, see the comment to the first of these entries above. 00591 "%1")); 00592 SET_PATTERN(Tag::Link, Att::None, Fmt::Rich, 00593 XXXX_NOOP2("@link/rich", 00594 // i18n: KUIT pattern, see the comment to the first of these entries above. 00595 "<a href=\"%1\">%1</a>")); 00596 SET_PATTERN(Tag::Link, Att::Url, Fmt::Plain, 00597 I18N_NOOP2("@link-with-description/plain\n" 00598 "%1 is the URL, %2 is the descriptive text", 00599 // i18n: KUIT pattern, see the comment to the first of these entries above. 00600 "%2 (%1)")); 00601 SET_PATTERN(Tag::Link, Att::Url, Fmt::Rich, 00602 I18N_NOOP2("@link-with-description/rich\n" 00603 "%1 is the URL, %2 is the descriptive text", 00604 // i18n: KUIT pattern, see the comment to the first of these entries above. 00605 "<a href=\"%1\">%2</a>")); 00606 00607 // -------> Filename 00608 SET_PATTERN(Tag::Filename, Att::None, Fmt::Plain, 00609 I18N_NOOP2("@filename/plain", 00610 // i18n: KUIT pattern, see the comment to the first of these entries above. 00611 "‘%1’")); 00612 SET_PATTERN(Tag::Filename, Att::None, Fmt::Rich, 00613 I18N_NOOP2("@filename/rich", 00614 // i18n: KUIT pattern, see the comment to the first of these entries above. 00615 "<tt>%1</tt>")); 00616 00617 // -------> Application 00618 SET_PATTERN(Tag::Application, Att::None, Fmt::Plain, 00619 I18N_NOOP2("@application/plain", 00620 // i18n: KUIT pattern, see the comment to the first of these entries above. 00621 "%1")); 00622 SET_PATTERN(Tag::Application, Att::None, Fmt::Rich, 00623 I18N_NOOP2("@application/rich", 00624 // i18n: KUIT pattern, see the comment to the first of these entries above. 00625 "%1")); 00626 00627 // -------> Command 00628 SET_PATTERN(Tag::Command, Att::None, Fmt::Plain, 00629 I18N_NOOP2("@command/plain", 00630 // i18n: KUIT pattern, see the comment to the first of these entries above. 00631 "%1")); 00632 SET_PATTERN(Tag::Command, Att::None, Fmt::Rich, 00633 I18N_NOOP2("@command/rich", 00634 // i18n: KUIT pattern, see the comment to the first of these entries above. 00635 "<tt>%1</tt>")); 00636 SET_PATTERN(Tag::Command, Att::Section, Fmt::Plain, 00637 I18N_NOOP2("@command-with-section/plain\n" 00638 "%1 is the command name, %2 is its man section", 00639 // i18n: KUIT pattern, see the comment to the first of these entries above. 00640 "%1(%2)")); 00641 SET_PATTERN(Tag::Command, Att::Section, Fmt::Rich, 00642 I18N_NOOP2("@command-with-section/rich\n" 00643 "%1 is the command name, %2 is its man section", 00644 // i18n: KUIT pattern, see the comment to the first of these entries above. 00645 "<tt>%1(%2)</tt>")); 00646 00647 // -------> Resource 00648 SET_PATTERN(Tag::Resource, Att::None, Fmt::Plain, 00649 I18N_NOOP2("@resource/plain", 00650 // i18n: KUIT pattern, see the comment to the first of these entries above. 00651 "“%1”")); 00652 SET_PATTERN(Tag::Resource, Att::None, Fmt::Rich, 00653 I18N_NOOP2("@resource/rich", 00654 // i18n: KUIT pattern, see the comment to the first of these entries above. 00655 "“%1”")); 00656 00657 // -------> Icode 00658 SET_PATTERN(Tag::Icode, Att::None, Fmt::Plain, 00659 I18N_NOOP2("@icode/plain", 00660 // i18n: KUIT pattern, see the comment to the first of these entries above. 00661 "“%1”")); 00662 SET_PATTERN(Tag::Icode, Att::None, Fmt::Rich, 00663 I18N_NOOP2("@icode/rich", 00664 // i18n: KUIT pattern, see the comment to the first of these entries above. 00665 "<tt>%1</tt>")); 00666 00667 // -------> Bcode 00668 SET_PATTERN(Tag::Bcode, Att::None, Fmt::Plain, 00669 XXXX_NOOP2("@bcode/plain", 00670 // i18n: KUIT pattern, see the comment to the first of these entries above. 00671 "\n%1\n")); 00672 SET_PATTERN(Tag::Bcode, Att::None, Fmt::Rich, 00673 XXXX_NOOP2("@bcode/rich", 00674 // i18n: KUIT pattern, see the comment to the first of these entries above. 00675 "<pre>%1</pre>")); 00676 00677 // -------> Shortcut 00678 SET_PATTERN(Tag::Shortcut, Att::None, Fmt::Plain, 00679 I18N_NOOP2("@shortcut/plain", 00680 // i18n: KUIT pattern, see the comment to the first of these entries above. 00681 "%1")); 00682 SET_PATTERN(Tag::Shortcut, Att::None, Fmt::Rich, 00683 I18N_NOOP2("@shortcut/rich", 00684 // i18n: KUIT pattern, see the comment to the first of these entries above. 00685 "<b>%1</b>")); 00686 00687 // -------> Interface 00688 SET_PATTERN(Tag::Interface, Att::None, Fmt::Plain, 00689 I18N_NOOP2("@interface/plain", 00690 // i18n: KUIT pattern, see the comment to the first of these entries above. 00691 "|%1|")); 00692 SET_PATTERN(Tag::Interface, Att::None, Fmt::Rich, 00693 I18N_NOOP2("@interface/rich", 00694 // i18n: KUIT pattern, see the comment to the first of these entries above. 00695 "<i>%1</i>")); 00696 00697 // -------> Emphasis 00698 SET_PATTERN(Tag::Emphasis, Att::None, Fmt::Plain, 00699 I18N_NOOP2("@emphasis/plain", 00700 // i18n: KUIT pattern, see the comment to the first of these entries above. 00701 "*%1*")); 00702 SET_PATTERN(Tag::Emphasis, Att::None, Fmt::Rich, 00703 I18N_NOOP2("@emphasis/rich", 00704 // i18n: KUIT pattern, see the comment to the first of these entries above. 00705 "<i>%1</i>")); 00706 SET_PATTERN(Tag::Emphasis, Att::Strong, Fmt::Plain, 00707 I18N_NOOP2("@emphasis-strong/plain", 00708 // i18n: KUIT pattern, see the comment to the first of these entries above. 00709 "**%1**")); 00710 SET_PATTERN(Tag::Emphasis, Att::Strong, Fmt::Rich, 00711 I18N_NOOP2("@emphasis-strong/rich", 00712 // i18n: KUIT pattern, see the comment to the first of these entries above. 00713 "<b>%1</b>")); 00714 00715 // -------> Placeholder 00716 SET_PATTERN(Tag::Placeholder, Att::None, Fmt::Plain, 00717 I18N_NOOP2("@placeholder/plain", 00718 // i18n: KUIT pattern, see the comment to the first of these entries above. 00719 "<%1>")); 00720 SET_PATTERN(Tag::Placeholder, Att::None, Fmt::Rich, 00721 I18N_NOOP2("@placeholder/rich", 00722 // i18n: KUIT pattern, see the comment to the first of these entries above. 00723 "<<i>%1</i>>")); 00724 00725 // -------> Email 00726 SET_PATTERN(Tag::Email, Att::None, Fmt::Plain, 00727 I18N_NOOP2("@email/plain", 00728 // i18n: KUIT pattern, see the comment to the first of these entries above. 00729 "<%1>")); 00730 SET_PATTERN(Tag::Email, Att::None, Fmt::Rich, 00731 I18N_NOOP2("@email/rich", 00732 // i18n: KUIT pattern, see the comment to the first of these entries above. 00733 "<<a href=\"mailto:%1\">%1</a>>")); 00734 SET_PATTERN(Tag::Email, Att::Address, Fmt::Plain, 00735 I18N_NOOP2("@email-with-name/plain\n" 00736 "%1 is name, %2 is address", 00737 // i18n: KUIT pattern, see the comment to the first of these entries above. 00738 "%1 <%2>")); 00739 SET_PATTERN(Tag::Email, Att::Address, Fmt::Rich, 00740 I18N_NOOP2("@email-with-name/rich\n" 00741 "%1 is name, %2 is address", 00742 // i18n: KUIT pattern, see the comment to the first of these entries above. 00743 "<a href=\"mailto:%2\">%1</a>")); 00744 00745 // -------> Envar 00746 SET_PATTERN(Tag::Envar, Att::None, Fmt::Plain, 00747 I18N_NOOP2("@envar/plain", 00748 // i18n: KUIT pattern, see the comment to the first of these entries above. 00749 "$%1")); 00750 SET_PATTERN(Tag::Envar, Att::None, Fmt::Rich, 00751 I18N_NOOP2("@envar/rich", 00752 // i18n: KUIT pattern, see the comment to the first of these entries above. 00753 "<tt>$%1</tt>")); 00754 00755 // -------> Message 00756 SET_PATTERN(Tag::Message, Att::None, Fmt::Plain, 00757 I18N_NOOP2("@message/plain", 00758 // i18n: KUIT pattern, see the comment to the first of these entries above. 00759 "/%1/")); 00760 SET_PATTERN(Tag::Message, Att::None, Fmt::Rich, 00761 I18N_NOOP2("@message/rich", 00762 // i18n: KUIT pattern, see the comment to the first of these entries above. 00763 "<i>%1</i>")); 00764 00765 // -------> Nl 00766 SET_PATTERN(Tag::Nl, Att::None, Fmt::Plain, 00767 XXXX_NOOP2("@nl/plain", 00768 // i18n: KUIT pattern, see the comment to the first of these entries above. 00769 "%1\n")); 00770 SET_PATTERN(Tag::Nl, Att::None, Fmt::Rich, 00771 XXXX_NOOP2("@nl/rich", 00772 // i18n: KUIT pattern, see the comment to the first of these entries above. 00773 "%1<br/>")); 00774 } 00775 00776 void KuitSemanticsPrivate::setTextTransformData () 00777 { 00778 // Mask metaTr with I18N_NOOP2 to have stuff extracted. 00779 #undef I18N_NOOP2 00780 #define I18N_NOOP2(ctxt, msg) metaTr(ctxt, msg) 00781 00782 // i18n: Decide which string is used to delimit keys in a keyboard 00783 // shortcut (e.g. + in Ctrl+Alt+Tab) in plain text. 00784 m_comboKeyDelim[Kuit::Fmt::Plain] = I18N_NOOP2("shortcut-key-delimiter/plain", "+"); 00785 m_comboKeyDelim[Kuit::Fmt::Term] = m_comboKeyDelim[Kuit::Fmt::Plain]; 00786 // i18n: Decide which string is used to delimit keys in a keyboard 00787 // shortcut (e.g. + in Ctrl+Alt+Tab) in rich text. 00788 m_comboKeyDelim[Kuit::Fmt::Rich] = I18N_NOOP2("shortcut-key-delimiter/rich", "+"); 00789 00790 // i18n: Decide which string is used to delimit elements in a GUI path 00791 // (e.g. -> in "Go to Settings->Advanced->Core tab.") in plain text. 00792 m_guiPathDelim[Kuit::Fmt::Plain] = I18N_NOOP2("gui-path-delimiter/plain", "→"); 00793 m_guiPathDelim[Kuit::Fmt::Term] = m_guiPathDelim[Kuit::Fmt::Plain]; 00794 // i18n: Decide which string is used to delimit elements in a GUI path 00795 // (e.g. -> in "Go to Settings->Advanced->Core tab.") in rich text. 00796 m_guiPathDelim[Kuit::Fmt::Rich] = I18N_NOOP2("gui-path-delimiter/rich", "→"); 00797 // NOTE: The '→' glyph seems to be available in all widespread fonts. 00798 00799 // Collect keyboard key names. 00800 #undef SET_KEYNAME 00801 #define SET_KEYNAME(rawname) do { \ 00802 /* Normalize key, trim and all lower-case. */ \ 00803 QString normname = QString::fromLatin1(rawname).trimmed().toLower(); \ 00804 m_keyNames[normname] = metaTr("keyboard-key-name", rawname); \ 00805 } while (0) 00806 00807 // Now we need I18N_NOOP2 that does remove context. 00808 #undef I18N_NOOP2 00809 #define I18N_NOOP2(ctxt, msg) msg 00810 00811 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Alt")); 00812 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "AltGr")); 00813 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Backspace")); 00814 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "CapsLock")); 00815 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Control")); 00816 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Ctrl")); 00817 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Del")); 00818 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Delete")); 00819 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Down")); 00820 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "End")); 00821 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Enter")); 00822 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Esc")); 00823 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Escape")); 00824 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Home")); 00825 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Hyper")); 00826 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Ins")); 00827 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Insert")); 00828 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Left")); 00829 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Menu")); 00830 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Meta")); 00831 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "NumLock")); 00832 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "PageDown")); 00833 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "PageUp")); 00834 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "PgDown")); 00835 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "PgUp")); 00836 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "PauseBreak")); 00837 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "PrintScreen")); 00838 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "PrtScr")); 00839 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Return")); 00840 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Right")); 00841 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "ScrollLock")); 00842 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Shift")); 00843 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Space")); 00844 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Super")); 00845 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "SysReq")); 00846 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Tab")); 00847 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Up")); 00848 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Win")); 00849 // TODO: Add rest of the key names? 00850 00851 // i18n: Pattern for the function keys. 00852 SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "F%1")); 00853 } 00854 00855 QString KuitSemanticsPrivate::format (const QString &text, 00856 const QString &ctxt) const 00857 { 00858 // Parse context marker to determine format. 00859 Kuit::FmtVar fmtExplicit = formatFromContextMarker(ctxt, text); 00860 00861 // Quick check: are there any tags at all? 00862 if (text.indexOf(QLatin1Char('<')) < 0) { 00863 return finalizeVisualText(text, fmtExplicit); 00864 } 00865 00866 // If format not explicitly given, heuristically determine 00867 // implicit format based on presence or lack of HTML tags. 00868 Kuit::FmtVar fmtImplicit = fmtExplicit; 00869 if (fmtExplicit == Kuit::Fmt::None) { 00870 fmtImplicit = formatFromTags(text); 00871 } 00872 00873 // Decide on the top tag, either TopLong or TopShort, 00874 // and wrap the text with it. 00875 Kuit::TagVar toptag; 00876 QString wtext = equipTopTag(text, toptag); 00877 00878 // Format the text. 00879 QString ftext = semanticToVisualText(wtext, fmtExplicit, fmtImplicit); 00880 if (ftext.isEmpty()) { // error while processing markup 00881 return salvageMarkup(text, fmtImplicit); 00882 } 00883 00884 return ftext; 00885 } 00886 00887 int KuitSemanticsPrivate::attSetKey (const QSet<Kuit::AttVar> &aset) 00888 { 00889 QList<Kuit::AttVar> alist = aset.toList(); 00890 qSort(alist); 00891 int key = 0; 00892 int tenp = 1; 00893 foreach (const Kuit::AttVar &att, alist) { 00894 key += att * tenp; 00895 tenp *= 10; 00896 } 00897 return key; 00898 } 00899 00900 Kuit::FmtVar KuitSemanticsPrivate::formatFromContextMarker ( 00901 const QString &ctxmark_, const QString &text) 00902 { 00903 #ifdef NDEBUG 00904 Q_UNUSED(text); 00905 #endif 00906 00907 KuitSemanticsStaticData *s = semanticsStaticData; 00908 00909 // Semantic context marker is in the form @rolname:cuename/fmtname, 00910 // and must start just after any leading whitespace in the context string. 00911 QString rolname; 00912 QString fmtname; 00913 QString cuename; 00914 QString ctxmark = ctxmark_.trimmed(); 00915 if (ctxmark.startsWith(QLatin1Char('@'))) { // found context marker 00916 static QRegExp wsRx(QString::fromLatin1("\\s")); 00917 ctxmark = ctxmark.mid(1, wsRx.indexIn(ctxmark) - 1); 00918 00919 // Possible visual format. 00920 int pfmt = ctxmark.indexOf(QLatin1Char('/')); 00921 if (pfmt >= 0) { 00922 fmtname = ctxmark.mid(pfmt + 1); 00923 ctxmark = ctxmark.left(pfmt); 00924 } 00925 00926 // Possible interface subcue. 00927 int pcue = ctxmark.indexOf(QLatin1Char(':')); 00928 if (pcue >= 0) { 00929 cuename = ctxmark.mid(pcue + 1); 00930 ctxmark = ctxmark.left(pcue); 00931 } 00932 00933 // Semantic role. 00934 rolname = ctxmark; 00935 } 00936 // Names remain empty if marker was not found, which is ok. 00937 00938 // Normalize names. 00939 rolname = rolname.trimmed().toLower(); 00940 cuename = cuename.trimmed().toLower(); 00941 fmtname = fmtname.trimmed().toLower(); 00942 00943 // Set role from name. 00944 Kuit::RolVar rol; 00945 if (s->knownRols.contains(rolname)) { // known role 00946 rol = s->knownRols[rolname]; 00947 } 00948 else { // unknown role 00949 rol = Kuit::Rol::None; 00950 if (!rolname.isEmpty()) { 00951 kDebug(173) << QString::fromLatin1("Unknown semantic role '@%1' in " 00952 "context marker for message {%2}.") 00953 .arg(rolname, shorten(text)); 00954 } 00955 } 00956 00957 // Set subcue from name. 00958 Kuit::CueVar cue; 00959 if (s->knownCues.contains(cuename)) { // known subcue 00960 cue = s->knownCues[cuename]; 00961 } 00962 else { // unknown or not given subcue 00963 cue = Kuit::Cue::None; 00964 if (!cuename.isEmpty()) { 00965 kDebug(173) << QString::fromLatin1("Unknown interface subcue ':%1' in " 00966 "context marker for message {%2}.") 00967 .arg(cuename, shorten(text)); 00968 } 00969 } 00970 00971 // Set format from name, or by derivation from contex/subcue. 00972 Kuit::FmtVar fmt; 00973 if (s->knownFmts.contains(fmtname)) { // known format 00974 fmt = s->knownFmts[fmtname]; 00975 } 00976 else { // unknown or not given format 00977 00978 // Check first if there is a format defined for role/subcue 00979 // combination, than for role only, then default to none. 00980 if (s->defFmts.contains(rol)) { 00981 if (s->defFmts[rol].contains(cue)) { 00982 fmt = s->defFmts[rol][cue]; 00983 } 00984 else { 00985 fmt = s->defFmts[rol][Kuit::Cue::None]; 00986 } 00987 } 00988 else { 00989 fmt = Kuit::Fmt::None; 00990 } 00991 00992 if (!fmtname.isEmpty()) { 00993 kDebug(173) << QString::fromLatin1("Unknown visual format '/%1' in " 00994 "context marker for message {%2}.") 00995 .arg(fmtname, shorten(text)); 00996 } 00997 } 00998 00999 return fmt; 01000 } 01001 01002 Kuit::FmtVar KuitSemanticsPrivate::formatFromTags (const QString &text) 01003 { 01004 KuitSemanticsStaticData *s = semanticsStaticData; 01005 static QRegExp staticTagRx(QString::fromLatin1("<\\s*(\\w+)[^>]*>")); 01006 01007 QRegExp tagRx = staticTagRx; // for thread-safety 01008 int p = tagRx.indexIn(text); 01009 while (p >= 0) { 01010 QString tagname = tagRx.capturedTexts().at(1).toLower(); 01011 if (s->qtHtmlTagNames.contains(tagname)) { 01012 return Kuit::Fmt::Rich; 01013 } 01014 p = tagRx.indexIn(text, p + tagRx.matchedLength()); 01015 } 01016 return Kuit::Fmt::Plain; 01017 } 01018 01019 QString KuitSemanticsPrivate::equipTopTag (const QString &text_, 01020 Kuit::TagVar &toptag) 01021 { 01022 KuitSemanticsStaticData *s = semanticsStaticData; 01023 01024 // Unless the text opens either with TopLong or TopShort tags, 01025 // make a guess: if it opens with one of Title, Subtitle, Para, 01026 // consider it TopLong, otherwise TopShort. 01027 static QRegExp opensWithTagRx(QString::fromLatin1("^\\s*<\\s*(\\w+)[^>]*>")); 01028 bool explicitTopTag = false; 01029 01030 QString text = text_; 01031 int p = opensWithTagRx.indexIn(text); 01032 01033 // <qt> or <html> tag are to be ignored for deciding the top tag. 01034 if (p >= 0) { 01035 QString fullmatch = opensWithTagRx.capturedTexts().at(0); 01036 QString tagname = opensWithTagRx.capturedTexts().at(1).toLower(); 01037 if (tagname == QLatin1String("qt") || tagname == QLatin1String("html")) { 01038 // Kill the tag and see if there is another one following, 01039 // for primary check below. 01040 text = text.mid(fullmatch.length()); 01041 p = opensWithTagRx.indexIn(text); 01042 } 01043 } 01044 01045 // Check the first non-<qt>/<html> tag. 01046 if (p >= 0) { // opens with a tag 01047 QString tagname = opensWithTagRx.capturedTexts().at(1).toLower(); 01048 if (s->knownTags.contains(tagname)) { // a known tag 01049 Kuit::TagVar tag = s->knownTags[tagname]; 01050 if ( tag == Kuit::Tag::TopLong 01051 || tag == Kuit::Tag::TopShort) { // explicitly given top tag 01052 toptag = tag; 01053 explicitTopTag = true; 01054 } 01055 else if ( tag == Kuit::Tag::Para 01056 || tag == Kuit::Tag::Title 01057 || tag == Kuit::Tag::Subtitle) { // one of long text tags 01058 toptag = Kuit::Tag::TopLong; 01059 } 01060 else { // not one of long text tags 01061 toptag = Kuit::Tag::TopShort; 01062 } 01063 } 01064 else { // not a KUIT tag 01065 toptag = Kuit::Tag::TopShort; 01066 } 01067 } 01068 else { // doesn't open with a tag 01069 toptag = Kuit::Tag::TopShort; 01070 } 01071 01072 // Wrap text with top tag if not explicitly given. 01073 if (!explicitTopTag) { 01074 return QLatin1Char('<') + s->tagNames[toptag] + QLatin1Char('>') 01075 + text_ // original text, not the one possibly stripped above 01076 + QLatin1String("</") + s->tagNames[toptag] + QLatin1Char('>'); 01077 } 01078 else { 01079 return text; 01080 } 01081 } 01082 01083 #define ENTITY_SUBRX "[a-z]+|#[0-9]+|#x[0-9a-fA-F]+" 01084 01085 QString KuitSemanticsPrivate::semanticToVisualText (const QString &text_, 01086 Kuit::FmtVar fmtExp_, 01087 Kuit::FmtVar fmtImp_) const 01088 { 01089 KuitSemanticsStaticData *s = semanticsStaticData; 01090 01091 // Replace &-shortcut marker with "&", not to confuse the parser; 01092 // but do not touch & which forms an XML entity as it is. 01093 QString original = text_; 01094 QString text; 01095 int p = original.indexOf(QLatin1Char('&')); 01096 while (p >= 0) { 01097 text.append(original.mid(0, p + 1)); 01098 original.remove(0, p + 1); 01099 static QRegExp restRx(QString::fromLatin1("^("ENTITY_SUBRX");")); 01100 if (original.indexOf(restRx) != 0) { // not an entity 01101 text.append(QLatin1String("amp;")); 01102 } 01103 p = original.indexOf(QLatin1Char('&')); 01104 } 01105 text.append(original); 01106 01107 Kuit::FmtVar fmtExp = fmtExp_; 01108 Kuit::FmtVar fmtImp = fmtImp_; 01109 int numCtx = 0; 01110 bool hadQtTag = false; 01111 bool hadAnyHtmlTag = false; 01112 QStack<OpenEl> openEls; 01113 QXmlStreamReader xml(text); 01114 QStringRef lastElementName; 01115 01116 while (!xml.atEnd()) { 01117 xml.readNext(); 01118 01119 if (xml.isStartElement()) { 01120 lastElementName = xml.name(); 01121 01122 // Find first proper enclosing element tag. 01123 Kuit::TagVar etag = Kuit::Tag::None; 01124 for (int i = openEls.size() - 1; i >= 0; --i) { 01125 if (openEls[i].handling == OpenEl::Proper) { 01126 etag = openEls[i].tag; 01127 break; 01128 } 01129 } 01130 01131 // Collect data about this element. 01132 OpenEl oel = parseOpenEl(xml, etag, text); 01133 if (oel.name == QLatin1String("qt") || oel.name == QLatin1String("html")) { 01134 hadQtTag = true; 01135 } 01136 if (s->qtHtmlTagNames.contains(oel.name)) { 01137 hadAnyHtmlTag = true; 01138 } 01139 01140 // If this is top tag, check if it overrides the context marker 01141 // by its ctx attribute. 01142 if (openEls.isEmpty() && oel.avals.contains(Kuit::Att::Ctx)) { 01143 // Resolve format override. 01144 fmtExp = formatFromContextMarker(oel.avals[Kuit::Att::Ctx], text); 01145 fmtImp = fmtExp; 01146 } 01147 01148 // Record the new element on the parse stack. 01149 openEls.push(oel); 01150 01151 // Update numeric context. 01152 if (oel.tag == Kuit::Tag::Numid) { 01153 ++numCtx; 01154 } 01155 } 01156 else if (xml.isEndElement()) { 01157 // Get closed element data. 01158 OpenEl oel = openEls.pop(); 01159 01160 // If this was closing of the top element, we're done. 01161 if (openEls.isEmpty()) { 01162 // Return with final touches applied. 01163 return finalizeVisualText(oel.formattedText, fmtExp, 01164 hadQtTag, hadAnyHtmlTag); 01165 } 01166 01167 // Append formatted text segment. 01168 QString pt = openEls.top().formattedText; // preceding text 01169 openEls.top().formattedText += formatSubText(pt, oel, fmtImp, numCtx); 01170 01171 // Update numeric context. 01172 if (oel.tag == Kuit::Tag::Numid) { 01173 --numCtx; 01174 } 01175 } 01176 else if (xml.isCharacters()) { 01177 // Stream reader will automatically resolve default XML entities, 01178 // which is not desired in this case, as the final text may 01179 // be rich. Convert them back into entities. 01180 QString text = xml.text().toString(); 01181 QString ntext; 01182 foreach (const QChar &c, text) { 01183 if (s->xmlEntitiesInverse.contains(c)) { 01184 const QString entname = s->xmlEntitiesInverse[c]; 01185 ntext += QLatin1Char('&') + entname + QLatin1Char(';'); 01186 } else { 01187 ntext += c; 01188 } 01189 } 01190 openEls.top().formattedText += ntext; 01191 } 01192 } 01193 01194 if (xml.hasError()) { 01195 kDebug(173) << QString::fromLatin1("Markup error in message {%1}: %2. Last tag parsed: %3") 01196 .arg(shorten(text), xml.errorString(), lastElementName.toString()); 01197 return QString(); 01198 } 01199 01200 // Cannot reach here. 01201 return text; 01202 } 01203 01204 KuitSemanticsPrivate::OpenEl 01205 KuitSemanticsPrivate::parseOpenEl (const QXmlStreamReader &xml, 01206 Kuit::TagVar etag, 01207 const QString &text) const 01208 { 01209 #ifdef NDEBUG 01210 Q_UNUSED(text); 01211 #endif 01212 01213 KuitSemanticsStaticData *s = semanticsStaticData; 01214 01215 OpenEl oel; 01216 oel.name = xml.name().toString().toLower(); 01217 01218 // Collect attribute names and values, and format attribute string. 01219 QStringList attnams, attvals; 01220 foreach (const QXmlStreamAttribute &xatt, xml.attributes()) { 01221 attnams += xatt.name().toString().toLower(); 01222 attvals += xatt.value().toString(); 01223 QChar qc = attvals.last().indexOf(QLatin1Char('\'')) < 0 ? QLatin1Char('\'') : QLatin1Char('"'); 01224 oel.astr += QLatin1Char(' ') + attnams.last() + QLatin1Char('=') + qc + attvals.last() + qc; 01225 } 01226 01227 if (s->knownTags.contains(oel.name)) { // known KUIT element 01228 oel.tag = s->knownTags[oel.name]; 01229 01230 // If this element can be contained within enclosing element, 01231 // mark it proper, otherwise mark it for removal. 01232 if (etag == Kuit::Tag::None || s->tagSubs[etag].contains(oel.tag)) { 01233 oel.handling = OpenEl::Proper; 01234 } 01235 else { 01236 oel.handling = OpenEl::Dropout; 01237 kDebug(173) << QString::fromLatin1("Tag '%1' cannot be subtag of '%2' " 01238 "in message {%3}.") 01239 .arg(s->tagNames[oel.tag], s->tagNames[etag], 01240 shorten(text)); 01241 } 01242 01243 // Resolve attributes and compute attribute set key. 01244 QSet<Kuit::AttVar> attset; 01245 for (int i = 0; i < attnams.size(); ++i) { 01246 if (s->knownAtts.contains(attnams[i])) { 01247 Kuit::AttVar att = s->knownAtts[attnams[i]]; 01248 if (s->tagAtts[oel.tag].contains(att)) { 01249 attset << att; 01250 oel.avals[att] = attvals[i]; 01251 } 01252 else { 01253 kDebug(173) << QString::fromLatin1("Attribute '%1' cannot be used in " 01254 "tag '%2' in message {%3}.") 01255 .arg(attnams[i], oel.name, 01256 shorten(text)); 01257 } 01258 } 01259 else { 01260 kDebug(173) << QString::fromLatin1("Unknown semantic tag attribute '%1' " 01261 "in message {%2}.") 01262 .arg(attnams[i], shorten(text)); 01263 } 01264 } 01265 oel.akey = attSetKey(attset); 01266 } 01267 else if (oel.name == QLatin1String("qt") || oel.name == QLatin1String("html")) { 01268 // Drop qt/html tags (gets added in the end). 01269 oel.handling = OpenEl::Dropout; 01270 } 01271 else { // other element, leave it in verbatim 01272 oel.handling = OpenEl::Ignored; 01273 if (!s->qtHtmlTagNames.contains(oel.name)) { 01274 kDebug(173) << QString::fromLatin1("Tag '%1' is neither semantic nor HTML in " 01275 "message {%3}.") 01276 .arg(oel.name, shorten(text)); 01277 } 01278 } 01279 01280 return oel; 01281 } 01282 01283 QString KuitSemanticsPrivate::visualPattern (Kuit::TagVar tag, int akey, 01284 Kuit::FmtVar fmt) const 01285 { 01286 // Default pattern: simple substitution. 01287 QString pattern = QString::fromLatin1("%1"); 01288 01289 // See if there is a pattern specifically for this element. 01290 if ( m_patterns.contains(tag) 01291 && m_patterns[tag].contains(akey) 01292 && m_patterns[tag][akey].contains(fmt)) 01293 { 01294 pattern = m_patterns[tag][akey][fmt]; 01295 } 01296 01297 return pattern; 01298 } 01299 01300 QString KuitSemanticsPrivate::formatSubText (const QString &ptext, 01301 const OpenEl &oel, 01302 Kuit::FmtVar fmt, 01303 int numctx) const 01304 { 01305 KuitSemanticsStaticData *s = semanticsStaticData; 01306 01307 if (oel.handling == OpenEl::Proper) { 01308 // Select formatting pattern. 01309 QString pattern = visualPattern(oel.tag, oel.akey, fmt); 01310 01311 // Some tags modify their text. 01312 QString mtext = modifyTagText(oel.formattedText, oel.tag, oel.avals, 01313 numctx, fmt); 01314 01315 using namespace Kuit; 01316 01317 // Format text according to pattern. 01318 QString ftext; 01319 if (oel.tag == Tag::Link && oel.avals.contains(Att::Url)) { 01320 ftext = pattern.arg(oel.avals[Att::Url], mtext); 01321 } 01322 else if (oel.tag == Tag::Command && oel.avals.contains(Att::Section)) { 01323 ftext = pattern.arg(mtext, oel.avals[Att::Section]); 01324 } 01325 else if (oel.tag == Tag::Email && oel.avals.contains(Att::Address)) { 01326 ftext = pattern.arg(mtext, oel.avals[Att::Address]); 01327 } 01328 else if (oel.tag == Tag::Note && oel.avals.contains(Att::Label)) { 01329 ftext = pattern.arg(oel.avals[Att::Label], mtext); 01330 } 01331 else if (oel.tag == Tag::Warning && oel.avals.contains(Att::Label)) { 01332 ftext = pattern.arg(oel.avals[Att::Label], mtext); 01333 } 01334 else { 01335 ftext = pattern.arg(mtext); 01336 } 01337 01338 // Handle leading newlines, if this is not start of the text 01339 // (ptext is the preceding text). 01340 if (!ptext.isEmpty() && s->leadingNewlines.contains(oel.tag)) { 01341 // Count number of present newlines. 01342 int pnumle, pnumtr, fnumle, fnumtr; 01343 countWrappingNewlines(ptext, pnumle, pnumtr); 01344 countWrappingNewlines(ftext, fnumle, fnumtr); 01345 // Number of leading newlines already present. 01346 int numle = pnumtr + fnumle; 01347 // The required extra newlines. 01348 QString strle; 01349 if (numle < s->leadingNewlines[oel.tag]) { 01350 strle = QString(s->leadingNewlines[oel.tag] - numle, QLatin1Char('\n')); 01351 } 01352 ftext = strle + ftext; 01353 } 01354 01355 return ftext; 01356 } 01357 else if (oel.handling == OpenEl::Ignored) { 01358 if (oel.name == QLatin1String("br") || oel.name == QLatin1String("hr")) { 01359 // Close these tags in-place (just for looks). 01360 return QLatin1Char('<') + oel.name + QLatin1String("/>"); 01361 } 01362 else { 01363 return QLatin1Char('<') + oel.name + oel.astr + QLatin1Char('>') 01364 + oel.formattedText 01365 + QLatin1String("</") + oel.name + QLatin1Char('>'); 01366 } 01367 } 01368 else { // oel.handling == OpenEl::Dropout 01369 return oel.formattedText; 01370 } 01371 } 01372 01373 void KuitSemanticsPrivate::countWrappingNewlines (const QString &text, 01374 int &numle, int &numtr) 01375 { 01376 int len = text.length(); 01377 // Number of newlines at start of text. 01378 numle = 0; 01379 while (numle < len && text[numle] == QLatin1Char('\n')) { 01380 ++numle; 01381 } 01382 // Number of newlines at end of text. 01383 numtr = 0; 01384 while (numtr < len && text[len - numtr - 1] == QLatin1Char('\n')) { 01385 ++numtr; 01386 } 01387 } 01388 01389 QString KuitSemanticsPrivate::modifyTagText (const QString &text, 01390 Kuit::TagVar tag, 01391 const QHash<Kuit::AttVar, QString> &avals, 01392 int numctx, 01393 Kuit::FmtVar fmt) const 01394 { 01395 // numctx < 1 means that the number is not in numeric-id context. 01396 if ( (tag == Kuit::Tag::NumIntg || tag == Kuit::Tag::NumReal) \ 01397 && numctx < 1) 01398 { 01399 int fieldWidth = avals.value(Kuit::Att::Width, QString(QLatin1Char('0'))).toInt(); 01400 const QString fillStr = avals.value(Kuit::Att::Fill, QString(QLatin1Char(' '))); 01401 const QChar fillChar = !fillStr.isEmpty() ? fillStr[0] : QChar::fromLatin1(' '); 01402 return QString::fromLatin1("%1").arg(KGlobal::locale()->formatNumber(text, false), 01403 fieldWidth, fillChar); 01404 } 01405 else if (tag == Kuit::Tag::Filename) { 01406 return QDir::toNativeSeparators(text); 01407 } 01408 else if (tag == Kuit::Tag::Shortcut) { 01409 return KuitFormats::toKeyCombo(text, m_comboKeyDelim[fmt], m_keyNames); 01410 } 01411 else if (tag == Kuit::Tag::Interface) { 01412 return KuitFormats::toInterfacePath(text, m_guiPathDelim[fmt]); 01413 } 01414 01415 // Fell through, no modification. 01416 return text; 01417 } 01418 01419 QString KuitSemanticsPrivate::finalizeVisualText (const QString &final, 01420 Kuit::FmtVar fmt, 01421 bool hadQtTag, 01422 bool hadAnyHtmlTag) const 01423 { 01424 KuitSemanticsStaticData *s = semanticsStaticData; 01425 01426 QString text = final; 01427 01428 // Resolve XML entities if format explicitly not rich 01429 // and no HTML tag encountered. 01430 if (fmt != Kuit::Fmt::Rich && !hadAnyHtmlTag) 01431 { 01432 static QRegExp staticEntRx(QLatin1String("&("ENTITY_SUBRX");")); 01433 // We have to have a local copy here, otherwise this function 01434 // will not be thread safe because QRegExp is not thread safe. 01435 QRegExp entRx = staticEntRx; 01436 int p = entRx.indexIn(text); 01437 QString plain; 01438 while (p >= 0) { 01439 QString ent = entRx.capturedTexts().at(1); 01440 plain.append(text.mid(0, p)); 01441 text.remove(0, p + ent.length() + 2); 01442 if (ent.startsWith(QLatin1Char('#'))) { // numeric character entity 01443 QChar c; 01444 bool ok; 01445 if (ent[1] == QLatin1Char('x')) { 01446 c = QChar(ent.mid(2).toInt(&ok, 16)); 01447 } else { 01448 c = QChar(ent.mid(1).toInt(&ok, 10)); 01449 } 01450 if (ok) { 01451 plain.append(c); 01452 } else { // unknown Unicode point, leave as is 01453 plain.append(QLatin1Char('&') + ent + QLatin1Char(';')); 01454 } 01455 } 01456 else if (s->xmlEntities.contains(ent)) { // known entity 01457 plain.append(s->xmlEntities[ent]); 01458 } else { // unknown entity, just leave as is 01459 plain.append(QLatin1Char('&') + ent + QLatin1Char(';')); 01460 } 01461 p = entRx.indexIn(text); 01462 } 01463 plain.append(text); 01464 text = plain; 01465 } 01466 01467 // Add top rich tag if format explicitly rich or such tag encountered. 01468 if (fmt == Kuit::Fmt::Rich || hadQtTag) { 01469 text = QString::fromLatin1("<html>") + text + QLatin1String("</html>"); 01470 } 01471 01472 return text; 01473 } 01474 01475 QString KuitSemanticsPrivate::salvageMarkup (const QString &text_, 01476 Kuit::FmtVar fmt) const 01477 { 01478 KuitSemanticsStaticData *s = semanticsStaticData; 01479 QString text = text_; 01480 QString ntext; 01481 int pos; 01482 01483 // Resolve KUIT tags simple-mindedly. 01484 01485 // - tags with content 01486 static QRegExp staticWrapRx(QLatin1String("(<\\s*(\\w+)\\b([^>]*)>)(.*)(<\\s*/\\s*\\2\\s*>)")); 01487 QRegExp wrapRx = staticWrapRx; // for thread-safety 01488 wrapRx.setMinimal(true); 01489 pos = 0; 01490 ntext.clear(); 01491 while (true) { 01492 int previousPos = pos; 01493 pos = wrapRx.indexIn(text, previousPos); 01494 if (pos < 0) { 01495 ntext += text.mid(previousPos); 01496 break; 01497 } 01498 ntext += text.mid(previousPos, pos - previousPos); 01499 const QStringList capts = wrapRx.capturedTexts(); 01500 QString tagname = capts[2].toLower(); 01501 QString content = salvageMarkup(capts[4], fmt); 01502 if (s->knownTags.contains(tagname)) { 01503 // Select formatting pattern. 01504 // TODO: Do not ignore attributes (in capts[3]). 01505 QString pattern = visualPattern(s->knownTags[tagname], 0, fmt); 01506 ntext += pattern.arg(content); 01507 } else { 01508 ntext += capts[1] + content + capts[5]; 01509 } 01510 pos += wrapRx.matchedLength(); 01511 } 01512 text = ntext; 01513 01514 // - content-less tags 01515 static QRegExp staticNowrRx(QLatin1String("<\\s*(\\w+)\\b([^>]*)/\\s*>")); 01516 QRegExp nowrRx = staticNowrRx; // for thread-safety 01517 nowrRx.setMinimal(true); 01518 pos = 0; 01519 ntext.clear(); 01520 while (true) { 01521 int previousPos = pos; 01522 pos = nowrRx.indexIn(text, previousPos); 01523 if (pos < 0) { 01524 ntext += text.mid(previousPos); 01525 break; 01526 } 01527 ntext += text.mid(previousPos, pos - previousPos); 01528 const QStringList capts = nowrRx.capturedTexts(); 01529 QString tagname = capts[1].toLower(); 01530 if (s->knownTags.contains(tagname)) { 01531 QString pattern = visualPattern(s->knownTags[tagname], 0, fmt); 01532 ntext += pattern.arg(QString()); 01533 } else { 01534 ntext += capts[0]; 01535 } 01536 pos += nowrRx.matchedLength(); 01537 } 01538 text = ntext; 01539 01540 return text; 01541 } 01542 01543 // ----------------------------------------------------------------------------- 01544 // The KuitSemantics methods, only delegate to KuitSemanticsPrivate. 01545 01546 KuitSemantics::KuitSemantics (const QString &lang) 01547 : d(new KuitSemanticsPrivate(lang)) 01548 { 01549 } 01550 01551 KuitSemantics::~KuitSemantics () 01552 { 01553 delete d; 01554 } 01555 01556 QString KuitSemantics::format (const QString &text, const QString &ctxt) const 01557 { 01558 return d->format(text, ctxt); 01559 } 01560 01561 bool KuitSemantics::mightBeRichText (const QString &text) 01562 { 01563 KuitSemanticsStaticData *s = semanticsStaticData; 01564 01565 // Check by appearance of a valid XML entity at first ampersand. 01566 int p1 = text.indexOf(QLatin1Char('&')); 01567 if (p1 >= 0) { 01568 p1 += 1; 01569 int p2 = text.indexOf(QLatin1Char(';'), p1); 01570 return (p2 > p1 && s->xmlEntities.contains(text.mid(p1, p2 - p1))); 01571 } 01572 01573 // Check by appearance of a valid Qt rich-text tag at first less-than. 01574 int tlen = text.length(); 01575 p1 = text.indexOf(QLatin1Char('<')); 01576 if (p1 >= 0) { 01577 p1 += 1; 01578 // Also allow first tag to be closing tag, 01579 // e.g. in case the text is pieced up with list.join("</foo><foo>") 01580 bool closing = false; 01581 while (p1 < tlen && (text[p1].isSpace() || text[p1] == QLatin1Char('/'))) { 01582 if (text[p1] == QLatin1Char('/')) { 01583 if (!closing) { 01584 closing = true; 01585 } else { 01586 return false; 01587 } 01588 } 01589 ++p1; 01590 } 01591 for (int p2 = p1; p2 < tlen; ++p2) { 01592 QChar c = text[p2]; 01593 if (c == QLatin1Char('>') || (!closing && c == QLatin1Char('/')) || c.isSpace()) { 01594 return s->qtHtmlTagNames.contains(text.mid(p1, p2 - p1)); 01595 } else if (!c.isLetter()) { 01596 return false; 01597 } 01598 } 01599 return false; 01600 } 01601 01602 return false; 01603 } 01604 01605 QString KuitSemantics::escape (const QString &text) 01606 { 01607 int tlen = text.length(); 01608 QString ntext; 01609 ntext.reserve(tlen); 01610 for (int i = 0; i < tlen; ++i) { 01611 QChar c = text[i]; 01612 if (c == QLatin1Char('&')) { 01613 ntext += QLatin1String("&"); 01614 } else if (c == QLatin1Char('<')) { 01615 ntext += QLatin1String("<"); 01616 } else if (c == QLatin1Char('>')) { 01617 ntext += QLatin1String(">"); 01618 } else if (c == QLatin1Char('\'')) { 01619 ntext += QLatin1String("'"); 01620 } else if (c == QLatin1Char('"')) { 01621 ntext += QLatin1String("""); 01622 } else { 01623 ntext += c; 01624 } 01625 } 01626 01627 return ntext; 01628 }
KDE 4.6 API Reference