ultimatepp/uppdev/RichTextP/ParaData.cpp
cxl 3cd394812c Merge continued
git-svn-id: svn://ultimatepp.org/upp/trunk@10263 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2016-10-04 08:34:39 +00:00

775 lines
20 KiB
C++

#include "RichText.h"
NAMESPACE_UPP
PaintInfo::PaintInfo()
{
sell = selh = 0;
tablesel = 0;
top = PageY(0, 0);
bottom = PageY(INT_MAX, INT_MAX);
hyperlink = SColorMark();
usecache = false;
sizetracking = false;
showcodes = Null;
spellingchecker = NULL;
highlightpara = -1;
highlight = Yellow();
indexentry = LtGreen();
coloroverride = false;
}
String RichPara::Number::AsText(const RichPara::NumberFormat& format) const
{
String result;
for(int i = 0; i < 8; i++)
if(format.number[i]) {
if(result.GetLength())
result.Cat('.');
int q = n[i];
switch(format.number[i]) {
case NUMBER_1:
result << AsString(q);
break;
case NUMBER_0:
result << AsString(q - 1);
break;
case NUMBER_a:
result << FormatIntAlpha(q, false);
break;
case NUMBER_A:
result << FormatIntAlpha(q, true);
break;
case NUMBER_i:
result << FormatIntRoman(q, false);
break;
case NUMBER_I:
result << FormatIntRoman(q, true);
break;
}
}
return format.before_number + result + format.after_number;
}
void RichPara::Number::TestReset(const RichPara::NumberFormat& fmt)
{
if(fmt.reset_number) {
bool done = false;
for(int i = 7; i >= 0; --i)
if(fmt.number[i]) {
n[i] = 0;
done = true;
break;
}
if(!done && !IsNull(n[0]))
n[0] = 0;
}
}
void RichPara::Number::Next(const RichPara::NumberFormat& fmt)
{
for(int i = 7; i >= 0; --i)
if(fmt.number[i]) {
n[i++]++;
while(i <= 7)
n[i++] = 0;
break;
}
}
RichPara::Number::Number()
{
memset(n, 0, sizeof(n));
}
bool RichPara::NumberFormat::IsNumbered() const
{
if(before_number.GetLength() || after_number.GetLength())
return true;
for(int i = 0; i < 8; i++)
if(number[i])
return true;
return false;
}
int RichPara::NumberFormat::GetNumberLevel() const
{
for(int i = 7; i >= 0; i--)
if(number[i])
return i + 1;
if(before_number.GetLength() || after_number.GetLength())
return 0;
return -1;
}
bool NumberingDiffers(const RichPara::Format& fmt1, const RichPara::Format& fmt2)
{
return fmt1.before_number != fmt2.before_number ||
fmt1.after_number != fmt2.after_number ||
fmt1.reset_number != fmt2.reset_number ||
memcmp(fmt1.number, fmt2.number, sizeof(fmt1.number));
}
bool operator==(const Vector<RichPara::Tab>& a, const Vector<RichPara::Tab>& b)
{
if(a.GetCount() != b.GetCount()) return false;
for(int i = 0; i < a.GetCount(); i++)
if(a[i].pos != b[i].pos || a[i].align != b[i].align || a[i].fillchar != b[i].fillchar)
return false;
return true;
}
RichPara::CharFormat::CharFormat()
{
(Font &)*this = Arial(100);
ink = Black;
paper = Null;
language = 0;
link = Null;
sscript = 0;
capitals = dashed = false;
}
RichPara::Format::Format()
{
align = ALIGN_LEFT;
ruler = before = lm = rm = indent = after = 0;
rulerink = Black;
bullet = 0;
keep = newpage = keepnext = orphan = false;
tabsize = 296;
memset(number, 0, sizeof(number));
reset_number = false;
linespacing = 0;
tab.Clear();
styleid = RichStyle::GetDefaultId();
}
void RichPara::Charformat(Stream& out, const RichPara::CharFormat& o,
const RichPara::CharFormat& n, const RichPara::CharFormat& s)
{
if(o.IsBold() != n.IsBold())
out.Put(n.IsBold() == s.IsBold() ? BOLDS : BOLD0 + n.IsBold());
if(o.IsItalic() != n.IsItalic())
out.Put(n.IsItalic() == s.IsItalic() ? ITALICS
: ITALIC0 + n.IsItalic());
if(o.IsUnderline() != n.IsUnderline())
out.Put(n.IsUnderline() == s.IsUnderline() ? UNDERLINES
: UNDERLINE0 + n.IsUnderline());
if(o.IsStrikeout() != n.IsStrikeout())
out.Put(n.IsStrikeout() == s.IsStrikeout() ? STRIKEOUTS
: STRIKEOUT0 + n.IsStrikeout());
if(o.IsNonAntiAliased() != n.IsNonAntiAliased()) {
out.Put(EXT);
out.Put(n.IsNonAntiAliased() == s.IsNonAntiAliased() ? NONAAS
: NONAA0 + n.IsNonAntiAliased());
}
if(o.capitals != n.capitals)
out.Put(n.capitals == s.capitals ? CAPITALSS
: CAPITALS0 + n.capitals);
if(o.dashed != n.dashed)
out.Put(n.dashed == s.dashed ? DASHEDS
: DASHED0 + n.dashed);
if(o.sscript != n.sscript) {
out.Put(SSCRIPT);
out.Put(n.sscript);
}
if(o.GetFace() != n.GetFace()) {
out.Put(FACE);
out.Put16(n.GetFace() == s.GetFace() ? 0xffff : n.GetFace());
}
if(o.GetHeight() != n.GetHeight()) {
out.Put(HEIGHT);
out.Put16(n.GetHeight() == s.GetHeight() ? 0xffff : n.GetHeight());
}
if(o.link != n.link) {
out.Put(LINK);
String s = n.link;
out % s;
}
if(o.ink != n.ink) {
out.Put(INK);
Color c = n.ink;
c.Serialize(out);
}
if(o.paper != n.paper) {
out.Put(PAPER);
Color c = n.paper;
c.Serialize(out);
}
if(o.language != n.language) {
out.Put(LANGUAGE);
out.Put32(n.language);
}
if(o.indexentry != n.indexentry) {
out.Put(INDEXENTRY);
WString s = n.indexentry;
out % s;
}
}
void RichPara::Cat(const WString& s, const RichPara::CharFormat& f)
{
part.Add();
part.Top().text = s;
part.Top().format = f;
}
void RichPara::Cat(const char *s, const CharFormat& f)
{
Cat(WString(s), f);
}
void RichPara::Cat(const RichObject& o, const RichPara::CharFormat& f)
{
part.Add();
part.Top().object = o;
part.Top().format = f;
}
void RichPara::Cat(Id field, const String& param, const RichPara::CharFormat& f)
{
Part& p = part.Add();
p.field = field;
p.fieldparam = param;
p.format = f;
VectorMap<String, Value> dummy;
FieldType *ft = fieldtype().Get(field, NULL);
if(ft)
p.fieldpart ^= ft->Evaluate(param, dummy, f);
}
struct TabLess {
bool operator () (const RichPara::Tab& a, const RichPara::Tab& b) const {
return a.pos == b.pos ? a.align < b.align : a.pos < b.pos;
}
};
void RichPara::Format::SortTabs()
{
Sort(tab, TabLess());
}
void RichPara::PackParts(Stream& out, const RichPara::CharFormat& chrstyle,
const Array<RichPara::Part>& part, CharFormat& cf,
Array<RichObject>& obj) const
{
for(int i = 0; i < part.GetCount(); i++) {
const Part& p = part[i];
Charformat(out, cf, p.format, chrstyle);
cf = p.format;
if(p.field) {
out.Put(FIELD);
String s = ~p.field;
out % s;
s = p.fieldparam;
out % s;
StringStream oout;
CharFormat subf = cf;
PackParts(oout, chrstyle, p.fieldpart, subf, obj);
s = oout;
out % s;
}
else
if(p.object) {
obj.Add(p.object);
out.Put(OBJECT);
}
else {
String x = ToUtf8(p.text);
out.Put(x);
}
}
}
String RichPara::Pack(const RichPara::Format& style, Array<RichObject>& obj) const
{
StringStream out;
dword pattr = 0;
if(format.align != style.align) pattr |= 1;
if(format.before != style.before) pattr |= 2;
if(format.lm != style.lm) pattr |= 4;
if(format.indent != style.indent) pattr |= 8;
if(format.rm != style.rm) pattr |= 0x10;
if(format.after != style.after) pattr |= 0x20;
if(format.bullet != style.bullet) pattr |= 0x40;
if(format.keep != style.keep) pattr |= 0x80;
if(format.newpage != style.newpage) pattr |= 0x100;
if(format.tabsize != style.tabsize) pattr |= 0x200;
if(!IsNull(format.label)) pattr |= 0x400;
if(format.keepnext != style.keepnext) pattr |= 0x800;
if(format.orphan != style.orphan) pattr |= 0x1000;
if(NumberingDiffers(format, style)) pattr |= 0x2000;
if(format.linespacing != style.linespacing) pattr |= 0x4000;
if(format.tab != style.tab) pattr |= 0x8000;
if(format.ruler != style.ruler) pattr |= 0x10000;
if(format.rulerink != style.rulerink) pattr |= 0x20000;
out.Put32(pattr);
if(pattr & 1) out.Put16(format.align);
if(pattr & 2) out.Put16(format.before);
if(pattr & 4) out.Put16(format.lm);
if(pattr & 8) out.Put16(format.indent);
if(pattr & 0x10) out.Put16(format.rm);
if(pattr & 0x20) out.Put16(format.after);
if(pattr & 0x40) out.Put16(format.bullet);
if(pattr & 0x80) out.Put(format.keep);
if(pattr & 0x100) out.Put(format.newpage);
if(pattr & 0x200) out.Put16(format.tabsize);
if(pattr & 0x400) { String t = format.label; out % t; }
if(pattr & 0x800) out.Put(format.keepnext);
if(pattr & 0x1000) out.Put(format.orphan);
if(pattr & 0x2000) {
String b = format.before_number, a = format.after_number;
out % b % a;
out.Put(format.reset_number);
out.Put(format.number, 8);
}
if(pattr & 0x4000)
out.Put(format.linespacing);
if(pattr & 0x8000) {
int c = 0;
int i;
for(i = 0; i < format.tab.GetCount(); i++) {
if(!IsNull(format.tab[i].pos))
c++;
}
out.Put16(c);
for(i = 0; i < format.tab.GetCount(); i++) {
const RichPara::Tab& w = format.tab[i];
if(!IsNull(w.pos)) {
out.Put32(w.pos);
out.Put(w.align);
out.Put(w.fillchar);
}
}
}
if(pattr & 0x10000)
out.Put16(format.ruler);
if(pattr & 0x20000) {
Color c = format.rulerink;
c.Serialize(out);
}
obj.Clear();
CharFormat cf = style;
if(part.GetCount())
PackParts(out, style, part, cf, obj);
else
Charformat(out, style, format, cf);
String r = out;
r.Shrink();
return r;
}
void RichPara::UnpackParts(Stream& in, const RichPara::CharFormat& chrstyle,
Array<RichPara::Part>& part, const Array<RichObject>& obj,
int& oi) {
part.Add();
part.Top().format = format;
int c;
while((c = in.Term()) >= 0)
if(c < 31 && c != 9 && c != FIELD) {
do
switch(in.Get()) {
case BOLD0:
format.NoBold();
break;
case BOLD1:
format.Bold();
break;
case BOLDS:
format.Bold(chrstyle.IsBold());
break;
case ITALIC0:
format.NoItalic();
break;
case ITALIC1:
format.Italic();
break;
case ITALICS:
format.Italic(chrstyle.IsItalic());
break;
case UNDERLINE0:
format.NoUnderline();
break;
case UNDERLINE1:
format.Underline();
break;
case UNDERLINES:
format.Underline(chrstyle.IsUnderline());
break;
case STRIKEOUT0:
format.NoStrikeout();
break;
case STRIKEOUT1:
format.Strikeout();
break;
case STRIKEOUTS:
format.Strikeout(chrstyle.IsStrikeout());
break;
case CAPITALS0:
format.capitals = false;
break;
case CAPITALS1:
format.capitals = true;
break;
case CAPITALSS:
format.capitals = chrstyle.capitals;
break;
case DASHED0:
format.dashed = false;
break;
case DASHED1:
format.dashed = true;
break;
case DASHEDS:
format.dashed = chrstyle.dashed;
break;
case SSCRIPT:
format.sscript = in.Get();
if(format.sscript == 3)
format.sscript = chrstyle.sscript;
break;
case FACE:
c = in.Get16();
format.Face(c == 0xffff ? chrstyle.GetFace() : c);
break;
case HEIGHT:
c = in.Get16();
format.Height(c == 0xffff ? chrstyle.GetHeight() : c);
break;
case LINK:
in % format.link;
break;
case INDEXENTRY:
in % format.indexentry;
break;
case INK:
in % format.ink;
break;
case PAPER:
in % format.paper;
break;
case LANGUAGE:
format.language = in.Get32();
break;
case EXT:
switch(in.Get()) {
case NONAA0:
format.NonAntiAliased(false);
break;
case NONAA1:
format.NonAntiAliased(true);
break;
case NONAAS:
format.NonAntiAliased(chrstyle.IsNonAntiAliased());
break;
}
}
while((c = in.Term()) < 31 && c != 9 && c != FIELD && c >= 0);
if(part.Top().text.GetLength())
part.Add();
part.Top().format = format;
}
else
if(in.Term() == FIELD) {
RichPara::Format pformat = format;
if(part.Top().text.GetLength()) {
part.Add();
part.Top().format = pformat;
}
in.Get();
Part& p = part.Top();
String s;
in % s;
p.field = s;
in % p.fieldparam;
String s;
in % s;
StringStream sn(s);
UnpackParts(sn, chrstyle, p.fieldpart, obj, oi);
part.Add();
part.Top().format = format = pformat;
}
else
if(in.Term() == OBJECT) {
if(part.Top().text.GetLength()) {
part.Add();
part.Top().format = format;
}
part.Top().object = obj[oi++];
part.Top().format = format;
part.Add();
part.Top().format = format;
in.Get();
}
else
part.Top().text.Cat(in.GetUtf8());
if(part.Top().text.GetLength() == 0 && part.Top().IsText())
part.Drop();
}
void RichPara::Unpack(const String& data, const Array<RichObject>& obj,
const RichPara::Format& style)
{
part.Clear();
StringStream in(data);
format = style;
dword pattr = in.Get32();
if(pattr & 1) format.align = in.Get16();
if(pattr & 2) format.before = in.Get16();
if(pattr & 4) format.lm = in.Get16();
if(pattr & 8) format.indent = in.Get16();
if(pattr & 0x10) format.rm = in.Get16();
if(pattr & 0x20) format.after = in.Get16();
if(pattr & 0x40) format.bullet = in.Get16();
if(pattr & 0x80) format.keep = in.Get();
if(pattr & 0x100) format.newpage = in.Get();
if(pattr & 0x200) format.tabsize = in.Get16();
if(pattr & 0x400) in % format.label;
if(pattr & 0x800) format.keepnext = in.Get();
if(pattr & 0x1000) format.orphan = in.Get();
if(pattr & 0x2000) {
in % format.before_number;
in % format.after_number;
format.reset_number = in.Get();
in.Get(format.number, 8);
}
if(pattr & 0x4000) {
format.linespacing = (int8)in.Get();
}
if(pattr & 0x8000) {
format.tab.Clear();
int n = in.Get16();
format.tab.Reserve(n);
for(int i = 0; i < n; i++) {
RichPara::Tab& w = format.tab.Add();
w.pos = in.Get32();
w.align = in.Get();
w.fillchar = in.Get();
}
}
if(pattr & 0x10000)
format.ruler = in.Get16();
if(pattr & 0x20000)
format.rulerink.Serialize(in);
part.Clear();
int oi = 0;
UnpackParts(in, style, part, obj, oi);
}
bool RichPara::IsEmpty() const
{
return part.GetCount() == 0 || GetLength() == 0;
}
int RichPara::GetLength() const
{
int n = 0;
for(int i = 0; i < part.GetCount(); i++)
n += part[i].GetLength();
return n;
}
WString RichPara::GetText() const
{
WString r;
for(int i = 0; i < part.GetCount(); i++)
if(part[i].IsText())
r.Cat(part[i].text);
else
r.Cat(127);
return r;
}
typedef VectorMap<Id, RichPara::FieldType *> FieldTypeMap;
GLOBAL_VAR(FieldTypeMap, fieldtype0)
const VectorMap<Id, RichPara::FieldType *>& RichPara::fieldtype()
{
return fieldtype0();
}
void RichPara::Register(Id id, FieldType& ft) init_
{
AssertST();
fieldtype0().GetAdd(id, &ft);
}
bool RichPara::EvaluateFields(VectorMap<String, Value>& vars)
{
bool b = false;
for(int i = 0; i < GetCount(); i++) {
Part& p = part[i];
if(p.field) {
FieldType *f = fieldtype().Get(p.field, NULL);
if(f) {
p.fieldpart ^= f->Evaluate(p.fieldparam, vars, p.format);
b = true;
}
}
}
return b;
}
bool RichPara::HasPos() const
{
if(!format.label.IsEmpty()) return true;
for(int i = 0; i < part.GetCount(); i++)
if(!part[i].format.indexentry.IsEmpty())
return true;
return false;
}
int RichPara::FindPart(int& pos) const
{
int pi = 0;
while(pi < part.GetCount() && pos >= part[pi].GetLength()) {
pos -= part[pi].GetLength();
pi++;
}
return pi;
}
void RichPara::Trim(int pos)
{
int i = FindPart(pos);
if(pos) {
ASSERT(part[i].IsText());
part[i].text.Trim(pos);
part.SetCount(i + 1);
}
else
part.SetCount(i);
}
void RichPara::Mid(int pos)
{
int i = FindPart(pos);
part.Remove(0, i);
if(pos) {
ASSERT(part[0].IsText());
part[0].text = part[0].text.Mid(pos);
}
}
void ApplyCharStyle(RichPara::CharFormat& format, const RichPara::CharFormat& f0,
const RichPara::CharFormat& newstyle) {
if(format.IsBold() == f0.IsBold())
format.Bold(newstyle.IsBold());
if(format.IsUnderline() == f0.IsUnderline())
format.Underline(newstyle.IsUnderline());
if(format.IsItalic() == f0.IsItalic())
format.Italic(newstyle.IsItalic());
if(format.IsStrikeout() == f0.IsStrikeout())
format.Strikeout(newstyle.IsStrikeout());
if(format.IsNonAntiAliased() == f0.IsNonAntiAliased())
format.NonAntiAliased(newstyle.IsNonAntiAliased());
if(format.capitals == f0.capitals)
format.capitals = newstyle.capitals;
if(format.dashed == f0.dashed)
format.dashed = newstyle.dashed;
if(format.sscript == f0.sscript)
format.sscript = newstyle.sscript;
if(format.GetFace() == f0.GetFace())
format.Face(newstyle.GetFace());
if(format.GetHeight() == f0.GetHeight())
format.Height(newstyle.GetHeight());
if(format.ink == f0.ink)
format.ink = newstyle.ink;
if(format.paper == f0.paper)
format.paper = newstyle.paper;
}
void RichPara::ApplyStyle(const Format& newstyle)
{
CharFormat f0 = part.GetCount() ? part[0].format : format;
for(int i = 0; i < part.GetCount(); i++)
ApplyCharStyle(part[i].format, f0, newstyle);
CharFormat h = format;
ApplyCharStyle(h, f0, newstyle);
format = newstyle;
(CharFormat&)format = h;
}
#ifdef _DEBUG
void RichPara::Dump()
{
LOG("RichPara dump" << LOG_BEGIN);
LOG("RULER: " << format.ruler << " " << format.rulerink);
LOG("BEFORE: " << format.before);
LOG("INDENT: " << format.indent);
LOG("LM: " << format.lm);
LOG("RM: " << format.rm);
LOG("AFTER: " << format.after);
LOG("KEEP: " << format.keep);
LOG("NEWPAGE: " << format.newpage);
LOG("BULLET: " << format.bullet);
int i;
for(i = 0; i < format.tab.GetCount(); i++)
LOG("TAB " << format.tab[i].pos << " : " << format.tab[i].align);
for(i = 0; i < part.GetCount(); i++)
LOG("Part[" << i << "] = \"" << part[i].text << "\" "
<< part[i].format);
LOG(LOG_END << "---------");
}
String RichPara::CharFormat::ToString() const
{
String out;
out
<< Font(*this)
<< ", ink " << DumpColor(ink);
if(!UPP::IsNull(paper))
out << ", paper " << DumpColor(paper);
switch(sscript)
{
case 0: break;
case 1: out << ", superscript"; break;
case 2: out << ", subscript"; break;
default: out << ", sscript(" << (int)sscript << ")"; break;
}
out << ", lang " << DumpLanguage(language);
if(!UPP::IsNull(link))
out << ", link " << link;
if(capitals)
out << ", capitals";
if(dashed)
out << ", dashed";
return out;
}
String RichPara::Format::ToString() const
{
String out;
if(!UPP::IsNull(label))
out << "label <" << label << ">: ";
out
<< DumpAlign(align) << ", left " << lm << ", right " << rm
<< ", indent " << indent << ", before " << before << ", after " << after
<< ", tabsize " << tabsize << ", bullet " << bullet
<< (newpage ? ", newpage" : "")
<< (keep ? ", keep" : "")
<< (keepnext ? ", keepnext" : "")
<< (orphan ? ", orphan" : "");
int i;
for(i = 0; i < tab.GetCount(); i++)
out << (i ? "\n" : ", ")
<< "tab[" << i << "] = " << tab[i].pos << ", align " << DumpAlign(tab[i].align)
<< ", fill " << FormatIntHex(tab[i].fillchar, 2);
out << "\n";
out << "before_number " << before_number << ", after_number " << after_number
<< (reset_number ? ", reset_number" : "");
for(i = 0; i < __countof(number); i++)
if(number[i] != RichPara::NUMBER_NONE)
out << " num[" << i << "] = " << (int)number[i];
out << "\n";
return out;
}
#endif
END_UPP_NAMESPACE