ultimatepp/uppsrc/DocTypes/DocTypes.cpp
cxl 90bf24af9c Draw: Numerous fixes
git-svn-id: svn://ultimatepp.org/upp/trunk@1473 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2009-08-04 11:50:18 +00:00

684 lines
17 KiB
C++

#include "DocTypes.h"
NAMESPACE_UPP
#define LLOG(x) // DLOG(x)
#define LDUMP(x) // DDUMP(x)
class BulletDisplay : public Display {
void Paint(Draw& w, const Rect& r, const Value& q, Color i, Color p, dword s) const {
Size sz = r.Size();
w.DrawEllipse(r.left, r.top + sz.cy / 4, sz.cx / 2, sz.cy / 2, i);
}
};
class WhiteBulletDisplay : public Display {
void Paint(Draw& w, const Rect& r, const Value& q, Color i, Color p, dword s) const {
Size sz = r.Size();
w.DrawEllipse(r.left, r.top + sz.cy / 4, sz.cx / 2, sz.cy / 2, p, sz.cx / 8, i);
}
};
class BoxBulletDisplay : public Display {
void Paint(Draw& w, const Rect& r, const Value& q, Color i, Color p, dword s) const {
Size sz = r.Size();
w.DrawRect(r.left, r.top + sz.cy / 4, sz.cx / 2, sz.cy / 2, i);
}
};
class WhiteBoxBulletDisplay : public Display {
void Paint(Draw& w, const Rect& r, const Value& q, Color i, Color p, dword s) const {
Size sz = r.Size();
w.DrawRect(r.left, r.top + sz.cy / 4, sz.cx / 2, sz.cy / 2, i);
w.DrawRect(r.left + sz.cx / 8, r.top + sz.cy / 4 + sz.cx / 8,
sz.cx / 2 - sz.cx / 4, sz.cy / 2 - sz.cx / 4, p);
}
};
static Display& sSb() { static BulletDisplay d; return d; }
static Display& sWb() { static WhiteBulletDisplay d; return d; }
static Display& sBb() { static BoxBulletDisplay d; return d; }
static Display& sWd() { static WhiteBoxBulletDisplay d; return d; }
GLOBAL_VARP(PaintRect, Paragraph::StdBullet, (sSb()))
GLOBAL_VARP(PaintRect, Paragraph::WhiteBullet, (sWb()))
GLOBAL_VARP(PaintRect, Paragraph::BoxBullet, (sBb()))
GLOBAL_VARP(PaintRect, Paragraph::WhiteBoxBullet, (sWd()))
Paragraph& Paragraph::Cat(const String& s, Font font, Color color, const Value& val, int delta) {
length += s.GetLength();
Part& n = part.Add();
n.text = s;
n.font = font;
n.color = color;
n.value = val;
n.delta = delta;
return *this;
}
Paragraph& Paragraph::Cat(const String& s, Font font, Color color, int delta) {
return Cat(s, font, color, Null, delta);
}
Paragraph& Paragraph::Cat(const char *s, Font font, Color color, int delta) {
return Cat(String(s), font, color, delta);
}
Paragraph& Paragraph::Cat(const PaintRect& pr, int cx, int cy, const Value& val, Color color) {
if(pr) {
length++;
Part& n = part.Add();
n.pr = pr;
n.sz = pr.RatioSize(cx, cy);
n.value = val;
n.color = color;
}
return *this;
}
Paragraph& Paragraph::Cat(const Paragraph& p) {
part.Append(p.part);
length += p.GetLength();
style.before = max(style.before, p.style.before);
style.after = max(style.after, p.style.after);
return *this;
}
Paragraph& Paragraph::Bullet(const PaintRect& bullet, int indent, Size size, Color color) {
style.bullet = bullet;
style.bulletindent = indent;
style.bulletsize = size;
style.bulletcolor = color;
return *this;
}
int Paragraph::GetWidth(int zoom) const {
const Part *pptr = part.Begin();
const Part *plim = part.End();
int cx = DocZoom(zoom, style.indent);
while(pptr != plim) {
if(pptr->pr)
cx += pptr->pr && !pptr->sz.cx ? pptr->pr.GetStdSize().cx
: DocZoom(zoom, pptr->sz.cx);
else {
FontInfo pf = Font(pptr->font).Height(DocZoom(zoom, pptr->font.GetHeight())).Info();
const char *s = pptr->text;
int n = pptr->text.GetLength();
while(n--) {
cx += pf[*s == 31 ? 32 : ToUnicode((byte) *s, CHARSET_DEFAULT)];
s++;
}
}
pptr++;
}
return cx + style.lm + style.rm + (style.bullet ? style.bulletindent : 0);
}
bool Paragraph::Format(ParaTypo& pfmt, int cx, int zoom) const {
int len = length + !!style.indent;
Buffer<char> chr(len);
Buffer<int> width(len);
Buffer<ParaTypo::Part *> info(len);
Buffer<ParaTypo::Part> pinfo(part.GetCount() + !!style.indent);
const Part *pptr = part.Begin();
const Part *plim = part.End();
ParaTypo::Part *pp = pinfo;
char *cp = chr;
int *wp = width;
ParaTypo::Part **ip = info;
if(!IsNull(parafont)) {
Font pf = parafont;
// pf.Height(max(1, DocZoom(zoom, parafont.GetHeight())));
int n = DocZoom(zoom, parafont.GetHeight());
pf.Height(n ? n : 1);
FontInfo f = pf.Info();
pfmt.SetMin(f.GetAscent(), f.GetDescent(), f.GetExternal());
}
/* if(!IsNull(parafont)) {
FontInfo f = w.GetFontInfo(parafont);
pfmt.SetMin(DocZoom(zoom, f.GetAscent()),
DocZoom(zoom, f.GetDescent()),
DocZoom(zoom, f.GetExternal()));
}
*/ if(style.indent) {
static Part dummy;
*cp++ = ' ';
*wp++ = DocZoom(zoom, style.indent);
pp->Set(Arial(0), Black);
pp->voidptr = &dummy;
*ip++ = pp;
pp++;
}
while(pptr != plim) {
if(pptr->pr) {
*cp++ = '@';
Size sz = pptr->pr.GetStdSize();
*wp++ = pp->width = pptr->pr && !pptr->sz.cx ? sz.cx : DocZoom(zoom, pptr->sz.cx);
*ip++ = pp;
pp->ascent = minmax(pptr->pr && !pptr->sz.cy ? sz.cy : DocZoom(zoom, pptr->sz.cy), 0, 1000);
pp->descent = 0;
pp->external = pp->overhang = 0;
pp->color = pptr->color;
}
else {
Font font = pptr->font;
font.Height(DocZoom(zoom, pptr->font.GetHeight()));
FontInfo pf = pp->Set(font, pptr->color);
const char *s = pptr->text;
int n = pptr->text.GetLength();
while(n--) {
*cp++ = *s;
*wp++ = pf[*s == 31 ? 32 : ToUnicode((byte) *s, CHARSET_DEFAULT)];
*ip++ = pp;
s++;
}
}
pp->voidptr = (void *)pptr;
pp++;
pptr++;
}
return pfmt.Format(style.align, len, chr, width, info, cx);
}
class Paragraph::ParaHeight : public ParaTypo {
protected:
virtual bool Put();
public:
int cy;
};
bool Paragraph::ParaHeight::Put() {
LDUMP(ascent);
LDUMP(descent);
LDUMP(external);
cy += ascent + descent + external;
return false;
}
int Paragraph::GetHeight(int zoom, int cx) const {
ParaHeight pp;
pp.cy = 0;
cx -= DocZoom(zoom, style.lm + style.rm + style.bulletindent);
if(cx <= 0) return 0;
Format(pp, cx, zoom);
LLOG("ParaHeight " << pp.cy);
return pp.cy;
}
class Paragraph::ParaPaint : public ParaTypo {
public:
Draw& draw;
int zoom;
int posx;
int liney;
int lm;
int ymax;
int yl, yp;
int bh;
Color paper;
void Put(int px, int py, Part *ip, Part **ibeg, Part **i, const char *text);
virtual bool PutAligned(int lm);
ParaPaint(Draw& w) : draw(w) {}
};
inline
void Paragraph::ParaPaint::Put(int px, int py, Part *ip, Part **ibeg, Part **i, const char *text) {
if(!ip) return;
const Paragraph::Part *p = (const Paragraph::Part *) ip->voidptr;
if(p->pr)
p->pr.Paint(draw, px, py - ip->ascent, ip->width, ip->ascent, ip->color, paper);
else
draw.DrawText(px, py - ip->ascent + DocZoom(zoom, p->delta),
text + (ibeg - info), ip->font, ip->color, i - ibeg);
}
bool Paragraph::ParaPaint::PutAligned(int alm) {
if(yp == 0) bh = ascent + descent;
if(liney + ascent + descent > ymax) return true;
if(yp >= yl) {
if(len) {
Part **i = info;
Part **ilim = info + len;
Part **ibeg = i;
Part *ip = *ibeg;
int *w = width;
int x = posx + alm + lm;
int px = x;
int py = liney + ascent;
while(i < ilim) {
if(*i != ip) {
Put(px, py, ip, ibeg, i, text);
px = x;
ibeg = i;
ip = *i;
}
x += *w++;
i++;
}
Put(px, py, ip, ibeg, i, text);
}
liney += ascent + descent + external;
}
yp += ascent + descent + external;
return false;
}
bool Paragraph::Paint(int zoom, Draw& w, int x, int y, int cx, int ymax, PaintInfo& pi,
Color paper) const {
ParaPaint pp(w);
int bi = style.bullet ? style.bulletindent : 0;
pp.zoom = zoom;
pp.posx = x;
pp.liney = y;
pp.ymax = ymax;
pp.lm = DocZoom(zoom, style.lm + bi);
pp.yl = pi.yl;
pp.yp = 0;
pp.paper = paper;
cx -= DocZoom(zoom, style.lm + style.rm + bi);
bool r = Format(pp, max(1, cx), zoom);
if(pi.yl == 0 && pp.yp > 0 && style.bullet)
style.bullet.Paint(w, x + DocZoom(zoom, style.lm), y,
style.bulletsize.cx ? DocZoom(zoom, style.bulletsize.cx) : pp.bh,
style.bulletsize.cy ? DocZoom(zoom, style.bulletsize.cy) : pp.bh,
style.bulletcolor, paper, 0);
pi.ypos = pp.liney;
pi.yl = pp.yp;
return r;
}
int Paragraph::Paint(int zoom, Draw& w, int x, int y, int cx, Color paper) const {
PaintInfo pi;
Paint(zoom, w, x, y, cx, INT_MAX, pi, paper);
return pi.ypos;
}
class Paragraph::ParaAngle : public ParaTypo {
public:
Draw& draw;
int posx;
int posy;
int y;
int lm;
int bh;
double cosf, sinf;
int angle;
void Put(int px, int py, Part *ip, Part **ibeg, Part **i, const char *text);
virtual bool PutAligned(int lm);
ParaAngle(Draw& w) : draw(w) {}
};
inline
void Paragraph::ParaAngle::Put(int x, int y, Part *ip, Part **ibeg, Part **i, const char *text) {
if(!ip) return;
const Paragraph::Part *p = (const Paragraph::Part *) ip->voidptr;
if(p->pr) return;
y -= ip->ascent;
draw.DrawText(int(x * cosf + y * sinf + posx),
int(-x * sinf + y * cosf + posy),
angle,
text + (ibeg - info), ip->font, ip->color, i - ibeg);
}
bool Paragraph::ParaAngle::PutAligned(int alm) {
if(y == 0) bh = ascent + descent;
if(len) {
Part **i = info;
Part **ilim = info + len;
Part **ibeg = i;
Part *ip = *ibeg;
int *w = width;
int x = alm + lm;
int px = x;
int py = ascent + y;
while(i < ilim) {
if(*i != ip) {
Put(px, py, ip, ibeg, i, text);
px = x;
ibeg = i;
ip = *i;
}
x += *w++;
i++;
}
Put(px, py, ip, ibeg, i, text);
}
y += ascent + descent + external;
return false;
}
void Paragraph::Paint(int zoom, int angle, Draw& w, int x, int y, int cx) const {
ParaAngle pp(w);
int bi = style.bullet ? style.bulletindent : 0;
double pan = 2 * M_PI * angle / 3600;
pp.sinf = sin(pan);
pp.cosf = cos(pan);
pp.angle = angle;
pp.posx = x;
pp.posy = y;
pp.y = 0;
pp.lm = DocZoom(zoom, style.lm + bi);
cx -= DocZoom(zoom, style.lm + style.rm + bi);
bool r = Format(pp, max(1, cx), zoom);
/* if(pp.yp > 0 && style.bullet)
style.bullet.Paint(w, x + DocZoom(zoom, style.lm), y, DocZoom(zoom, style.bulletindent),
pp.bh, Black, White, 0);*/
}
int Paragraph::GetHeight(int cx) const {
return GetHeight(1024, cx);
}
bool Paragraph::Paint(Draw& w, int x, int y, int cx, int ymax, PaintInfo& pi, Color paper) const {
return Paint(1024, w, x, y, cx, ymax, pi, paper);
}
int Paragraph::Paint(Draw& w, int x, int y, int cx, Color paper) const {
return Paint(1024, w, x, y, cx, paper);
}
class Paragraph::ParaRect : public ParaTypo {
public:
Draw& draw;
int liney;
int lm;
int px;
Vector<ValueRect>& vr;
void Put(int x, int y, Part *ip, Part **ibeg, Part **i, const char *text);
virtual bool PutAligned(int lm);
ParaRect(Draw& w, Vector<ValueRect>& vr) : draw(w), vr(vr) {}
};
inline
void Paragraph::ParaRect::Put(int x, int y, Part *ip, Part **ibeg, Part **i, const char *text) {
if(!ip) return;
const Paragraph::Part *p = (const Paragraph::Part *) ip->voidptr;
if(IsNull(p->value)) return;
ValueRect& r = vr.Add();
r.value = p->value;
r.rect = RectC(px + x, y, p->pr ? ip->width
: GetTextSize(text + (ibeg - info), ip->font, i - ibeg).cx,
ascent + descent + external);
}
bool Paragraph::ParaRect::PutAligned(int alm) {
if(len) {
Part **i = info;
Part **ilim = info + len;
Part **ibeg = i;
Part *ip = *ibeg;
int *w = width;
int x = alm + lm;
int px = x;
while(i < ilim) {
if(*i != ip) {
Put(px, liney, ip, ibeg, i, text);
px = x;
ibeg = i;
ip = *i;
}
x += *w++;
i++;
}
Put(px, liney, ip, ibeg, i, text);
}
liney += ascent + descent + external;
return false;
}
void Paragraph::GetValueRects(int zoom, Draw& w, int x, int& y, int cx, Vector<ValueRect>& vr)
const {
ParaRect pr(w, vr);
int bi = style.bullet ? style.bulletindent : 0;
pr.lm = DocZoom(zoom, style.lm + bi);
pr.liney = y;
pr.px = x;
cx -= DocZoom(zoom, style.lm + style.rm + bi);
Format(pr, max(1, cx), zoom);
y = pr.liney;
}
void Paragraph::Style::Init() {
align = ALIGN_LEFT;
before = lm = rm = indent = after = 0;
bulletindent = 0;
bulletsize = Size(0, 0);
bulletcolor = Black;
bullet = PaintRect();
}
void Paragraph::Clear() {
part.Clear();
style.Init();
length = 0;
parafont = Null;
}
Paragraph::Paragraph(const Paragraph& p, int) : part(p.part, 1) {
style = p.style;
length = p.length;
parafont = p.parafont;
}
Paragraph::Paragraph() {
length = 0;
style.Init();
parafont = Null;
}
Table& Document::AddTable() {
Item& m = item.Add();
m.type = TABLE;
return m.data;
}
Paragraph& Document::AddParagraph() {
Item& m = item.Add();
m.type = PARAGRAPH;
Table& tab = m.data;
tab.AddRow();
return tab.AddCell().Par();
}
void Document::Cat(const Paragraph& p) {
if(item.GetCount() && item.Top().type == PARAGRAPH)
item.Top().data.LastCell().Par().Cat(p);
else
AddParagraph() <<= p;
}
int Document::GetHeight(int zoom, int cx) const {
int cy = 0;
for(int i = 0; i < item.GetCount(); i++) {
const Item& m = item[i];
if(m.type == PARAGRAPH) {
const Paragraph& p = m.data.LastCell().Par();
cy += DocZoom(zoom, p.GetBefore());
cy += p.GetHeight(zoom, cx);
cy += DocZoom(zoom, p.GetAfter());
}
else {
cy += DocZoom(zoom, m.data.GetBefore());
cy += m.data.GetHeight(zoom, cx);
cy += DocZoom(zoom, m.data.GetAfter());
}
}
return cy;
}
int Document::GetWidth(int zoom) const {
int cx = 0;
for(int i = 0; i < item.GetCount(); i++) {
const Item& m = item[i];
if(m.type == PARAGRAPH)
cx = max(cx, m.data.LastCell().Par().GetWidth(zoom));
else
cx = max(cx, m.data.GetWidth(zoom));
}
return cx;
}
int Document::Paint(int zoom, Draw& w, int x, int y, int cx, Color paper) const {
for(int i = 0; i < item.GetCount(); i++) {
const Item& m = item[i];
if(m.type == PARAGRAPH) {
const Paragraph& p = m.data.LastCell().Par();
y += DocZoom(zoom, p.GetBefore());
y = p.Paint(zoom, w, x, y, cx, paper);
y += DocZoom(zoom, p.GetAfter());
}
else {
y += DocZoom(zoom, m.data.GetBefore());
y = m.data.Paint(zoom, w, x, y, cx);
y += DocZoom(zoom, m.data.GetAfter());
}
}
return y;
}
Vector<ValueRect> Document::GetValueRects(int zoom, Draw& w, int x, int y, int cx) const {
Vector<ValueRect> vr;
for(int i = 0; i < item.GetCount(); i++) {
const Item& m = item[i];
if(m.type == PARAGRAPH) {
const Paragraph& p = m.data.LastCell().Par();
y += DocZoom(zoom, p.GetBefore());
p.GetValueRects(zoom, w, 0, y, cx, vr);
y += DocZoom(zoom, p.GetAfter());
}
else {
y += DocZoom(zoom, m.data.GetBefore());
m.data.GetValueRects(zoom, w, x, y, cx, vr);
y += DocZoom(zoom, m.data.GetAfter());
}
}
return vr;
}
int Document::GetHeight(int zoom, int i, int cx, Document::Cache& cache) const {
if(cache.cx == cx && cache.zoom == zoom && i < cache.cy.GetCount() && !IsNull(cache.cy[i]))
return cache.cy[i];
int cy;
const Item& m = item[i];
if(m.type == PARAGRAPH) {
const Paragraph& p = m.data.LastCell().Par();
cy = DocZoom(zoom, p.GetBefore());
cy += p.GetHeight(zoom, cx);
cy += DocZoom(zoom, p.GetAfter());
}
else {
cy = DocZoom(zoom, m.data.GetBefore());
cy += m.data.GetHeight(zoom, cx);
cy += DocZoom(zoom, m.data.GetAfter());
}
if(cache.cx != cx || cache.zoom != zoom) {
cache.Clear();
cache.cx = cx;
cache.zoom = zoom;
}
cache.cy.DoIndex(i, Null) = cy;
return cy;
}
int Document::GetHeight(int zoom, int cx, Cache& cache) const {
int cy = 0;
for(int i = 0; i < item.GetCount(); i++)
cy += GetHeight(zoom, i, cx, cache);
return cy;
}
void Document::Paint(int zoom, Draw& w, int x, int y, int cx, int cy, int y0,
Cache& cache, Color paper) const {
int yp = 0;
for(int i = 0; i < item.GetCount() && yp < y0 + cy; i++) {
const Item& m = item[i];
int h = GetHeight(zoom, i, cx, cache);
int yo = yp + y - y0;
if((yp += h) <= y0)
continue;
if(m.type == PARAGRAPH) {
const Paragraph& p = m.data.LastCell().Par();
p.Paint(zoom, w, x, yo + DocZoom(zoom, p.GetBefore()), cx, paper);
}
else
m.data.Paint(zoom, w, x, yo + DocZoom(zoom, m.data.GetBefore()), cx);
}
}
void Document::Qtf0(const char *s, bool append, byte charset) {
QtfParser parser(s);
for(;;) {
switch(parser.Parse()) {
case QtfParser::PARAGRAPH:
if(append)
Cat(parser.GetParagraph());
else
AddParagraph() = parser.PickParagraph();
break;
case QtfParser::TABLE:
AddTable() = parser.PickTable();
break;
case QtfParser::END:
return;
}
append = false;
}
}
Document& Document::operator=(const char *s) {
Clear();
Qtf(s);
return *this;
}
Document& Document::operator|=(const char *s) {
AppendQtf(s, true);
return *this;
}
Document::Document(const Document& p, int) : item(p.item, 1) {}
Document::Document() {}
Document::Document(const char *qtf) {
Qtf(qtf);
}
void DocOut::Qtf(const char *s) {
QtfParser parser(s);
for(;;) {
switch(parser.Parse()) {
case QtfParser::PARAGRAPH:
Put(parser.GetParagraph());
break;
case QtfParser::TABLE:
Put(parser.GetTable());
break;
case QtfParser::PAGE:
Page();
break;
case QtfParser::END:
return;
}
}
}
void DocOut::Put(const Document& doc) {
for(int i = 0; i < doc.GetCount(); i++)
if(doc.IsParagraph(i))
Put(doc.GetParagraph(i));
else
Put(doc.GetTable(i));
}
END_UPP_NAMESPACE