void RichTxt::CombineFormat(FormatInfo& fi, int pi, int pi2, bool& first, const RichStyles& style) const { while(pi < pi2) { if(IsTable(pi)) { const RichTable& tab = part[pi].Get<RichTable>(); for(int i = 0; i < tab.GetRows(); i++) for(int j = 0; j < tab.GetColumns(); j++) if(tab(i, j)) { const RichTxt& txt = tab[i][j].text; txt.CombineFormat(fi, 0, txt.GetPartCount(), first, style); } } else { RichPara pa = Get(pi, style); if(first) { fi.Set(pa.format); if(pa.GetCount()) fi.Set(pa[0].format); first = false; } else fi.Combine(pa.format); for(int i = first; i < pa.GetCount(); i++) fi.Combine(pa[i].format); } pi++; } }
void RichTxt::RestoreFormat(int pi, const Formating& info, int& ii, const RichStyles& style) { Array<RichObject> dummy; while(ii < info.format.GetCount() && pi < GetPartCount()) { if(IsTable(pi)) { RichTable& tab = part[pi].Get<RichTable>(); for(int i = 0; i < tab.GetRows(); i++) for(int j = 0; j < tab.GetColumns(); j++) { if(tab(i, j)) { if(ii >= info.format.GetCount()) return; tab.InvalidateRefresh(i, j); tab[i][j].text.RestoreFormat(0, info, ii, style); } } pi++; } else { RichPara pa = Get(pi, style); RichPara pf; pf.Unpack(info.format[ii], dummy, GetStyle(style, info.styleid[ii]).format); RichPara t; t.format = pf.format; int si = 0; int sp = 0; for(int j = 0; j < pf.GetCount(); j++) { const RichPara::Part& q = pf[j]; for(int k = 0; k < q.text.GetLength(); k++) { int len = q.text[k] - 32; t.part.Add().format = q.format; while(len) { const RichPara::Part& p = pa[si]; if(p.IsText()) { int l = min(len, p.GetLength() - sp); t.part.Top().text.Cat(p.text.Mid(sp, l)); sp += l; len -= l; ASSERT(sp <= p.GetLength()); if(sp >= p.GetLength()) { sp = 0; si++; } } else { ASSERT(sp == 0); (t.part.Add() = pa[si++]).format = q.format; len--; sp = 0; } } } } ASSERT(si == pa.GetCount() && sp == 0); Put(pi, t, style); ii++; pi++; } } }
void RichQtfParser::EndPart() { if(istable) { if(paragraph.GetCount() == 0 && text.GetCount() == 0) if(table.GetCount()) table.Top().text.CatPick(pick(tablepart)); else target.CatPick(pick(tablepart)); else { paragraph.part.Clear(); text.Clear(); } } else { Flush(); bool b = paragraph.format.newpage; if(breakpage) paragraph.format.newpage = true; if(table.GetCount()) table.Top().text.Cat(paragraph, target.GetStyles()); else target.Cat(paragraph); paragraph.part.Clear();; paragraph.format.newpage = b; SetFormat(); breakpage = false; } istable = false; }
void RichTxt::SaveFormat(Formating& r, int p1, int p2, const RichStyles& style) const { Array<RichObject> dummy; for(int i = p1; i <= p2; i++) if(IsTable(i)) { const RichTable& tab = part[i].Get<RichTable>(); for(int i = 0; i < tab.GetRows(); i++) for(int j = 0; j < tab.GetColumns(); j++) if(tab(i, j)) { const RichTxt& txt = tab[i][j].text; txt.SaveFormat(r, 0, txt.GetPartCount() - 1, style); } } else { RichPara pa = Get(i, style); for(int i = 0; i < pa.GetCount(); i++) { RichPara::Part& p = pa[i]; int q = p.GetLength(); p.field = Id(); p.object = RichObject(); WString h; while(q) { int c = min(q, 50000); h.Cat(c + 32); q -= c; } p.text = h; } r.styleid.Add(pa.format.styleid); r.format.Add(pa.Pack(GetStyle(style, pa.format.styleid).format, dummy)); } }
void RichTxt::Apply(const RichText::FormatInfo& fi, RichPara& pa, const RichStyles& style) { ApplyStyle(fi, pa, style); for(int i = 0; i < pa.GetCount(); i++) fi.ApplyTo(pa[i].format); fi.ApplyTo(pa.format); }
NAMESPACE_UPP void RichTxt::GetAllLanguages(Index<int>& all) const { for(int i = 0; i < part.GetCount(); i++) { if(IsTable(i)) { const RichTable& tab = part[i].Get<RichTable>(); for(int i = 0; i < tab.GetRows(); i++) for(int j = 0; j < tab.GetColumns(); j++) if(tab(i, j)) tab[i][j].text.GetAllLanguages(all); } else { RichPara p = Get(i, RichStyle::GetDefault()); all.FindAdd(p.format.language); for(int i = 0; i < p.GetCount(); i++) all.FindAdd(p[i].format.language); } } }
Value DashEdit::GetData() const { const RichText& txt = Get(); String r; for(int i = 0; i < txt.GetPartCount(); i++) { if(i) r << "\r\n"; if(txt.IsPara(i)) { RichPara p = txt.Get(i); for(int j = 0; j < p.GetCount(); j++) if(p.part[j].format.dashed) { r << '~'; r << p.part[j].text; r << '~'; } else r << p.part[j].text; } } return r; }
virtual bool operator()(int pos, const RichPara& para) { for(int i = 0; i < para.GetCount(); i++) { String l = para[i].format.link; if(!IsNull(l)) { if(l[0] == ':') { int q = reflink->Find(l); int w = q; if(q < 0) q = reflink->Find(l + "::class"); if(q < 0) q = reflink->Find(l + "::struct"); if(q < 0) q = reflink->Find(l + "::union"); if(q >= 0) l = (*reflink)[q]; } link.FindAdd(Nvl(reflink->Get(l, Null), l)); } } return false; }
Bits RichEdit::SpellParagraph(const RichPara& para) { int len = para.GetLength(); Buffer<wchar> text(len); Buffer<int> lang(len); wchar *s = text; int *g = lang; for(int i = 0; i < para.GetCount(); i++) { const RichPara::Part& p = para[i]; if(p.IsText()) { int l = p.text.GetLength(); memcpy(s, p.text, l * sizeof(wchar)); Fill(g, g + l, fixedlang ? fixedlang : p.format.language); s += l; g += l; } else { *s++ = 127; *g++ = 0; } } Bits e; s = text; wchar *end = text + len; while(s < end) { if(IsLetter(*s)) { const wchar *q = s; while(s < end && IsLetter(*s) || s + 1 < end && *s == '\'' && IsLetter(s[1])) s++; if(!SpellWord(q, int(s - q), lang[q - text])) e.Set(int(q - text), true, int(s - q)); } else s++; } return e; }
void RichQtfParser::Parse(const char *qtf, int _accesskey) { accesskey = _accesskey; term = qtf; while(*term) { if(Key('[')) { Flush(); fstack.Add(format); for(;;) { int c = *term; if(!c) Error("Unexpected end of text"); term++; if(c == ' ' || c == '\n') break; switch(c) { case 's': { Uuid id; c = *term; if(Key('\"') || Key('\'')) id = target.GetStyleId(GetText(c)); else { int i = ReadNumber(); if(i >= 0 && i < styleid.GetCount()) id = styleid[i]; else id = RichStyle::GetDefaultId(); } const RichStyle& s = target.GetStyle(id); bool p = format.newpage; int lng = format.language; (RichPara::Format&) format = s.format; format.styleid = id; format.language = lng; format.newpage = p; break; } case '/': format.Italic(!format.IsItalic()); break; case '*': format.Bold(!format.IsBold()); break; case '_': format.Underline(!format.IsUnderline()); break; case 'T': format.NonAntiAliased(!format.IsNonAntiAliased()); break; case '-': format.Strikeout(!format.IsStrikeout()); break; case 'c': format.capitals = !format.capitals; break; case 'd': format.dashed = !format.dashed; break; case '`': format.sscript = format.sscript == 1 ? 0 : 1; break; case ',': format.sscript = format.sscript == 2 ? 0 : 2; break; case '^': format.link = GetText('^'); break; case 'I': format.indexentry = FromUtf8(GetText(';')); break; case '+': format.Height(GetNumber()); break; case '@': format.ink = GetColor(); break; case '$': format.paper = GetColor(); break; case 'A': format.Face(Font::ARIAL); break; case 'R': format.Face(Font::ROMAN); break; case 'C': format.Face(Font::COURIER); break; case 'G': format.Face(Font::STDFONT); break; case 'S': #ifdef PLATFORM_WIN32 format.Face(Font::SYMBOL); #endif break; case '.': { int n = GetNumber(); if(n >= Font::GetFaceCount()) Error("Invalid face number"); format.Face(n); break; } case '!': { String fn = GetText('!'); int i = Font::FindFaceNameIndex(fn); if(i < 0) i = Font::ARIAL; format.Face(i); } break; case '{': { String cs = GetText('}'); if(cs.GetLength() == 1) { int c = *cs; if(c == '_') format.charset = CHARSET_UTF8; if(c >= '0' && c <= '8') format.charset = c - '0' + CHARSET_WIN1250; if(c >= 'A' && c <= 'Z') format.charset = c - '0' + CHARSET_ISO8859_1; } else { for(int i = 0; i < CharsetCount(); i++) if(stricmp(CharsetName(i), cs) == 0) { format.charset = i; break; } } break; } case '%': { String h; if(*term == '-') { format.language = 0; term++; } else if(*term == '%') { format.language = LNG_ENGLISH; term++; } else { while(*term && h.GetLength() < 5) h.Cat(*term++); format.language = LNGFromText(h); } break; } case 'g': format.Face(Font::STDFONT); format.Height(GetRichTextScreenStdFontHeight()); break; default: if(c >= '0' && c <= '9') { format.Height(QTFFontHeight[c - '0']); break; } switch(c) { case ':': format.label = GetText(':'); break; case '<': format.align = ALIGN_LEFT; break; case '>': format.align = ALIGN_RIGHT; break; case '=': format.align = ALIGN_CENTER; break; case '#': format.align = ALIGN_JUSTIFY; break; case 'l': format.lm = GetNumber(); break; case 'r': format.rm = GetNumber(); break; case 'i': format.indent = GetNumber(); break; case 'b': format.before = GetNumber(); break; case 'a': format.after = GetNumber(); break; case 'P': format.newpage = !format.newpage; break; case 'k': format.keep = !format.keep; break; case 'K': format.keepnext = !format.keepnext; break; case 'H': format.ruler = GetNumber(); break; case 'h': format.rulerink = GetColor(); break; case 'L': format.rulerstyle = GetNumber(); break; case 'Q': format.orphan = !format.orphan; break; case 'n': format.before_number = GetText(';'); break; case 'm': format.after_number = GetText(';'); break; case 'N': { memset(format.number, 0, sizeof(format.number)); format.reset_number = false; int i = 0; while(i < 8) { int c; if(Key('-')) c = RichPara::NUMBER_NONE; else if(Key('1')) c = RichPara::NUMBER_1; else if(Key('0')) c = RichPara::NUMBER_0; else if(Key('a')) c = RichPara::NUMBER_a; else if(Key('A')) c = RichPara::NUMBER_A; else if(Key('i')) c = RichPara::NUMBER_i; else if(Key('I')) c = RichPara::NUMBER_I; else break; format.number[i++] = c; } if(Key('!')) format.reset_number = true; break; } case 'o': format.bullet = RichPara::BULLET_ROUND; format.indent = 150; break; case 'O': if(Key('_')) format.bullet = RichPara::BULLET_NONE; else { int c = *term++; if(!c) Error("Unexpected end of text"); format.bullet = c == '1' ? RichPara::BULLET_ROUNDWHITE : c == '2' ? RichPara::BULLET_BOX : c == '3' ? RichPara::BULLET_BOXWHITE : c == '9' ? RichPara::BULLET_TEXT : RichPara::BULLET_ROUND; } break; case 'p': switch(*term++) { case 0: Error("Unexpected end of text"); case 'h': format.linespacing = RichPara::LSP15; break; case 'd': format.linespacing = RichPara::LSP20; break; default: format.linespacing = RichPara::LSP10; } break; case 't': if(IsDigit(*term)) //temporary fix... :( format.tabsize = ReadNumber(); break; case '~': { if(Key('~')) format.tab.Clear(); else { RichPara::Tab tab; Key('<'); if(Key('>')) tab.align = ALIGN_RIGHT; if(Key('=')) tab.align = ALIGN_CENTER; if(Key('.')) tab.fillchar = 1; if(Key('-')) tab.fillchar = 2; if(Key('_')) tab.fillchar = 3; int rightpos = Key('>') ? RichPara::TAB_RIGHTPOS : 0; tab.pos = rightpos | ReadNumber(); format.tab.Add(tab); } } break; default: continue; } } } SetFormat(); } else if(Key(']')) { Flush(); if(fstack.GetCount()) { format = fstack.Top(); fstack.Drop(); } else Error("Unmatched ']'"); } else if(Key2('{')) { if(oldtab) Error("{{ in ++ table"); if(text.GetLength() || paragraph.GetCount()) EndPart(); table.Add(); int r = IsDigit(*term) ? ReadNumber() : 1; Table().AddColumn(r); while(Key(':')) Table().AddColumn(ReadNumber()); TableFormat(); SetFormat(); } else if(Key2('}')) { if(oldtab) Error("}} in ++ table"); FinishTable(); } else if(Key2('+')) if(oldtab) FinishOldTable(); else { Flush(); if(text.GetLength() || paragraph.GetCount()) EndPart(); Tab& b = table.Add(); b.rown.Add(0); b.hspan = 1; b.Old(); oldtab = true; } else if(Key2('|')) FinishCell(); else if(Key2('-')) { FinishCell(); table.Top().rown.Add(0); } else if(Key2(':')) { if(!oldtab) FinishCell(); TableFormat(oldtab); } else if(Key2('^')) { EndPart(); breakpage = true; } else if(Key2('@')) { ReadObject(); } else if(Key2('@', '$')) { String xu; while(isxdigit(*term)) xu.Cat(*term++); int c = stou(~xu, NULL, 16); if(c >= 32) Cat(c); if(*term == ';') term++; SetFormat(); } else if(Key2('^', 'H')) target.SetHeaderQtf(GetText2('^', '^')); else if(Key2('^', 'F')) target.SetFooterQtf(GetText2('^', '^')); else if(Key2('{', ':')) { Flush(); String field = GetText(':'); String param = GetText(':'); Id fid(field); if(RichPara::fieldtype().Find(fid) >= 0) paragraph.Cat(fid, param, format); Key('}'); } else if(Key('&')) { SetFormat(); EndPart(); } else if(Key2('$')) { Flush(); int i = GetNumber(); Uuid id; RichStyle style; style.format = format; if(Key(',')) stylenext.At(i, 0) = GetNumber(); else stylenext.At(i, 0) = i; if(Key('#')) { String xu; while(isxdigit(*term)) xu.Cat(*term++); if(xu.GetLength() != 32) Error("Invalid UUID !"); id = ScanUuid(xu); } else if(i) id = Uuid::Create(); else id = RichStyle::GetDefaultId(); if(Key(':')) style.name = GetText(']'); if(fstack.GetCount()) { format = fstack.Top(); fstack.Drop(); } target.SetStyle(id, style); styleid.At(i, RichStyle::GetDefaultId()) = id; if(id == RichStyle::GetDefaultId()) { bool p = format.newpage; int lng = format.language; (RichPara::Format&) format = style.format; format.styleid = id; format.language = lng; format.newpage = p; } } else if(*term == '_') { SetFormat(); text.Cat(160); term++; } else if(Key2('-', '|')) { SetFormat(); text.Cat(9); } else if(*term == '\1') { if(istable) EndPart(); SetFormat(); const char *b = ++term; for(; *term && *term != '\1'; term++) { if((byte)*term == '\n') { text.Cat(ToUnicode(b, (int)(term - b), format.charset)); EndPart(); b = term + 1; } if((byte)*term == '\t') { text.Cat(ToUnicode(b, (int)(term - b), format.charset)); text.Cat(9); b = term + 1; } } text.Cat(ToUnicode(b, (int)(term - b), format.charset)); if(*term == '\1') term++; } else { if(!Key('`')) Key('\\'); if((byte)*term >= ' ') { SetFormat(); do { if(istable) EndPart(); if(format.charset == CHARSET_UTF8) { word code = (byte)*term++; if(code <= 0x7F) Cat(code); else if(code <= 0xDF) { if(*term == '\0') break; int c0 = (byte)*term++; if(c0 < 0x80) Error("Invalid UTF-8 sequence"); Cat(((code - 0xC0) << 6) + c0 - 0x80); } else if(code <= 0xEF) { int c0 = (byte)*term++; int c1 = (byte)*term++; if(c0 < 0x80 || c1 < 0x80) Error("Invalid UTF-8 sequence"); Cat(((code - 0xE0) << 12) + ((c0 - 0x80) << 6) + c1 - 0x80); } else Error("Invalid UTF-8 sequence"); } else Cat(ToUnicode((byte)*term++, format.charset)); } while((byte)*term >= 128 || s_nodeqtf[(byte)*term]); } else if(*term) term++; } } // if(paragraph.GetCount() == 0) // TRC 11/02/15: kills formatting of last paragraph in text // SetFormat(); if(oldtab) FinishOldTable(); else while(table.GetCount()) FinishTable(); EndPart(); FlushStyles(); }
// Parses TOC and fills tocTree control bool HelpViewer::LoadTOC(String const &tocLink) { // TOC is composed by QTF lines with a TOC link and label text // lines not containing the TOC link are simply ignored // TOC level is represented by number of TABS contained // inside a line; zero tabs means toplevel, id 0 of tocTree Vector<int>ids; ids.Add(0); int curLevel = 0; int curId = 0; TreeCtrl tocTree; Topic t = GetTopic(tocLink); if(IsNull(t.text)) return false; String label = t.label; String topic = t.link; // sets toc root string from topic ticle tocTree.SetRoot(Null, t.title); // converts QTF to RichTxt, easier to analyze RichText txt = ParseQTF(t.text); // extract all paragraphs from RichTxt, skip other objects for(int iPart = 0; iPart < txt.GetPartCount(); iPart++) { String tocLine; String lineLink; if(!txt.IsPara(iPart)) continue; RichPara p = txt.Get(iPart); for(int iParaPart = 0; iParaPart < p.GetCount(); iParaPart++) { RichPara::Part ¶Part = p.part[iParaPart]; if(paraPart.format.link != "") lineLink = paraPart.format.link; if(paraPart.NonText()) continue; tocLine += paraPart.text.ToString(); } // if part contains no text nor link to topic stuffs, simply skip it if(tocLine == "" || lineLink == "" || !lineLink.StartsWith("topic:")) continue; // now we should count tabls to get TOC indent level int level = 0; int j; while( (j = tocLine.Find(0x09)) >= 0) { level++; tocLine.Remove(j); } // allows just SINGLE UPPER level change, i.e., deeper ONE level from current // for each step; backleveling is possible at any depth if(level > curLevel+1) return false; if(level > curLevel) { ids.Add(curId); curLevel++; } else while(curLevel > level) { curLevel--; ids.Pop(); } // add the line to correct tree node curId = tocTree.Add(ids.Top(), Null, Value(lineLink), Value(tocLine)); } // if the tree control is non empty, appends it to the main TOC TreeCtrl if(tocTree.GetChildCount(0)) AppendTOC(tocTree); // opens all content tree and display first item mainTocTree.OpenDeep(0); mainTocTree.SetCursor(2); // stores first topic at top of stack String curLink = mainTocTree.Get(); stack.Clear(); stack.Add(curLink); tos = 0; return true; }