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