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

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
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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