00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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
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
00052 namespace Kuit {
00053
00054 namespace Tag {
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
00062 } Var;
00063 }
00064
00065 namespace Att {
00066 typedef enum {
00067 None,
00068 Ctx, Url, Address, Section, Label
00069 } Var;
00070 }
00071
00072 namespace Rol {
00073 typedef enum {
00074 None,
00075 Action, Title, Option, Label, Item, Info
00076 } Var;
00077 }
00078
00079 namespace Cue {
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 {
00092 typedef enum {
00093 None, Plain, Rich, Term
00094 } Var;
00095 }
00096
00097 namespace Numfmt {
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
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
00149
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);
00200 SETUP_TAG(NumReal, KUIT_NUMREAL, None, None);
00201
00202
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
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
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
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
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
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
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
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
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
00338 numfmtInt = -1;
00339 numfmtReal = -1;
00340 }
00341
00342 K_GLOBAL_STATIC(KuitSemanticsStaticData, staticData)
00343
00344
00345
00346
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
00357 QString metaTr (const char *ctxt, const char *id) const;
00358
00359
00360 void setFormattingPatterns ();
00361
00362
00363 void setTextTransformData ();
00364
00365
00366 static int attSetKey (const QSet<Kuit::AttVar> &aset = QSet<Kuit::AttVar>());
00367
00368
00369 static Kuit::FmtVar formatFromContextMarker (const QString &ctxmark,
00370 const QString &text);
00371
00372 static Kuit::FmtVar formatFromTags (const QString &text);
00373
00374
00375 static QString equipTopTag (const QString &text, Kuit::TagVar &toptag);
00376
00377
00378 QString semanticToVisualText (const QString &text,
00379 Kuit::FmtVar fmtExp,
00380 Kuit::FmtVar fmtImp) const;
00381
00382
00383 QString finalizeVisualText (const QString &final,
00384 Kuit::FmtVar fmt,
00385 bool hadQtTag = false,
00386 bool hadAnyHtmlTag = false) const;
00387
00388
00389 QString salvageMarkup (const QString &text, Kuit::FmtVar fmt) const;
00390
00391
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
00408 KuitSemanticsPrivate::OpenEl parseOpenEl (const QXmlStreamReader &xml,
00409 Kuit::TagVar etag,
00410 const QString &text) const;
00411
00412
00413 QString visualPattern (Kuit::TagVar tag, int akey, Kuit::FmtVar fmt) const;
00414
00415
00416 QString formatSubText (const QString &ptext, const OpenEl &oel,
00417 Kuit::FmtVar fmt, int numctx) const;
00418
00419
00420 static void countWrappingNewlines (const QString &ptext,
00421 int &numle, int &numtr);
00422
00423
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,
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
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
00455
00456
00457
00458
00459
00460
00461
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
00474 m_metaTranslit = KTranslit::create(realLang);
00475 int pos = lang.indexOf('@');
00476 if (pos >= 0) {
00477 m_metaScript = lang.mid(pos + 1);
00478 }
00479
00480
00481
00482
00483 setFormattingPatterns();
00484
00485
00486 setTextTransformData();
00487
00488
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
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 \
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
00526 #undef I18N_NOOP2
00527 #define I18N_NOOP2(ctxt, msg) ctxt, msg
00528
00529
00530
00531 #undef XXXX_NOOP2
00532 #define XXXX_NOOP2(ctxt, msg) ctxt, msg
00533
00534
00535
00536
00537
00538 SET_PATTERN(Tag::Title, Att::None, Fmt::Plain,
00539 I18N_NOOP2("@title/plain",
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550 "== %1 =="));
00551 SET_PATTERN(Tag::Title, Att::None, Fmt::Rich,
00552 I18N_NOOP2("@title/rich",
00553
00554 "<h2>%1</h2>"));
00555
00556
00557 SET_PATTERN(Tag::Subtitle, Att::None, Fmt::Plain,
00558 I18N_NOOP2("@subtitle/plain",
00559
00560 "~ %1 ~"));
00561 SET_PATTERN(Tag::Subtitle, Att::None, Fmt::Rich,
00562 I18N_NOOP2("@subtitle/rich",
00563
00564 "<h3>%1</h3>"));
00565
00566
00567 SET_PATTERN(Tag::Para, Att::None, Fmt::Plain,
00568 XXXX_NOOP2("@para/plain",
00569
00570 "%1"));
00571 SET_PATTERN(Tag::Para, Att::None, Fmt::Rich,
00572 XXXX_NOOP2("@para/rich",
00573
00574 "<p>%1</p>"));
00575
00576
00577 SET_PATTERN(Tag::List, Att::None, Fmt::Plain,
00578 XXXX_NOOP2("@list/plain",
00579
00580 "%1"));
00581 SET_PATTERN(Tag::List, Att::None, Fmt::Rich,
00582 XXXX_NOOP2("@list/rich",
00583
00584 "<ul>%1</ul>"));
00585
00586
00587 SET_PATTERN(Tag::Item, Att::None, Fmt::Plain,
00588 I18N_NOOP2("@item/plain",
00589
00590 " * %1"));
00591 SET_PATTERN(Tag::Item, Att::None, Fmt::Rich,
00592 I18N_NOOP2("@item/rich",
00593
00594 "<li>%1</li>"));
00595
00596
00597 SET_PATTERN(Tag::Note, Att::None, Fmt::Plain,
00598 I18N_NOOP2("@note/plain",
00599
00600 "Note: %1"));
00601 SET_PATTERN(Tag::Note, Att::None, Fmt::Rich,
00602 I18N_NOOP2("@note/rich",
00603
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
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
00614 "<i>%1</i>: %2"));
00615
00616
00617 SET_PATTERN(Tag::Warning, Att::None, Fmt::Plain,
00618 I18N_NOOP2("@warning/plain",
00619
00620 "WARNING: %1"));
00621 SET_PATTERN(Tag::Warning, Att::None, Fmt::Rich,
00622 I18N_NOOP2("@warning/rich",
00623
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
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
00634 "<b>%1</b>: %2"));
00635
00636
00637 SET_PATTERN(Tag::Link, Att::None, Fmt::Plain,
00638 XXXX_NOOP2("@link/plain",
00639
00640 "%1"));
00641 SET_PATTERN(Tag::Link, Att::None, Fmt::Rich,
00642 XXXX_NOOP2("@link/rich",
00643
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
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
00654 "<a href=\"%1\">%2</a>"));
00655
00656
00657 SET_PATTERN(Tag::Filename, Att::None, Fmt::Plain,
00658 I18N_NOOP2("@filename/plain",
00659
00660 "‘%1’"));
00661 SET_PATTERN(Tag::Filename, Att::None, Fmt::Rich,
00662 I18N_NOOP2("@filename/rich",
00663
00664 "<tt>%1</tt>"));
00665
00666
00667 SET_PATTERN(Tag::Application, Att::None, Fmt::Plain,
00668 I18N_NOOP2("@application/plain",
00669
00670 "%1"));
00671 SET_PATTERN(Tag::Application, Att::None, Fmt::Rich,
00672 I18N_NOOP2("@application/rich",
00673
00674 "%1"));
00675
00676
00677 SET_PATTERN(Tag::Command, Att::None, Fmt::Plain,
00678 I18N_NOOP2("@command/plain",
00679
00680 "%1"));
00681 SET_PATTERN(Tag::Command, Att::None, Fmt::Rich,
00682 I18N_NOOP2("@command/rich",
00683
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
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
00694 "<tt>%1(%2)</tt>"));
00695
00696
00697 SET_PATTERN(Tag::Resource, Att::None, Fmt::Plain,
00698 I18N_NOOP2("@resource/plain",
00699
00700 "“%1”"));
00701 SET_PATTERN(Tag::Resource, Att::None, Fmt::Rich,
00702 I18N_NOOP2("@resource/rich",
00703
00704 "“%1”"));
00705
00706
00707 SET_PATTERN(Tag::Icode, Att::None, Fmt::Plain,
00708 I18N_NOOP2("@icode/plain",
00709
00710 "“%1”"));
00711 SET_PATTERN(Tag::Icode, Att::None, Fmt::Rich,
00712 I18N_NOOP2("@icode/rich",
00713
00714 "<tt>%1</tt>"));
00715
00716
00717 SET_PATTERN(Tag::Bcode, Att::None, Fmt::Plain,
00718 XXXX_NOOP2("@bcode/plain",
00719
00720 "\n%1\n"));
00721 SET_PATTERN(Tag::Bcode, Att::None, Fmt::Rich,
00722 XXXX_NOOP2("@bcode/rich",
00723
00724 "<pre>%1</pre>"));
00725
00726
00727 SET_PATTERN(Tag::Shortcut, Att::None, Fmt::Plain,
00728 I18N_NOOP2("@shortcut/plain",
00729
00730 "%1"));
00731 SET_PATTERN(Tag::Shortcut, Att::None, Fmt::Rich,
00732 I18N_NOOP2("@shortcut/rich",
00733
00734 "<b>%1</b>"));
00735
00736
00737 SET_PATTERN(Tag::Interface, Att::None, Fmt::Plain,
00738 I18N_NOOP2("@interface/plain",
00739
00740 "|%1|"));
00741 SET_PATTERN(Tag::Interface, Att::None, Fmt::Rich,
00742 I18N_NOOP2("@interface/rich",
00743
00744 "<i>%1</i>"));
00745
00746
00747 SET_PATTERN(Tag::Emphasis, Att::None, Fmt::Plain,
00748 I18N_NOOP2("@emphasis/plain",
00749
00750 "*%1*"));
00751 SET_PATTERN(Tag::Emphasis, Att::None, Fmt::Rich,
00752 I18N_NOOP2("@emphasis/rich",
00753
00754 "<i>%1</i>"));
00755
00756
00757 SET_PATTERN(Tag::Placeholder, Att::None, Fmt::Plain,
00758 I18N_NOOP2("@placeholder/plain",
00759
00760 "<%1>"));
00761 SET_PATTERN(Tag::Placeholder, Att::None, Fmt::Rich,
00762 I18N_NOOP2("@placeholder/rich",
00763
00764 "<<i>%1</i>>"));
00765
00766
00767 SET_PATTERN(Tag::Email, Att::None, Fmt::Plain,
00768 I18N_NOOP2("@email/plain",
00769
00770 "<%1>"));
00771 SET_PATTERN(Tag::Email, Att::None, Fmt::Rich,
00772 I18N_NOOP2("@email/rich",
00773
00774 "<<a href=\"mailto:%1\">%1</a>>"));
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
00779 "%1 <%2>"));
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
00784 "<a href=\"mailto:%2\">%1</a>"));
00785
00786
00787 SET_PATTERN(Tag::Envar, Att::None, Fmt::Plain,
00788 I18N_NOOP2("@envar/plain",
00789
00790 "$%1"));
00791 SET_PATTERN(Tag::Envar, Att::None, Fmt::Rich,
00792 I18N_NOOP2("@envar/rich",
00793
00794 "<tt>$%1</tt>"));
00795
00796
00797 SET_PATTERN(Tag::Message, Att::None, Fmt::Plain,
00798 I18N_NOOP2("@message/plain",
00799
00800 "/%1/"));
00801 SET_PATTERN(Tag::Message, Att::None, Fmt::Rich,
00802 I18N_NOOP2("@message/rich",
00803
00804 "<i>%1</i>"));
00805
00806
00807 SET_PATTERN(Tag::Nl, Att::None, Fmt::Plain,
00808 XXXX_NOOP2("@nl/plain",
00809
00810 "%1\n"));
00811 SET_PATTERN(Tag::Nl, Att::None, Fmt::Rich,
00812 XXXX_NOOP2("@nl/rich",
00813
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
00846
00847 return s->knownNumfmts[fmtname];
00848 } else if (ctry.isEmpty()) {
00849
00850
00851 currfmt = s->knownNumfmts[fmtname];
00852 }
00853 }
00854 return currfmt;
00855 }
00856
00857 void KuitSemanticsPrivate::setTextTransformData ()
00858 {
00859 KuitSemanticsStaticData *s = staticData;
00860
00861
00862 #undef I18N_NOOP2
00863 #define I18N_NOOP2(ctxt, msg) metaTr(ctxt, msg)
00864
00865
00866
00867
00868
00869
00870
00871
00872
00873
00874
00875
00876
00877
00878
00879
00880
00881
00882 QString fmtstrInt = I18N_NOOP2("number-format:integer", "us");
00883 m_numfmtInt = parseNumberFormat(fmtstrInt);
00884
00885
00886
00887 QString fmtstrReal = I18N_NOOP2("number-format:real", "us");
00888 m_numfmtReal = parseNumberFormat(fmtstrReal);
00889
00890
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
00899
00900 m_comboKeyDelim[Kuit::Fmt::Plain] = I18N_NOOP2("shortcut-key-delimiter/plain", "+");
00901 m_comboKeyDelim[Kuit::Fmt::Term] = m_comboKeyDelim[Kuit::Fmt::Plain];
00902
00903
00904 m_comboKeyDelim[Kuit::Fmt::Rich] = I18N_NOOP2("shortcut-key-delimiter/rich", "+");
00905
00906
00907
00908 m_guiPathDelim[Kuit::Fmt::Plain] = I18N_NOOP2("gui-path-delimiter/plain", "→");
00909 m_guiPathDelim[Kuit::Fmt::Term] = m_guiPathDelim[Kuit::Fmt::Plain];
00910
00911
00912 m_guiPathDelim[Kuit::Fmt::Rich] = I18N_NOOP2("gui-path-delimiter/rich", "→");
00913
00914
00915
00916 #undef SET_KEYNAME
00917 #define SET_KEYNAME(rawname) do { \
00918 \
00919 QString normname = QString(rawname).trimmed().toLower(); \
00920 m_keyNames[normname] = metaTr("keyboard-key-name", rawname); \
00921 } while (0)
00922
00923
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
00966
00967
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
00975 Kuit::FmtVar fmtExplicit = formatFromContextMarker(ctxt, text);
00976
00977
00978 if (text.indexOf('<') < 0) {
00979 return finalizeVisualText(text, fmtExplicit);
00980 }
00981
00982
00983
00984 Kuit::FmtVar fmtImplicit = fmtExplicit;
00985 if (fmtExplicit == Kuit::Fmt::None) {
00986 fmtImplicit = formatFromTags(text);
00987 }
00988
00989
00990
00991 Kuit::TagVar toptag;
00992 QString wtext = equipTopTag(text, toptag);
00993
00994
00995 QString ftext = semanticToVisualText(wtext, fmtExplicit, fmtImplicit);
00996 if (ftext.isEmpty()) {
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
01026
01027 QString rolname;
01028 QString fmtname;
01029 QString cuename;
01030 QString ctxmark = ctxmark_.trimmed();
01031 if (ctxmark.startsWith('@')) {
01032 static QRegExp wsRx("\\s");
01033 ctxmark = ctxmark.mid(1, wsRx.indexIn(ctxmark) - 1);
01034
01035
01036 int pfmt = ctxmark.indexOf('/');
01037 if (pfmt >= 0) {
01038 fmtname = ctxmark.mid(pfmt + 1);
01039 ctxmark = ctxmark.left(pfmt);
01040 }
01041
01042
01043 int pcue = ctxmark.indexOf(':');
01044 if (pcue >= 0) {
01045 cuename = ctxmark.mid(pcue + 1);
01046 ctxmark = ctxmark.left(pcue);
01047 }
01048
01049
01050 rolname = ctxmark;
01051 }
01052
01053
01054
01055 rolname = rolname.trimmed().toLower();
01056 cuename = cuename.trimmed().toLower();
01057 fmtname = fmtname.trimmed().toLower();
01058
01059
01060 Kuit::RolVar rol;
01061 if (s->knownRols.contains(rolname)) {
01062 rol = s->knownRols[rolname];
01063 }
01064 else {
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
01074 Kuit::CueVar cue;
01075 if (s->knownCues.contains(cuename)) {
01076 cue = s->knownCues[cuename];
01077 }
01078 else {
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
01088 Kuit::FmtVar fmt;
01089 if (s->knownFmts.contains(fmtname)) {
01090 fmt = s->knownFmts[fmtname];
01091 }
01092 else {
01093
01094
01095
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;
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
01141
01142
01143 static QRegExp opensWithTagRx("^\\s*<\\s*(\\w+)[^>]*>");
01144 bool explicitTopTag = false;
01145
01146 QString text = text_;
01147 int p = opensWithTagRx.indexIn(text);
01148
01149
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
01155
01156 text = text.mid(fullmatch.length());
01157 p = opensWithTagRx.indexIn(text);
01158 }
01159 }
01160
01161
01162 if (p >= 0) {
01163 QString tagname = opensWithTagRx.capturedTexts().at(1).toLower();
01164 if (s->knownTags.contains(tagname)) {
01165 Kuit::TagVar tag = s->knownTags[tagname];
01166 if ( tag == Kuit::Tag::TopLong
01167 || tag == Kuit::Tag::TopShort) {
01168 toptag = tag;
01169 explicitTopTag = true;
01170 }
01171 else if ( tag == Kuit::Tag::Para
01172 || tag == Kuit::Tag::Title
01173 || tag == Kuit::Tag::Subtitle) {
01174 toptag = Kuit::Tag::TopLong;
01175 }
01176 else {
01177 toptag = Kuit::Tag::TopShort;
01178 }
01179 }
01180 else {
01181 toptag = Kuit::Tag::TopShort;
01182 }
01183 }
01184 else {
01185 toptag = Kuit::Tag::TopShort;
01186 }
01187
01188
01189 if (!explicitTopTag) {
01190 return '<' + s->tagNames[toptag] + '>'
01191 + text_
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
01208
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) {
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
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
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
01254
01255 if (openEls.isEmpty() && oel.avals.contains(Kuit::Att::Ctx)) {
01256
01257 fmtExp = formatFromContextMarker(oel.avals[Kuit::Att::Ctx], text);
01258 fmtImp = fmtExp;
01259 }
01260
01261
01262 openEls.push(oel);
01263
01264
01265 if (oel.tag == Kuit::Tag::Numid) {
01266 ++numCtx;
01267 }
01268 }
01269 else if (xml.isEndElement()) {
01270
01271 OpenEl oel = openEls.pop();
01272
01273
01274 if (openEls.isEmpty()) {
01275
01276 return finalizeVisualText(oel.formattedText, fmtExp,
01277 hadQtTag, hadAnyHtmlTag);
01278 }
01279
01280
01281 QString pt = openEls.top().formattedText;
01282 openEls.top().formattedText += formatSubText(pt, oel, fmtImp, numCtx);
01283
01284
01285 if (oel.tag == Kuit::Tag::Numid) {
01286 --numCtx;
01287 }
01288 }
01289 else if (xml.isCharacters()) {
01290
01291
01292
01293
01294
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
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
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)) {
01339 oel.tag = s->knownTags[oel.name];
01340
01341
01342
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
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
01380 oel.handling = OpenEl::Dropout;
01381 }
01382 else {
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
01398 QString pattern("%1");
01399
01400
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
01420 QString pattern = visualPattern(oel.tag, oel.akey, fmt);
01421
01422
01423 QString mtext = modifyTagText(oel.tag, oel.formattedText, numctx, fmt);
01424
01425 using namespace Kuit;
01426
01427
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
01449
01450 if (!ptext.isEmpty() && s->leadingNewlines.contains(oel.tag)) {
01451
01452 int pnumle, pnumtr, fnumle, fnumtr;
01453 countWrappingNewlines(ptext, pnumle, pnumtr);
01454 countWrappingNewlines(ftext, fnumle, fnumtr);
01455
01456 int numle = pnumtr + fnumle;
01457
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
01470 return '<' + oel.name + "/>";
01471 }
01472 else {
01473 return '<' + oel.name + oel.astr + '>'
01474 + oel.formattedText
01475 + "</" + oel.name + '>';
01476 }
01477 }
01478 else {
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
01488 numle = 0;
01489 while (numle < len && text[numle] == '\n') {
01490 ++numle;
01491 }
01492
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
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
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
01557
01558 if (fmt != Kuit::Fmt::Rich && !hadAnyHtmlTag)
01559 {
01560 static QRegExp staticEntRx("&("ENTITY_SUBRX");");
01561
01562
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('#')) {
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 {
01581 plain.append('&' + ent + ';');
01582 }
01583 }
01584 else if (s->xmlEntities.contains(ent)) {
01585 plain.append(s->xmlEntities[ent]);
01586 } else {
01587 plain.append('&' + ent + ';');
01588 }
01589 p = entRx.indexIn(text);
01590 }
01591 plain.append(text);
01592 text = plain;
01593 }
01594
01595
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
01612
01613
01614 static QRegExp staticWrapRx("(<\\s*(\\w+)\\b([^>]*)>)(.*)(<\\s*/\\s*\\2\\s*>)");
01615 QRegExp wrapRx = staticWrapRx;
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
01632
01633
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
01644 static QRegExp staticNowrRx("<\\s*(\\w+)\\b([^>]*)/\\s*>");
01645 QRegExp nowrRx = staticNowrRx;
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
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
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
01703 int tlen = text.length();
01704 p1 = text.indexOf('<');
01705 if (p1 >= 0) {
01706 p1 += 1;
01707
01708
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 += "&";
01743 } else if (c == '<') {
01744 ntext += "<";
01745 } else if (c == '>') {
01746 ntext += ">";
01747 } else {
01748 ntext += c;
01749 }
01750 }
01751
01752 return ntext;
01753 }
01754