• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

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

KDECore

Skip menu "KDECore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.6
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal