ultimatepp/uppsrc/RichText/ParaType.cpp

458 lines
10 KiB
C++

#include "RichText.h"
namespace Upp {
void RichPara::Smh(Lines& lines, HeightInfo *th, int cx) const
{
Line& l = lines.line.Top();
l.ascent = l.descent = l.external = 0;
const HeightInfo *he = th + l.pos + l.len;
for(const HeightInfo *h = th + l.pos; h < he; h++) {
if(h->ascent > l.ascent) l.ascent = h->ascent;
if(h->descent > l.descent) l.descent = h->descent;
if(h->external > l.external) l.external = h->external;
}
if(format.linespacing == LSP115) {
l.ascent = 115 * l.ascent / 100;
l.descent = 115 * l.descent / 100;
}
if(format.linespacing == LSP15) {
l.ascent = (3 * l.ascent) >> 1;
l.descent = (3 * l.descent) >> 1;
}
if(format.linespacing == LSP20) {
l.ascent = 2 * l.ascent;
l.descent = 2 * l.descent;
}
l.xpos = format.lm;
cx -= format.lm + format.rm;
l.xpos += lines.GetCount() == 1 ? lines.first_indent : lines.next_indent;
if(!l.withtabs && cx != INT_MAX)
switch(format.align) {
case ALIGN_RIGHT:
l.xpos += cx - l.cx;
break;
case ALIGN_CENTER:
l.xpos += (cx - l.cx) / 2;
break;
}
l.cx -= lines.GetCount() == 1 ? lines.first_indent : lines.next_indent;
}
RichPara::Tab RichPara::GetNextTab(int pos, int cx) const
{
int tabi = -1;
int dist = INT_MAX;
for(int i = 0; i < format.tab.GetCount(); i++) {
const Tab& tab = format.tab[i];
int tabpos = tab.pos;
if(tabpos & TAB_RIGHTPOS)
tabpos = cx - (tabpos & ~TAB_RIGHTPOS);
if(tabpos > pos && tabpos - pos < dist) {
tabi = i;
dist = tabpos - pos;
}
}
if(format.bullet == BULLET_TEXT) {
int q = format.indent + format.lm;
if(q > pos && q - pos < dist) {
Tab tab;
tab.align = ALIGN_LEFT;
tab.pos = q;
return tab;
}
}
if(tabi < 0) {
Tab tab;
tab.pos = format.tabsize ? (pos + format.tabsize) / format.tabsize * format.tabsize : 0;
tab.align = ALIGN_LEFT;
return tab;
}
Tab tab = format.tab[tabi];
if(tab.pos & TAB_RIGHTPOS)
tab.pos = cx - (tab.pos & ~TAB_RIGHTPOS);
return tab;
}
struct RichPara::StorePart {
wchar *t;
int *w;
int *p;
const CharFormat **f;
HeightInfo *h;
int pos;
Font font;
void Store(Lines& lines, const Part& p, int pinc);
};
void RichPara::StorePart::Store(Lines& lines, const Part& part, int pinc)
{
CharFormat& pfmt = lines.hformat.Add();
pfmt = part.format;
if(part.field && pinc) {
for(int i = 0; i < part.fieldpart.GetCount(); i++)
Store(lines, part.fieldpart[i], 0);
pos++;
}
else
if(part.object) {
*f++ = &pfmt;
Size sz = part.object.GetSize();
*w++ = sz.cx;
h->ydelta = part.object.GetYDelta();
h->ascent = sz.cy - h->ydelta;
h->descent = max(h->ydelta, 0);
h->external = 0;
lines.object.Add(part.object);
h->object = &lines.object.Top();
h++;
*t++ = 'x';
*p++ = pos;
pos += pinc;
}
else {
const wchar *s = part.text;
const wchar *lim = part.text.End();
Font fnt = part.format;
Font fi = fnt;
Font wf = fnt;
if(part.format.sscript) {
fnt.Height(fnt.GetHeight() * 3 / 5);
wf = fnt;
}
if(part.format.capitals) {
CharFormat& cfmt = lines.hformat.Add();
cfmt = part.format;
cfmt.Height(cfmt.GetHeight() * 4 / 5);
Font cwf = cfmt;
if(part.format.sscript) {
Font fnt = cfmt;
fnt.Height(fnt.GetHeight() * 3 / 5);
cwf = fnt;
}
while(s < lim) {
wchar c = *s++;
if(c == 9) {
*f++ = &pfmt;
h->ascent = font.GetAscent();
h->descent = font.GetDescent();
h->external = font.GetExternal();
*w++ = 0;
}
else
if(IsLower(c)) {
*f++ = &cfmt;
c = (wchar)ToUpper(c);
h->ascent = cfmt.GetAscent();
h->descent = cfmt.GetDescent();
h->external = cfmt.GetExternal();
*w++ = c >= 32 ? cwf[c] : 0;
}
else {
*f++ = &pfmt;
h->ascent = fi.GetAscent();
h->descent = fi.GetDescent();
h->external = fi.GetExternal();
*w++ = c >= 32 ? wf[c] : 0;
}
h->object = NULL;
*t++ = c;
*p++ = pos;
pos += pinc;
h++;
}
}
else {
while(s < lim) {
wchar c = *s++;
*f++ = &pfmt;
if(c == 9) {
h->ascent = font.GetAscent();
h->descent = font.GetDescent();
h->external = font.GetExternal();
}
else {
h->ascent = fi.GetAscent();
h->descent = fi.GetDescent();
h->external = fi.GetExternal();
}
h->object = NULL;
*p++ = pos;
pos += pinc;
h++;
*w++ = c >= 32 ? wf[c] : 0;
*t++ = c;
}
}
}
}
static int CountChars(const Array<RichPara::Part>& part)
{
int n = 0;
for(int i = 0; i < part.GetCount(); i++) {
const RichPara::Part& p = part[i];
if(p.field)
n += CountChars(p.fieldpart);
else
n += p.GetLength();
}
return n;
}
RichPara::Lines::Lines()
{
justified = false;
incache = false;
cacheid = 0;
number_chars = 0;
}
Array<RichPara::Lines>& RichPara::Lines::Cache()
{
static Array<Lines> x;
return x;
}
RichPara::Lines::~Lines()
{
if(cacheid && line.GetCount() && !incache) {
Mutex::Lock __(cache_lock);
Array<Lines>& cache = Cache();
incache = true;
cache.Insert(0) = pick(*this);
// cache.SetCount(1);
int total = 0;
for(int i = 1; i < cache.GetCount(); i++) {
total += cache[i].clen;
if(total > 10000 || i > 64) {
cache.SetCount(i);
break;
}
}
}
}
RichPara::Lines RichPara::FormatLines(int acx, const Number& n) const
{
Lines lines;
if(cacheid) {
Mutex::Lock __(cache_lock);
Array<Lines>& cache = Lines::Cache();
for(int i = 0; i < cache.GetCount(); i++)
if(cache[i].cacheid == cacheid && cache[i].cx == acx) {
lines = pick(cache[i]);
lines.incache = false;
cache.Remove(i);
return lines;
}
}
Number nn = n;
nn.Next(format);
WString number = nn.AsText(format).ToWString();
if(number.GetCount())
number.Cat(' ');
int i;
lines.cacheid = cacheid;
lines.cx = acx;
lines.len = GetLength();
lines.clen = CountChars(part) + number.GetCount();
lines.first_indent = lines.next_indent = format.indent;
if(format.bullet == BULLET_TEXT)
lines.first_indent = 0;
else
if(!format.bullet && !format.IsNumbered())
lines.next_indent = 0;
if(lines.clen == 0) {
Line& l = lines.line.Add();
l.pos = 0;
l.ppos = 0;
l.plen = 0;
l.len = 0;
l.cx = lines.first_indent;
l.withtabs = false;
HeightInfo dummy;
Smh(lines, &dummy, lines.cx);
l.ascent = format.GetAscent();
l.descent = format.GetDescent();
l.external = format.GetExternal();
return lines;
}
lines.text.Alloc(lines.clen);
lines.width.Alloc(lines.clen);
lines.pos.Alloc(lines.clen);
lines.format.Alloc(lines.clen);
lines.height.Alloc(lines.clen);
StorePart sp;
sp.t = lines.text;
sp.w = lines.width;
sp.p = lines.pos;
sp.f = lines.format;
sp.h = lines.height;
sp.font = format;
sp.pos = 0;
if(number.GetCount()) {
Part np;
np.text = number;
np.format = part.GetCount() ? part[0].format : format;
np.format.indexentry.Clear();
np.format.link.Clear();
sp.Store(lines, np, 0);
lines.number_chars = number.GetCount();
}
for(i = 0; i < part.GetCount(); i++)
sp.Store(lines, part[i], 1);
wchar *s = lines.text;
wchar *text = s;
wchar *end = lines.text + lines.clen;
wchar *space = NULL;
int *w = lines.width;
int cx = lines.first_indent;
int rcx = lines.cx - format.lm - format.rm;
bool withtabs = false;
int scx = cx;
while(s < end) {
Tab t;
if(*s == ' ') {
space = s;
scx = cx;
}
else {
if(*s == '\t') {
t = GetNextTab(cx + format.lm, rcx);
space = NULL;
}
if(*s == '\n' || // fieldparts with newline
cx + *w > rcx && s > text ||
*s == '\t' && (t.align == ALIGN_RIGHT ? t.pos - format.lm > rcx
: t.pos - format.lm >= rcx)) {
if(*s == '\n') // fieldparts with newline
space = NULL;
Line& l = lines.line.Add();
l.withtabs = withtabs;
l.pos = (int)(text - lines.text);
if(space) {
l.len = (int)(space - text) + 1;
l.cx = scx;
text = s = space + 1;
}
else {
l.len = (int)(s - text);
l.cx = cx;
text = s;
}
Smh(lines, lines.height, lines.cx);
cx = lines.next_indent;
w = text - ~lines.text + lines.width;
space = NULL;
rcx = lines.cx - format.lm - format.rm;
withtabs = false;
t = GetNextTab(cx + format.lm, acx);
}
}
if(*s == '\t') {
*s += t.fillchar;
if(t.align == ALIGN_LEFT) {
*w++ = t.pos - format.lm - cx;
cx = t.pos - format.lm;
}
else {
int tcx = 0;
int *tw = w + 1;
for(wchar *ts = s + 1; ts < end && *ts != '\t'; ts++)
tcx += *tw++;
int ww = t.pos - format.lm - cx - (t.align == ALIGN_RIGHT ? tcx : tcx / 2);
if(ww > 0) {
*w++ = ww;
cx += ww;
}
else
*w++ = 0;
}
withtabs = true;
}
else
cx += *w++;
s++;
}
Line& l = lines.line.Add();
l.withtabs = withtabs;
l.pos = (int)(text - lines.text);
l.len = (int)(s - text);
l.cx = cx;
Smh(lines, lines.height, lines.cx);
for(i = 0; i < lines.line.GetCount(); i++) {
Line& l = lines.line[i];
l.ppos = lines.pos[l.pos];
l.plen = (l.pos + l.len < lines.clen ? lines.pos[l.pos + l.len] : lines.len) - l.ppos;
}
return lines;
}
void RichPara::Lines::Justify(const RichPara::Format& format)
{
if(justified)
return;
justified = true;
if(format.align != ALIGN_JUSTIFY) return;
for(int i = 0; i < line.GetCount() - 1; i++) {
const Line& li = line[i];
if(!li.withtabs && li.len) {
const wchar *s = ~text + li.pos;
const wchar *lim = s + li.len;
while(lim - 1 > s) {
if(*(lim - 1) != ' ') break;
lim--;
}
while(s < lim) {
if(*s != ' ' && *s != 160) break;
s++;
}
const wchar *beg = s;
int nspc = 0;
while(s < lim) {
if(*s == ' ' || *s == 160) nspc++;
s++;
}
s = beg;
if(nspc) {
int q = ((cx - format.lm - format.rm -
(i == 0 ? first_indent : next_indent) - li.cx) << 16)
/ nspc;
int *w = beg - ~text + width;
int prec = 0;
while(s < lim) {
if(*s == ' ' || *s == 160) {
*w += (prec + q) >> 16;
prec = (prec + q) & 0xffff;
}
w++;
s++;
}
}
}
}
}
int RichPara::Lines::BodyHeight() const
{
int sum = 0;
for(int i = 0; i < line.GetCount(); i++)
sum += line[i].Sum();
return sum;
}
}