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