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