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