• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • 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 <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                            "&lt;%1&gt;"));
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                            "&lt;<i>%1</i>&gt;"));
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                            "&lt;%1&gt;"));
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                            "&lt;<a href=\"mailto:%1\">%1</a>&gt;"));
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 &lt;%2&gt;"));
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 "&amp;", 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("&amp;");
01614         } else if (c == QLatin1Char('<')) {
01615             ntext += QLatin1String("&lt;");
01616         } else if (c == QLatin1Char('>')) {
01617             ntext += QLatin1String("&gt;");
01618         } else if (c == QLatin1Char('\'')) {
01619             ntext += QLatin1String("&apos;");
01620         } else if (c == QLatin1Char('"')) {
01621             ntext += QLatin1String("&quot;");
01622         } else {
01623             ntext += c;
01624         }
01625     }
01626 
01627     return ntext;
01628 }

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
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
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