• 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     delete m_metaTranslit;
00489 }
00490 
00491 QString KuitSemanticsPrivate::metaTr (const char *ctxt, const char *id) const
00492 {
00493     if (m_metaCat == NULL) {
00494         return QString(id);
00495     }
00496     QString meta = m_metaCat->translate(ctxt, id);
00497     if (m_metaTranslit != NULL) {
00498         meta = m_metaTranslit->transliterate(meta, m_metaScript);
00499     }
00500     return meta;
00501 }
00502 
00503 void KuitSemanticsPrivate::setFormattingPatterns ()
00504 {
00505     using namespace Kuit;
00506 
00507     // Macro to expedite setting the patterns.
00508     #undef SET_PATTERN
00509     #define SET_PATTERN(tag, atts, fmt, ctxt_ptrn) do { \
00510         QSet<AttVar> aset; \
00511         aset << atts; \
00512         int akey = attSetKey(aset); \
00513         QString pattern = metaTr(ctxt_ptrn); \
00514         m_patterns[tag][akey][fmt] = pattern; \
00515         /* Make Term pattern same as Plain, unless explicitly given. */ \
00516         if (fmt == Fmt::Plain && !m_patterns[tag][akey].contains(Fmt::Term)) { \
00517             m_patterns[tag][akey][Fmt::Term] = pattern; \
00518         } \
00519     } while (0)
00520 
00521     // Normal I18N_NOOP2 removes context, but below we need both.
00522     #undef I18N_NOOP2
00523     #define I18N_NOOP2(ctxt, msg) ctxt, msg
00524 
00525     // Some of the formatting patterns are intentionally not exposed for
00526     // localization.
00527     #undef XXXX_NOOP2
00528     #define XXXX_NOOP2(ctxt, msg) ctxt, msg
00529 
00530     // NOTE: The following "i18n:" comments are oddly placed in order that
00531     // xgettext extracts them properly.
00532 
00533     // -------> Title
00534     SET_PATTERN(Tag::Title, Att::None, Fmt::Plain,
00535                 I18N_NOOP2("@title/plain",
00536     // i18n: The following messages, with msgctxt "@tag/modifier",
00537     // are KUIT patterns for formatting the text found inside semantic tags.
00538     // For review of the KUIT semantic markup, see the article on Techbase:
00539     // http://techbase.kde.org/Development/Tutorials/Localization/i18n_Semantics
00540     // The "/modifier" tells if the pattern is used for plain text, or rich text
00541     // which can use HTML tags.
00542     // You may be in general satisfied with the patterns as they are in the
00543     // original. Some things you may think about changing:
00544     // - the proper quotes, those used in msgid are English-standard
00545     // - the <i> and <b> tags, does your language script work well with them?
00546                            "== %1 =="));
00547     SET_PATTERN(Tag::Title, Att::None, Fmt::Rich,
00548                 I18N_NOOP2("@title/rich",
00549     // i18n: KUIT pattern, see the comment to the first of these entries above.
00550                            "<h2>%1</h2>"));
00551 
00552     // -------> Subtitle
00553     SET_PATTERN(Tag::Subtitle, Att::None, Fmt::Plain,
00554                 I18N_NOOP2("@subtitle/plain",
00555     // i18n: KUIT pattern, see the comment to the first of these entries above.
00556                            "~ %1 ~"));
00557     SET_PATTERN(Tag::Subtitle, Att::None, Fmt::Rich,
00558                 I18N_NOOP2("@subtitle/rich",
00559     // i18n: KUIT pattern, see the comment to the first of these entries above.
00560                            "<h3>%1</h3>"));
00561 
00562     // -------> Para
00563     SET_PATTERN(Tag::Para, Att::None, Fmt::Plain,
00564                 XXXX_NOOP2("@para/plain",
00565     // i18n: KUIT pattern, see the comment to the first of these entries above.
00566                            "%1"));
00567     SET_PATTERN(Tag::Para, Att::None, Fmt::Rich,
00568                 XXXX_NOOP2("@para/rich",
00569     // i18n: KUIT pattern, see the comment to the first of these entries above.
00570                            "<p>%1</p>"));
00571 
00572     // -------> List
00573     SET_PATTERN(Tag::List, Att::None, Fmt::Plain,
00574                 XXXX_NOOP2("@list/plain",
00575     // i18n: KUIT pattern, see the comment to the first of these entries above.
00576                            "%1"));
00577     SET_PATTERN(Tag::List, Att::None, Fmt::Rich,
00578                 XXXX_NOOP2("@list/rich",
00579     // i18n: KUIT pattern, see the comment to the first of these entries above.
00580                            "<ul>%1</ul>"));
00581 
00582     // -------> Item
00583     SET_PATTERN(Tag::Item, Att::None, Fmt::Plain,
00584                 I18N_NOOP2("@item/plain",
00585     // i18n: KUIT pattern, see the comment to the first of these entries above.
00586                            "  * %1"));
00587     SET_PATTERN(Tag::Item, Att::None, Fmt::Rich,
00588                 I18N_NOOP2("@item/rich",
00589     // i18n: KUIT pattern, see the comment to the first of these entries above.
00590                            "<li>%1</li>"));
00591 
00592     // -------> Note
00593     SET_PATTERN(Tag::Note, Att::None, Fmt::Plain,
00594                 I18N_NOOP2("@note/plain",
00595     // i18n: KUIT pattern, see the comment to the first of these entries above.
00596                            "Note: %1"));
00597     SET_PATTERN(Tag::Note, Att::None, Fmt::Rich,
00598                 I18N_NOOP2("@note/rich",
00599     // i18n: KUIT pattern, see the comment to the first of these entries above.
00600                            "<i>Note</i>: %1"));
00601     SET_PATTERN(Tag::Note, Att::Label, Fmt::Plain,
00602                 I18N_NOOP2("@note-with-label/plain\n"
00603                            "%1 is the note label, %2 is the text",
00604     // i18n: KUIT pattern, see the comment to the first of these entries above.
00605                            "%1: %2"));
00606     SET_PATTERN(Tag::Note, Att::Label, Fmt::Rich,
00607                 I18N_NOOP2("@note-with-label/rich\n"
00608                            "%1 is the note label, %2 is the text",
00609     // i18n: KUIT pattern, see the comment to the first of these entries above.
00610                            "<i>%1</i>: %2"));
00611 
00612     // -------> Warning
00613     SET_PATTERN(Tag::Warning, Att::None, Fmt::Plain,
00614                 I18N_NOOP2("@warning/plain",
00615     // i18n: KUIT pattern, see the comment to the first of these entries above.
00616                            "WARNING: %1"));
00617     SET_PATTERN(Tag::Warning, Att::None, Fmt::Rich,
00618                 I18N_NOOP2("@warning/rich",
00619     // i18n: KUIT pattern, see the comment to the first of these entries above.
00620                            "<b>Warning</b>: %1"));
00621     SET_PATTERN(Tag::Warning, Att::Label, Fmt::Plain,
00622                 I18N_NOOP2("@warning-with-label/plain\n"
00623                            "%1 is the warning label, %2 is the text",
00624     // i18n: KUIT pattern, see the comment to the first of these entries above.
00625                            "%1: %2"));
00626     SET_PATTERN(Tag::Warning, Att::Label, Fmt::Rich,
00627                 I18N_NOOP2("@warning-with-label/rich\n"
00628                            "%1 is the warning label, %2 is the text",
00629     // i18n: KUIT pattern, see the comment to the first of these entries above.
00630                            "<b>%1</b>: %2"));
00631 
00632     // -------> Link
00633     SET_PATTERN(Tag::Link, Att::None, Fmt::Plain,
00634                 XXXX_NOOP2("@link/plain",
00635     // i18n: KUIT pattern, see the comment to the first of these entries above.
00636                            "%1"));
00637     SET_PATTERN(Tag::Link, Att::None, Fmt::Rich,
00638                 XXXX_NOOP2("@link/rich",
00639     // i18n: KUIT pattern, see the comment to the first of these entries above.
00640                            "<a href=\"%1\">%1</a>"));
00641     SET_PATTERN(Tag::Link, Att::Url, Fmt::Plain,
00642                 I18N_NOOP2("@link-with-description/plain\n"
00643                            "%1 is the URL, %2 is the descriptive text",
00644     // i18n: KUIT pattern, see the comment to the first of these entries above.
00645                            "%2 (%1)"));
00646     SET_PATTERN(Tag::Link, Att::Url, Fmt::Rich,
00647                 I18N_NOOP2("@link-with-description/rich\n"
00648                            "%1 is the URL, %2 is the descriptive text",
00649     // i18n: KUIT pattern, see the comment to the first of these entries above.
00650                            "<a href=\"%1\">%2</a>"));
00651 
00652     // -------> Filename
00653     SET_PATTERN(Tag::Filename, Att::None, Fmt::Plain,
00654                 I18N_NOOP2("@filename/plain",
00655     // i18n: KUIT pattern, see the comment to the first of these entries above.
00656                            "‘%1’"));
00657     SET_PATTERN(Tag::Filename, Att::None, Fmt::Rich,
00658                 I18N_NOOP2("@filename/rich",
00659     // i18n: KUIT pattern, see the comment to the first of these entries above.
00660                            "<tt>%1</tt>"));
00661 
00662     // -------> Application
00663     SET_PATTERN(Tag::Application, Att::None, Fmt::Plain,
00664                 I18N_NOOP2("@application/plain",
00665     // i18n: KUIT pattern, see the comment to the first of these entries above.
00666                            "%1"));
00667     SET_PATTERN(Tag::Application, Att::None, Fmt::Rich,
00668                 I18N_NOOP2("@application/rich",
00669     // i18n: KUIT pattern, see the comment to the first of these entries above.
00670                            "%1"));
00671 
00672     // -------> Command
00673     SET_PATTERN(Tag::Command, Att::None, Fmt::Plain,
00674                 I18N_NOOP2("@command/plain",
00675     // i18n: KUIT pattern, see the comment to the first of these entries above.
00676                            "%1"));
00677     SET_PATTERN(Tag::Command, Att::None, Fmt::Rich,
00678                 I18N_NOOP2("@command/rich",
00679     // i18n: KUIT pattern, see the comment to the first of these entries above.
00680                            "<tt>%1</tt>"));
00681     SET_PATTERN(Tag::Command, Att::Section, Fmt::Plain,
00682                 I18N_NOOP2("@command-with-section/plain\n"
00683                            "%1 is the command name, %2 is its man section",
00684     // i18n: KUIT pattern, see the comment to the first of these entries above.
00685                            "%1(%2)"));
00686     SET_PATTERN(Tag::Command, Att::Section, Fmt::Rich,
00687                 I18N_NOOP2("@command-with-section/rich\n"
00688                            "%1 is the command name, %2 is its man section",
00689     // i18n: KUIT pattern, see the comment to the first of these entries above.
00690                            "<tt>%1(%2)</tt>"));
00691 
00692     // -------> Resource
00693     SET_PATTERN(Tag::Resource, Att::None, Fmt::Plain,
00694                 I18N_NOOP2("@resource/plain",
00695     // i18n: KUIT pattern, see the comment to the first of these entries above.
00696                            "“%1”"));
00697     SET_PATTERN(Tag::Resource, Att::None, Fmt::Rich,
00698                 I18N_NOOP2("@resource/rich",
00699     // i18n: KUIT pattern, see the comment to the first of these entries above.
00700                            "“%1”"));
00701 
00702     // -------> Icode
00703     SET_PATTERN(Tag::Icode, Att::None, Fmt::Plain,
00704                 I18N_NOOP2("@icode/plain",
00705     // i18n: KUIT pattern, see the comment to the first of these entries above.
00706                            "“%1”"));
00707     SET_PATTERN(Tag::Icode, Att::None, Fmt::Rich,
00708                 I18N_NOOP2("@icode/rich",
00709     // i18n: KUIT pattern, see the comment to the first of these entries above.
00710                            "<tt>%1</tt>"));
00711 
00712     // -------> Bcode
00713     SET_PATTERN(Tag::Bcode, Att::None, Fmt::Plain,
00714                 XXXX_NOOP2("@bcode/plain",
00715     // i18n: KUIT pattern, see the comment to the first of these entries above.
00716                            "\n%1\n"));
00717     SET_PATTERN(Tag::Bcode, Att::None, Fmt::Rich,
00718                 XXXX_NOOP2("@bcode/rich",
00719     // i18n: KUIT pattern, see the comment to the first of these entries above.
00720                            "<pre>%1</pre>"));
00721 
00722     // -------> Shortcut
00723     SET_PATTERN(Tag::Shortcut, Att::None, Fmt::Plain,
00724                 I18N_NOOP2("@shortcut/plain",
00725     // i18n: KUIT pattern, see the comment to the first of these entries above.
00726                            "%1"));
00727     SET_PATTERN(Tag::Shortcut, Att::None, Fmt::Rich,
00728                 I18N_NOOP2("@shortcut/rich",
00729     // i18n: KUIT pattern, see the comment to the first of these entries above.
00730                            "<b>%1</b>"));
00731 
00732     // -------> Interface
00733     SET_PATTERN(Tag::Interface, Att::None, Fmt::Plain,
00734                 I18N_NOOP2("@interface/plain",
00735     // i18n: KUIT pattern, see the comment to the first of these entries above.
00736                            "|%1|"));
00737     SET_PATTERN(Tag::Interface, Att::None, Fmt::Rich,
00738                 I18N_NOOP2("@interface/rich",
00739     // i18n: KUIT pattern, see the comment to the first of these entries above.
00740                            "<i>%1</i>"));
00741 
00742     // -------> Emphasis
00743     SET_PATTERN(Tag::Emphasis, Att::None, Fmt::Plain,
00744                 I18N_NOOP2("@emphasis/plain",
00745     // i18n: KUIT pattern, see the comment to the first of these entries above.
00746                            "*%1*"));
00747     SET_PATTERN(Tag::Emphasis, Att::None, Fmt::Rich,
00748                 I18N_NOOP2("@emphasis/rich",
00749     // i18n: KUIT pattern, see the comment to the first of these entries above.
00750                            "<i>%1</i>"));
00751 
00752     // -------> Placeholder
00753     SET_PATTERN(Tag::Placeholder, Att::None, Fmt::Plain,
00754                 I18N_NOOP2("@placeholder/plain",
00755     // i18n: KUIT pattern, see the comment to the first of these entries above.
00756                            "&lt;%1&gt;"));
00757     SET_PATTERN(Tag::Placeholder, Att::None, Fmt::Rich,
00758                 I18N_NOOP2("@placeholder/rich",
00759     // i18n: KUIT pattern, see the comment to the first of these entries above.
00760                            "&lt;<i>%1</i>&gt;"));
00761 
00762     // -------> Email
00763     SET_PATTERN(Tag::Email, Att::None, Fmt::Plain,
00764                 I18N_NOOP2("@email/plain",
00765     // i18n: KUIT pattern, see the comment to the first of these entries above.
00766                            "&lt;%1&gt;"));
00767     SET_PATTERN(Tag::Email, Att::None, Fmt::Rich,
00768                 I18N_NOOP2("@email/rich",
00769     // i18n: KUIT pattern, see the comment to the first of these entries above.
00770                            "&lt;<a href=\"mailto:%1\">%1</a>&gt;"));
00771     SET_PATTERN(Tag::Email, Att::Address, Fmt::Plain,
00772                 I18N_NOOP2("@email-with-name/plain\n"
00773                            "%1 is name, %2 is address",
00774     // i18n: KUIT pattern, see the comment to the first of these entries above.
00775                            "%1 &lt;%2&gt;"));
00776     SET_PATTERN(Tag::Email, Att::Address, Fmt::Rich,
00777                 I18N_NOOP2("@email-with-name/rich\n"
00778                            "%1 is name, %2 is address",
00779     // i18n: KUIT pattern, see the comment to the first of these entries above.
00780                            "<a href=\"mailto:%2\">%1</a>"));
00781 
00782     // -------> Envar
00783     SET_PATTERN(Tag::Envar, Att::None, Fmt::Plain,
00784                 I18N_NOOP2("@envar/plain",
00785     // i18n: KUIT pattern, see the comment to the first of these entries above.
00786                            "$%1"));
00787     SET_PATTERN(Tag::Envar, Att::None, Fmt::Rich,
00788                 I18N_NOOP2("@envar/rich",
00789     // i18n: KUIT pattern, see the comment to the first of these entries above.
00790                            "<tt>$%1</tt>"));
00791 
00792     // -------> Message
00793     SET_PATTERN(Tag::Message, Att::None, Fmt::Plain,
00794                 I18N_NOOP2("@message/plain",
00795     // i18n: KUIT pattern, see the comment to the first of these entries above.
00796                            "/%1/"));
00797     SET_PATTERN(Tag::Message, Att::None, Fmt::Rich,
00798                 I18N_NOOP2("@message/rich",
00799     // i18n: KUIT pattern, see the comment to the first of these entries above.
00800                            "<i>%1</i>"));
00801 
00802     // -------> Nl
00803     SET_PATTERN(Tag::Nl, Att::None, Fmt::Plain,
00804                 XXXX_NOOP2("@nl/plain",
00805     // i18n: KUIT pattern, see the comment to the first of these entries above.
00806                            "%1\n"));
00807     SET_PATTERN(Tag::Nl, Att::None, Fmt::Rich,
00808                 XXXX_NOOP2("@nl/rich",
00809     // i18n: KUIT pattern, see the comment to the first of these entries above.
00810                            "%1<br/>"));
00811 }
00812 
00813 void KuitSemanticsPrivate::setTextTransformData ()
00814 {
00815     KuitSemanticsStaticData *s = staticData;
00816 
00817     m_numfmtInt = Kuit::Numfmt::Posix;
00818     // i18n: Decide how integer-valued amounts will be formatted in your
00819     // language. Currently available number formats are:
00820     //   posix   - decimal point
00821     //   us      - thousands separation by comma, decimal point
00822     //   euro    - thousands separation by point, decimal comma
00823     //   euro2   - thousands separation by space, decimal comma
00824     //   euro2ct - as euro2, except thousand not separated when <10000
00825     //   system  - by locale settings (i.e. override language ortography)
00826     // If none of the existing formats is appropriate for your language,
00827     // write to kde-i18n-doc@kde.org to arrange for a new format.
00828     QString fmtnameInt = metaTr("number-format:integer", "us").toLower();
00829     if (s->knownNumfmts.contains(fmtnameInt)) {
00830         m_numfmtInt = s->knownNumfmts[fmtnameInt];
00831     }
00832     else {
00833         kDebug(173) << QString("Format of integer numbers '%1', selected in "
00834                                "kdelibs4.po, is not valid; using POSIX format.")
00835                               .arg(fmtnameInt);
00836     }
00837 
00838     m_numfmtReal = Kuit::Numfmt::Posix;
00839     // i18n: Decide how real-valued amounts will be formatted in your
00840     // language. See the comment to previous entry.
00841     QString fmtnameReal = metaTr("number-format:real", "us").toLower();
00842     if (s->knownNumfmts.contains(fmtnameReal)) {
00843         m_numfmtReal = s->knownNumfmts[fmtnameReal];
00844     }
00845     else {
00846         kDebug(173) << QString("Format of real numbers '%1', selected in "
00847                                "kdelibs4.po, is not valid; using POSIX format.")
00848                               .arg(fmtnameReal);
00849     }
00850 
00851     // If adherence to locale settings requested, set it globally.
00852     if (s->knownNumfmts[fmtnameInt] == Kuit::Numfmt::System) {
00853         s->numfmtInt = Kuit::Numfmt::System;
00854     }
00855     if (s->knownNumfmts[fmtnameReal] == Kuit::Numfmt::System) {
00856         s->numfmtReal = Kuit::Numfmt::System;
00857     }
00858 
00859     // i18n: Decide which string is used to delimit keys in a keyboard
00860     // shortcut (e.g. + in Ctrl+Alt+Tab) in plain text.
00861     m_comboKeyDelim[Kuit::Fmt::Plain] = metaTr("shortcut-key-delimiter/plain", "+");
00862     m_comboKeyDelim[Kuit::Fmt::Term] = m_comboKeyDelim[Kuit::Fmt::Plain];
00863     // i18n: Decide which string is used to delimit keys in a keyboard
00864     // shortcut (e.g. + in Ctrl+Alt+Tab) in rich text.
00865     m_comboKeyDelim[Kuit::Fmt::Rich] = metaTr("shortcut-key-delimiter/rich", "+");
00866 
00867     // i18n: Decide which string is used to delimit elements in a GUI path
00868     // (e.g. -> in "Go to Settings->Advanced->Core tab.") in plain text.
00869     m_guiPathDelim[Kuit::Fmt::Plain] = metaTr("gui-path-delimiter/plain", "→");
00870     m_guiPathDelim[Kuit::Fmt::Term] = m_guiPathDelim[Kuit::Fmt::Plain];
00871     // i18n: Decide which string is used to delimit elements in a GUI path
00872     // (e.g. -> in "Go to Settings->Advanced->Core tab.") in rich text.
00873     m_guiPathDelim[Kuit::Fmt::Rich] = metaTr("gui-path-delimiter/rich", "→");
00874     // NOTE: The '→' glyph seems to be available in all widespread fonts.
00875 
00876     // Collect keyboard key names.
00877     #undef SET_KEYNAME
00878     #define SET_KEYNAME(rawname) do { \
00879         /* Normalize key, trim and all lower-case. */ \
00880         QString normname = QString(rawname).trimmed().toLower(); \
00881         m_keyNames[normname] = metaTr("keyboard-key-name", rawname); \
00882     } while (0)
00883 
00884     // Now we need I18N_NOOP2 that does remove context.
00885     #undef I18N_NOOP2
00886     #define I18N_NOOP2(ctxt, msg) msg
00887 
00888     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Alt"));
00889     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "AltGr"));
00890     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Backspace"));
00891     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "CapsLock"));
00892     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Control"));
00893     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Ctrl"));
00894     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Del"));
00895     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Delete"));
00896     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Down"));
00897     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "End"));
00898     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Enter"));
00899     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Esc"));
00900     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Escape"));
00901     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Home"));
00902     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Hyper"));
00903     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Ins"));
00904     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Insert"));
00905     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Left"));
00906     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Menu"));
00907     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Meta"));
00908     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "NumLock"));
00909     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "PageDown"));
00910     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "PageUp"));
00911     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "PgDown"));
00912     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "PgUp"));
00913     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "PauseBreak"));
00914     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "PrintScreen"));
00915     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "PrtScr"));
00916     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Return"));
00917     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Right"));
00918     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "ScrollLock"));
00919     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Shift"));
00920     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Space"));
00921     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Super"));
00922     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "SysReq"));
00923     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Tab"));
00924     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Up"));
00925     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "Win"));
00926     // TODO: Add rest of the key names?
00927 
00928     // i18n: Pattern for the function keys.
00929     SET_KEYNAME(I18N_NOOP2("keyboard-key-name", "F%1"));
00930 }
00931 
00932 QString KuitSemanticsPrivate::format (const QString &text,
00933                                       const QString &ctxt) const
00934 {
00935     // Parse context marker to determine format.
00936     Kuit::FmtVar fmtExplicit = formatFromContextMarker(ctxt, text);
00937 
00938     // Quick check: are there any tags at all?
00939     if (text.indexOf('<') < 0) {
00940         return finalizeVisualText(text, fmtExplicit);
00941     }
00942 
00943     // If format not explicitly given, heuristically determine
00944     // implicit format based on presence or lack of HTML tags.
00945     Kuit::FmtVar fmtImplicit = fmtExplicit;
00946     if (fmtExplicit == Kuit::Fmt::None) {
00947         fmtImplicit = formatFromTags(text);
00948     }
00949 
00950     // Decide on the top tag, either TopLong or TopShort,
00951     // and wrap the text with it.
00952     Kuit::TagVar toptag;
00953     QString wtext = equipTopTag(text, toptag);
00954 
00955     // Format the text.
00956     QString ftext = semanticToVisualText(wtext, fmtExplicit, fmtImplicit);
00957     if (ftext.isEmpty()) { // error while processing markup
00958         return salvageMarkup(text, fmtImplicit);
00959     }
00960 
00961     return ftext;
00962 }
00963 
00964 int KuitSemanticsPrivate::attSetKey (const QSet<Kuit::AttVar> &aset)
00965 {
00966     QList<Kuit::AttVar> alist = aset.toList();
00967     qSort(alist);
00968     int key = 0;
00969     int tenp = 1;
00970     foreach (const Kuit::AttVar &att, alist) {
00971         key += att * tenp;
00972         tenp *= 10;
00973     }
00974     return key;
00975 }
00976 
00977 Kuit::FmtVar KuitSemanticsPrivate::formatFromContextMarker (
00978     const QString &ctxmark_, const QString &text)
00979 {
00980     #ifdef NDEBUG
00981     Q_UNUSED(text);
00982     #endif
00983 
00984     KuitSemanticsStaticData *s = staticData;
00985 
00986     // Semantic context marker is in the form @rolname:cuename/fmtname,
00987     // and must start just after any leading whitespace in the context string.
00988     QString rolname;
00989     QString fmtname;
00990     QString cuename;
00991     QString ctxmark = ctxmark_.trimmed();
00992     if (ctxmark.startsWith('@')) { // found context marker
00993         static QRegExp wsRx("\\s");
00994         ctxmark = ctxmark.mid(1, wsRx.indexIn(ctxmark) - 1);
00995 
00996         // Possible visual format.
00997         int pfmt = ctxmark.indexOf('/');
00998         if (pfmt >= 0) {
00999             fmtname = ctxmark.mid(pfmt + 1);
01000             ctxmark = ctxmark.left(pfmt);
01001         }
01002 
01003         // Possible interface subcue.
01004         int pcue = ctxmark.indexOf(':'); 
01005         if (pcue >= 0) {
01006             cuename = ctxmark.mid(pcue + 1);
01007             ctxmark = ctxmark.left(pcue);
01008         }
01009 
01010         // Semantic role.
01011         rolname = ctxmark;
01012     }
01013     // Names remain empty if marker was not found, which is ok.
01014 
01015     // Normalize names.
01016     rolname = rolname.trimmed().toLower();
01017     cuename = cuename.trimmed().toLower();
01018     fmtname = fmtname.trimmed().toLower();
01019 
01020     // Set role from name.
01021     Kuit::RolVar rol;
01022     if (s->knownRols.contains(rolname)) { // known role
01023         rol = s->knownRols[rolname];
01024     }
01025     else { // unknown role
01026         rol = Kuit::Rol::None;
01027         if (!rolname.isEmpty()) {
01028             kDebug(173) << QString("Unknown semantic role '@%1' in "
01029                                    "context marker for message {%2}.")
01030                                   .arg(rolname, shorten(text));
01031         }
01032     }
01033 
01034     // Set subcue from name.
01035     Kuit::CueVar cue;
01036     if (s->knownCues.contains(cuename)) { // known subcue
01037         cue = s->knownCues[cuename];
01038     }
01039     else { // unknown or not given subcue
01040         cue = Kuit::Cue::None;
01041         if (!cuename.isEmpty()) {
01042             kDebug(173) << QString("Unknown interface subcue ':%1' in "
01043                                    "context marker for message {%2}.")
01044                                   .arg(cuename, shorten(text));
01045         }
01046     }
01047 
01048     // Set format from name, or by derivation from contex/subcue.
01049     Kuit::FmtVar fmt;
01050     if (s->knownFmts.contains(fmtname)) { // known format
01051         fmt = s->knownFmts[fmtname];
01052     }
01053     else { // unknown or not given format
01054 
01055         // Check first if there is a format defined for role/subcue
01056         // combination, than for role only, then default to none.
01057         if (s->defFmts.contains(rol)) {
01058             if (s->defFmts[rol].contains(cue)) {
01059                 fmt = s->defFmts[rol][cue];
01060             }
01061             else {
01062                 fmt = s->defFmts[rol][Kuit::Cue::None];
01063             }
01064         }
01065         else {
01066             fmt = Kuit::Fmt::None;
01067         }
01068 
01069         if (!fmtname.isEmpty()) {
01070             kDebug(173) << QString("Unknown visual format '/%1' in "
01071                                    "context marker for message {%2}.")
01072                                   .arg(fmtname, shorten(text));
01073         }
01074     }
01075 
01076     return fmt;
01077 }
01078 
01079 Kuit::FmtVar KuitSemanticsPrivate::formatFromTags (const QString &text)
01080 {
01081     KuitSemanticsStaticData *s = staticData;
01082     static QRegExp staticTagRx("<\\s*(\\w+)[^>]*>");
01083 
01084     QRegExp tagRx = staticTagRx; // for thread-safety
01085     int p = tagRx.indexIn(text);
01086     while (p >= 0) {
01087         QString tagname = tagRx.capturedTexts().at(1).toLower();
01088         if (s->qtHtmlTagNames.contains(tagname)) {
01089             return Kuit::Fmt::Rich;
01090         }
01091         p = tagRx.indexIn(text, p + tagRx.matchedLength());
01092     }
01093     return Kuit::Fmt::Plain;
01094 }
01095 
01096 QString KuitSemanticsPrivate::equipTopTag (const QString &text_,
01097                                            Kuit::TagVar &toptag)
01098 {
01099     KuitSemanticsStaticData *s = staticData;
01100 
01101     // Unless the text opens either with TopLong or TopShort tags,
01102     // make a guess: if it opens with one of Title, Subtitle, Para,
01103     // consider it TopLong, otherwise TopShort.
01104     static QRegExp opensWithTagRx("^\\s*<\\s*(\\w+)[^>]*>");
01105     bool explicitTopTag = false;
01106 
01107     QString text = text_;
01108     int p = opensWithTagRx.indexIn(text);
01109 
01110     // <qt> or <html> tag are to be ignored for deciding the top tag.
01111     if (p >= 0) {
01112         QString fullmatch = opensWithTagRx.capturedTexts().at(0);
01113         QString tagname = opensWithTagRx.capturedTexts().at(1).toLower();
01114         if (tagname == "qt" || tagname == "html") {
01115             // Kill the tag and see if there is another one following,
01116             // for primary check below.
01117             text = text.mid(fullmatch.length());
01118             p = opensWithTagRx.indexIn(text);
01119         }
01120     }
01121 
01122     // Check the first non-<qt>/<html> tag.
01123     if (p >= 0) { // opens with a tag
01124         QString tagname = opensWithTagRx.capturedTexts().at(1).toLower();
01125         if (s->knownTags.contains(tagname)) { // a known tag
01126             Kuit::TagVar tag = s->knownTags[tagname];
01127             if (   tag == Kuit::Tag::TopLong
01128                 || tag == Kuit::Tag::TopShort) { // explicitly given top tag
01129                 toptag = tag;
01130                 explicitTopTag = true;
01131             }
01132             else if (   tag == Kuit::Tag::Para
01133                      || tag == Kuit::Tag::Title
01134                      || tag == Kuit::Tag::Subtitle) { // one of long text tags
01135                 toptag = Kuit::Tag::TopLong;
01136             }
01137             else { // not one of long text tags
01138                 toptag = Kuit::Tag::TopShort;
01139             }
01140         }
01141         else { // not a KUIT tag
01142             toptag = Kuit::Tag::TopShort;
01143         }
01144     }
01145     else { // doesn't open with a tag
01146         toptag = Kuit::Tag::TopShort;
01147     }
01148 
01149     // Wrap text with top tag if not explicitly given.
01150     if (!explicitTopTag) {
01151         return   '<' + s->tagNames[toptag] + '>'
01152                + text_ // original text, not the one possibly stripped above
01153                + "</" + s->tagNames[toptag] + '>';
01154     }
01155     else {
01156         return text;
01157     }
01158 }
01159 
01160 QString KuitSemanticsPrivate::semanticToVisualText (const QString &text_,
01161                                                     Kuit::FmtVar fmtExp_,
01162                                                     Kuit::FmtVar fmtImp_) const
01163 {
01164     KuitSemanticsStaticData *s = staticData;
01165 
01166     // Replace &-shortcut marker with "&amp;", not to confuse the parser;
01167     // but do not touch & in "&[a-z]+;", which is an XML entity as it is.
01168     QString original = text_;
01169     QString text;
01170     int p = original.indexOf('&');
01171     while (p >= 0) {
01172         text.append(original.mid(0, p + 1));
01173         original.remove(0, p + 1);
01174         static QRegExp restRx("^(?:[a-z]+|#[0-9]+);");
01175         if (original.indexOf(restRx) != 0) { // not an entity
01176             text.append("amp;");
01177         }
01178         p = original.indexOf('&');
01179     }
01180     text.append(original);
01181 
01182     Kuit::FmtVar fmtExp = fmtExp_;
01183     Kuit::FmtVar fmtImp = fmtImp_;
01184     int numCtx = 0;
01185     bool hadQtTag = false;
01186     bool hadAnyHtmlTag = false;
01187     QStack<OpenEl> openEls;
01188     QXmlStreamReader xml(text);
01189 
01190     while (!xml.atEnd()) {
01191         xml.readNext();
01192 
01193         if (xml.isStartElement()) {
01194             // Find first proper enclosing element tag.
01195             Kuit::TagVar etag = Kuit::Tag::None;
01196             for (int i = openEls.size() - 1; i >= 0; --i) {
01197                 if (openEls[i].handling == OpenEl::Proper) {
01198                     etag = openEls[i].tag;
01199                     break;
01200                 }
01201             }
01202 
01203             // Collect data about this element.
01204             OpenEl oel = parseOpenEl(xml, etag, text);
01205             if (oel.name == "qt" || oel.name == "html") {
01206                 hadQtTag = true;
01207             }
01208             if (s->qtHtmlTagNames.contains(oel.name)) {
01209                 hadAnyHtmlTag = true;
01210             }
01211 
01212             // If this is top tag, check if it overrides the context marker
01213             // by its ctx attribute.
01214             if (openEls.isEmpty() && oel.avals.contains(Kuit::Att::Ctx)) {
01215                 // Resolve format override.
01216                 fmtExp = formatFromContextMarker(oel.avals[Kuit::Att::Ctx], text);
01217                 fmtImp = fmtExp;
01218             }
01219 
01220             // Record the new element on the parse stack.
01221             openEls.push(oel);
01222 
01223             // Update numeric context.
01224             if (oel.tag == Kuit::Tag::Numid) {
01225                 ++numCtx;
01226             }
01227         }
01228         else if (xml.isEndElement()) {
01229             // Get closed element data.
01230             OpenEl oel = openEls.pop();
01231 
01232             // If this was closing of the top element, we're done.
01233             if (openEls.isEmpty()) {
01234                 // Return with final touches applied.
01235                 return finalizeVisualText(oel.formattedText, fmtExp,
01236                                           hadQtTag, hadAnyHtmlTag);
01237             }
01238 
01239             // Append formatted text segment.
01240             QString pt = openEls.top().formattedText; // preceding text
01241             openEls.top().formattedText += formatSubText(pt, oel, fmtImp, numCtx);
01242 
01243             // Update numeric context.
01244             if (oel.tag == Kuit::Tag::Numid) {
01245                 --numCtx;
01246             }
01247         }
01248         else if (xml.isCharacters()) {
01249             // Stream reader will automatically reslove entities, which is
01250             // not desired in this case, as the final text may be rich.
01251             // The text element will be broken at the resolved entity, so
01252             // the first character may be one of those resolved.
01253             // Convert it back into an entity.
01254             QString text = xml.text().toString();
01255             QString firstChar = text.left(1);
01256             if (s->xmlEntitiesInverse.contains(firstChar)) {
01257                 QString entname = s->xmlEntitiesInverse[firstChar];
01258                 text = '&' + entname + ';' + text.mid(1);
01259             }
01260             openEls.top().formattedText += text;
01261         }
01262     }
01263 
01264     if (xml.hasError()) {
01265         kDebug(173) << QString("Markup error in message {%1}: %2")
01266                               .arg(shorten(text), xml.errorString());
01267         return QString();
01268     }
01269 
01270     // Cannot reach here.
01271     return text;
01272 }
01273 
01274 KuitSemanticsPrivate::OpenEl
01275 KuitSemanticsPrivate::parseOpenEl (const QXmlStreamReader &xml,
01276                                    Kuit::TagVar etag,
01277                                    const QString &text) const
01278 {
01279     #ifdef NDEBUG
01280     Q_UNUSED(text);
01281     #endif
01282 
01283     KuitSemanticsStaticData *s = staticData;
01284 
01285     OpenEl oel;
01286     oel.name = xml.name().toString().toLower();
01287 
01288     // Collect attribute names and values, and format attribute string.
01289     QStringList attnams, attvals;
01290     foreach (const QXmlStreamAttribute &xatt, xml.attributes()) {
01291         attnams += xatt.name().toString().toLower();
01292         attvals += xatt.value().toString();
01293         QChar qc = attvals.last().indexOf('\'') < 0 ? '\'' : '"';
01294         oel.astr += ' ' + attnams.last() + '=' + qc + attvals.last() + qc;
01295     }
01296 
01297     if (s->knownTags.contains(oel.name)) { // known KUIT element
01298         oel.tag = s->knownTags[oel.name];
01299 
01300         // If this element can be contained within enclosing element,
01301         // mark it proper, otherwise mark it for removal.
01302         if (etag == Kuit::Tag::None || s->tagSubs[etag].contains(oel.tag)) {
01303             oel.handling = OpenEl::Proper;
01304         }
01305         else {
01306             oel.handling = OpenEl::Dropout;
01307             kDebug(173) << QString("Tag '%1' cannot be subtag of '%2' "
01308                                    "in message {%3}.")
01309                                   .arg(s->tagNames[oel.tag], s->tagNames[etag],
01310                                        shorten(text));
01311         }
01312 
01313         // Resolve attributes and compute attribute set key.
01314         QSet<Kuit::AttVar> attset;
01315         for (int i = 0; i < attnams.size(); ++i) {
01316             if (s->knownAtts.contains(attnams[i])) {
01317                 Kuit::AttVar att = s->knownAtts[attnams[i]];
01318                 if (s->tagAtts[oel.tag].contains(att)) {
01319                     attset << att;
01320                     oel.avals[att] = attvals[i];
01321                 }
01322                 else {
01323                     kDebug(173) << QString("Attribute '%1' cannot be used in "
01324                                            "tag '%2' in message {%3}.")
01325                                           .arg(attnams[i], oel.name,
01326                                                shorten(text));
01327                 }
01328             }
01329             else {
01330                 kDebug(173) << QString("Unknown semantic tag attribute '%1' "
01331                                        "in message {%2}.")
01332                                       .arg(attnams[i], shorten(text));
01333             }
01334         }
01335         oel.akey = attSetKey(attset);
01336     }
01337     else if (oel.name == "qt" || oel.name == "html") {
01338         // Drop qt/html tags (gets added in the end).
01339         oel.handling = OpenEl::Dropout;
01340     }
01341     else { // other element, leave it in verbatim
01342         oel.handling = OpenEl::Ignored;
01343         if (!s->qtHtmlTagNames.contains(oel.name)) {
01344             kDebug(173) << QString("Tag '%1' is neither semantic nor HTML in "
01345                                    "message {%3}.")
01346                                   .arg(oel.name, shorten(text));
01347         }
01348     }
01349 
01350     return oel;
01351 }
01352 
01353 QString KuitSemanticsPrivate::visualPattern (Kuit::TagVar tag, int akey,
01354                                              Kuit::FmtVar fmt) const
01355 {
01356     // Default pattern: simple substitution.
01357     QString pattern("%1");
01358 
01359     // See if there is a pattern specifically for this element.
01360     if (   m_patterns.contains(tag)
01361         && m_patterns[tag].contains(akey)
01362         && m_patterns[tag][akey].contains(fmt))
01363     {
01364         pattern = m_patterns[tag][akey][fmt];
01365     }
01366 
01367     return pattern;
01368 }
01369 
01370 QString KuitSemanticsPrivate::formatSubText (const QString &ptext,
01371                                              const OpenEl &oel,
01372                                              Kuit::FmtVar fmt,
01373                                              int numctx) const
01374 {
01375     KuitSemanticsStaticData *s = staticData;
01376 
01377     if (oel.handling == OpenEl::Proper) {
01378         // Select formatting pattern.
01379         QString pattern = visualPattern(oel.tag, oel.akey, fmt);
01380 
01381         // Some tags modify their text.
01382         QString mtext = modifyTagText(oel.tag, oel.formattedText, numctx, fmt);
01383 
01384         using namespace Kuit;
01385 
01386         // Format text according to pattern.
01387         QString ftext;
01388          if (oel.tag == Tag::Link && oel.avals.contains(Att::Url)) {
01389             ftext = pattern.arg(oel.avals[Att::Url], mtext);
01390         }
01391         else if (oel.tag == Tag::Command && oel.avals.contains(Att::Section)) {
01392             ftext = pattern.arg(mtext, oel.avals[Att::Section]);
01393         }
01394         else if (oel.tag == Tag::Email && oel.avals.contains(Att::Address)) {
01395             ftext = pattern.arg(mtext, oel.avals[Att::Address]);
01396         }
01397         else if (oel.tag == Tag::Note && oel.avals.contains(Att::Label)) {
01398             ftext = pattern.arg(oel.avals[Att::Label], mtext);
01399         }
01400         else if (oel.tag == Tag::Warning && oel.avals.contains(Att::Label)) {
01401             ftext = pattern.arg(oel.avals[Att::Label], mtext);
01402         }
01403         else {
01404             ftext = pattern.arg(mtext);
01405         }
01406 
01407         // Handle leading newlines, if this is not start of the text
01408         // (ptext is the preceding text).
01409         if (!ptext.isEmpty() && s->leadingNewlines.contains(oel.tag)) {
01410             // Count number of present newlines.
01411             int pnumle, pnumtr, fnumle, fnumtr;
01412             countWrappingNewlines(ptext, pnumle, pnumtr);
01413             countWrappingNewlines(ftext, fnumle, fnumtr);
01414             // Number of leading newlines already present.
01415             int numle = pnumtr + fnumle;
01416             // The required extra newlines.
01417             QString strle;
01418             if (numle < s->leadingNewlines[oel.tag]) {
01419                 strle = QString(s->leadingNewlines[oel.tag] - numle, '\n');
01420             }
01421             ftext = strle + ftext;
01422         }
01423 
01424         return ftext;
01425     }
01426     else if (oel.handling == OpenEl::Ignored) {
01427         if (oel.name == "br" || oel.name == "hr") {
01428             // Close these tags in-place (just for looks).
01429             return '<' + oel.name + "/>";
01430         }
01431         else {
01432             return   '<' + oel.name + oel.astr + '>'
01433                    + oel.formattedText
01434                    + "</" + oel.name + '>';
01435         }
01436     }
01437     else { // oel.handling == OpenEl::Dropout
01438         return oel.formattedText;
01439     }
01440 }
01441 
01442 void KuitSemanticsPrivate::countWrappingNewlines (const QString &text,
01443                                                   int &numle, int &numtr)
01444 {
01445     int len = text.length();
01446     // Number of newlines at start of text.
01447     numle = 0;
01448     while (numle < len && text[numle] == '\n') {
01449         ++numle;
01450     }
01451     // Number of newlines at end of text.
01452     numtr = 0;
01453     while (numtr < len && text[len - numtr - 1] == '\n') {
01454         ++numtr;
01455     }
01456 }
01457 
01458 QString KuitSemanticsPrivate::modifyTagText (Kuit::TagVar tag,
01459                                              const QString &text,
01460                                              int numctx,
01461                                              Kuit::FmtVar fmt) const
01462 {
01463     KuitSemanticsStaticData *s = staticData;
01464 
01465     // numctx < 1 means that the number is not in numeric-id context.
01466     if (   (tag == Kuit::Tag::NumIntg || tag == Kuit::Tag::NumReal) \
01467         && numctx < 1)
01468     {
01469         int numfmt;
01470         if (tag == Kuit::Tag::NumIntg) {
01471             numfmt = s->numfmtInt >= 0 ? s->numfmtInt : m_numfmtInt;
01472         } else {
01473             numfmt = s->numfmtReal >= 0 ? s->numfmtReal : m_numfmtReal;
01474         }
01475         switch (numfmt) {
01476         case Kuit::Numfmt::System:
01477             return KuitFormats::toNumberSystem(text);
01478         case Kuit::Numfmt::US:
01479             return KuitFormats::toNumberUS(text);
01480         case Kuit::Numfmt::Euro:
01481             return KuitFormats::toNumberEuro(text);
01482         case Kuit::Numfmt::Euro2:
01483             return KuitFormats::toNumberEuro2(text);
01484         case Kuit::Numfmt::Euro2ct:
01485             return KuitFormats::toNumberEuro2ct(text);
01486         default:
01487             return text;
01488         }
01489     }
01490     else if (tag == Kuit::Tag::Filename) {
01491         return QDir::toNativeSeparators(text);
01492     }
01493     else if (tag == Kuit::Tag::Shortcut) {
01494         return KuitFormats::toKeyCombo(text, m_comboKeyDelim[fmt], m_keyNames);
01495     }
01496     else if (tag == Kuit::Tag::Interface) {
01497         return KuitFormats::toInterfacePath(text, m_guiPathDelim[fmt]);
01498     }
01499 
01500     // Fell through, no modification.
01501     return text;
01502 }
01503 
01504 QString KuitSemanticsPrivate::finalizeVisualText (const QString &final,
01505                                                   Kuit::FmtVar fmt,
01506                                                   bool hadQtTag,
01507                                                   bool hadAnyHtmlTag) const
01508 {
01509     KuitSemanticsStaticData *s = staticData;
01510 
01511     QString text = final;
01512 
01513     // Resolve XML entities if format explicitly not rich
01514     // and no HTML tag encountered.
01515     if (fmt != Kuit::Fmt::Rich && !hadAnyHtmlTag)
01516     {
01517         static QRegExp staticEntRx("&([a-z]+);");
01518         // We have to have a local copy here, otherwise this function
01519         // will not be thread safe because QRegExp is not thread safe.
01520         QRegExp entRx = staticEntRx;
01521         int p = entRx.indexIn(text);
01522         QString plain;
01523         while (p >= 0) {
01524             QString ent = entRx.capturedTexts().at(1);
01525             plain.append(text.mid(0, p));
01526             text.remove(0, p + ent.length() + 2);
01527             if (s->xmlEntities.contains(ent)) { // known entity
01528                 plain.append(s->xmlEntities[ent]);
01529             } else { // unknown entity, just leave as is
01530                 plain.append('&' + ent + ';');
01531             }
01532             p = entRx.indexIn(text);
01533         }
01534         plain.append(text);
01535         text = plain;
01536     }
01537 
01538     // Add top rich tag if format explicitly rich or such tag encountered.
01539     if (fmt == Kuit::Fmt::Rich || hadQtTag) {
01540         text = "<html>" + text + "</html>";
01541     }
01542 
01543     return text;
01544 }
01545 
01546 QString KuitSemanticsPrivate::salvageMarkup (const QString &text_,
01547                                              Kuit::FmtVar fmt) const
01548 {
01549     KuitSemanticsStaticData *s = staticData;
01550     QString text = text_;
01551     QString ntext;
01552     int pos;
01553 
01554     // Resolve KUIT tags simple-mindedly.
01555 
01556     // - tags with content
01557     static QRegExp staticWrapRx("(<\\s*(\\w+)\\b([^>]*)>)(.*)(<\\s*/\\s*\\2\\s*>)");
01558     QRegExp wrapRx = staticWrapRx; // for thread-safety
01559     wrapRx.setMinimal(true);
01560     pos = 0;
01561     ntext.clear();
01562     while (true) {
01563         int previousPos = pos;
01564         pos = wrapRx.indexIn(text, previousPos);
01565         if (pos < 0) {
01566             ntext += text.mid(previousPos);
01567             break;
01568         }
01569         ntext += text.mid(previousPos, pos - previousPos);
01570         const QStringList capts = wrapRx.capturedTexts();
01571         QString tagname = capts[2].toLower();
01572         QString content = salvageMarkup(capts[4], fmt);
01573         if (s->knownTags.contains(tagname)) {
01574             // Select formatting pattern.
01575             // TODO: Do not ignore attributes (in capts[3]).
01576             // TODO: Locale-format numbers (in num* tags).
01577             QString pattern = visualPattern(s->knownTags[tagname], 0, fmt);
01578             ntext += pattern.arg(content);
01579         } else {
01580             ntext += capts[1] + content + capts[5];
01581         }
01582         pos += wrapRx.matchedLength();
01583     }
01584     text = ntext;
01585 
01586     // - content-less tags
01587     static QRegExp staticNowrRx("<\\s*(\\w+)\\b([^>]*)/\\s*>");
01588     QRegExp nowrRx = staticNowrRx; // for thread-safety
01589     nowrRx.setMinimal(true);
01590     pos = 0;
01591     ntext.clear();
01592     while (true) {
01593         int previousPos = pos;
01594         pos = nowrRx.indexIn(text, previousPos);
01595         if (pos < 0) {
01596             ntext += text.mid(previousPos);
01597             break;
01598         }
01599         ntext += text.mid(previousPos, pos - previousPos);
01600         const QStringList capts = nowrRx.capturedTexts();
01601         QString tagname = capts[1].toLower();
01602         if (s->knownTags.contains(tagname)) {
01603             QString pattern = visualPattern(s->knownTags[tagname], 0, fmt);
01604             ntext += pattern.arg(QString());
01605         } else {
01606             ntext += capts[0];
01607         }
01608         pos += nowrRx.matchedLength();
01609     }
01610     text = ntext;
01611 
01612     return text;
01613 }
01614 
01615 // -----------------------------------------------------------------------------
01616 // The KuitSemantics methods, only delegate to KuitSemanticsPrivate.
01617 
01618 KuitSemantics::KuitSemantics (const QString &lang)
01619 : d(new KuitSemanticsPrivate(lang))
01620 {
01621 }
01622 
01623 KuitSemantics::~KuitSemantics ()
01624 {
01625     delete d;
01626 }
01627 
01628 QString KuitSemantics::format (const QString &text, const QString &ctxt) const
01629 {
01630     return d->format(text, ctxt);
01631 }
01632 
01633 bool KuitSemantics::mightBeRichText (const QString &text)
01634 {
01635     KuitSemanticsStaticData *s = staticData;
01636 
01637     // Check by appearance of a valid XML entity at first ampersand.
01638     int p1 = text.indexOf('&');
01639     if (p1 >= 0) {
01640         p1 += 1;
01641         int p2 = text.indexOf(';', p1);
01642         return (p2 > p1 && s->xmlEntities.contains(text.mid(p1, p2 - p1)));
01643     }
01644 
01645     // Check by appearance of a valid Qt rich-text tag at first less-than.
01646     int tlen = text.length();
01647     p1 = text.indexOf('<');
01648     if (p1 >= 0) {
01649         p1 += 1;
01650         // Also allow first tag to be closing tag,
01651         // e.g. in case the text is pieced up with list.join("</foo><foo>")
01652         bool closing = false;
01653         while (p1 < tlen && (text[p1].isSpace() || text[p1] == '/')) {
01654             if (text[p1] == '/') {
01655                 if (!closing) {
01656                     closing = true;
01657                 } else {
01658                     return false;
01659                 }
01660             }
01661             ++p1;
01662         }
01663         for (int p2 = p1; p2 < tlen; ++p2) {
01664             QChar c = text[p2];
01665             if (c == '>' || (!closing && c == '/') || c.isSpace()) {
01666                 return s->qtHtmlTagNames.contains(text.mid(p1, p2 - p1));
01667             } else if (!c.isLetter()) {
01668                 return false;
01669             }
01670         }
01671         return false;
01672     }
01673 
01674     return false;
01675 }
01676 
01677 QString KuitSemantics::escape (const QString &text)
01678 {
01679     int tlen = text.length();
01680     QString ntext;
01681     ntext.reserve(tlen);
01682     for (int i = 0; i < tlen; ++i) {
01683         QChar c = text[i];
01684         if (c == '&') {
01685             ntext += "&amp;";
01686         } else if (c == '<') {
01687             ntext += "&lt;";
01688         } else if (c == '>') {
01689             ntext += "&gt;";
01690         } else {
01691             ntext += c;
01692         }
01693     }
01694 
01695     return ntext;
01696 }
01697 

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.4
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