mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 22:02:49 -06:00
367 lines
8.9 KiB
C++
367 lines
8.9 KiB
C++
#include "RichText.h"
|
|
|
|
namespace Upp {
|
|
|
|
int RichTxt::GetWidth(const RichStyles& st) const
|
|
{
|
|
int cx = 0;
|
|
for(int i = 0; i < part.GetCount(); i++) {
|
|
if(IsPara(i)) {
|
|
RichPara p = Get(i, st, true);
|
|
RichPara::Lines pl = p.FormatLines(INT_MAX);
|
|
int ccx = 0;
|
|
Sum(ccx, ~pl.width, ~pl.width + pl.clen);
|
|
cx = max(cx, ccx);
|
|
}
|
|
else
|
|
return GetTable(i).GetWidth(st);
|
|
}
|
|
return cx;
|
|
}
|
|
|
|
void RichTxt::Sync0(const Para& pp, int parti, const RichContext& rc) const
|
|
{
|
|
int cx = rc.page.Width();
|
|
pp.ccx = cx;
|
|
RichPara p = Get(parti, *rc.styles, false);
|
|
RichPara::Lines pl = p.FormatLines(cx);
|
|
pp.ruler = p.format.ruler;
|
|
pp.before = p.format.before;
|
|
pp.linecy.Clear();
|
|
pp.linecy.SetCount(pl.GetCount());
|
|
for(int i = 0; i < pl.GetCount(); i++)
|
|
pp.linecy[i] = pl[i].Sum();
|
|
pp.cy = Sum(pp.linecy);
|
|
pp.after = p.format.after;
|
|
pp.newpage = p.format.newpage;
|
|
pp.firstonpage = p.format.firstonpage;
|
|
pp.keep = p.format.keep;
|
|
pp.keepnext = p.format.keepnext;
|
|
pp.orphan = p.format.orphan;
|
|
pp.newhdrftr = p.format.newhdrftr;
|
|
if(~pp.header_qtf != ~p.format.header_qtf) { // we compare just pointers
|
|
pp.header_qtf = p.format.header_qtf;
|
|
Upp::SetQTF(pp.header, pp.header_qtf);
|
|
}
|
|
if(~pp.footer_qtf != ~p.format.footer_qtf) { // we compare just pointers
|
|
pp.footer_qtf = p.format.footer_qtf;
|
|
Upp::SetQTF(pp.footer, pp.footer_qtf);
|
|
}
|
|
}
|
|
|
|
void RichTxt::Sync(int parti, const RichContext& rc) const {
|
|
ASSERT(part[parti].Is<Para>());
|
|
const Para& pp = part[parti].Get<Para>();
|
|
if(rc.page.Width() != pp.ccx)
|
|
Sync0(pp, parti, rc);
|
|
}
|
|
|
|
bool RichTxt::BreaksPage(PageY py, const Para& pp, int i, const Rect& page) const
|
|
{
|
|
int linecy = pp.linecy[i];
|
|
if(linecy >= page.Height()) return false;
|
|
if(linecy + py.y > page.bottom)
|
|
return true;
|
|
if(pp.orphan || pp.linecy.GetCount() < 2) return false;
|
|
if((i == 0 || i == pp.linecy.GetCount() - 2) && py.y + linecy + pp.linecy[i + 1] > page.bottom)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
void RichTxt::Advance(int parti, RichContext& rc, RichContext& begin) const
|
|
{
|
|
if(part[parti].Is<RichTable>()) {
|
|
const RichTable& tab = GetTable(parti);
|
|
if(tab.format.newhdrftr && rc.text == this)
|
|
rc.HeaderFooter(~tab.header, ~tab.footer);
|
|
if(tab.format.newpage)
|
|
rc.Page();
|
|
begin = rc;
|
|
PageY py = GetTable(parti).GetHeight(rc);
|
|
if(py.page > rc.py.page)
|
|
rc.Page(); // set new header / footer and page size
|
|
rc.py = py;
|
|
}
|
|
else {
|
|
Sync(parti, rc);
|
|
const Para& pp = part[parti].Get<Para>();
|
|
int cy = pp.before + pp.ruler;
|
|
if(pp.keep || pp.keepnext)
|
|
cy += pp.cy;
|
|
else
|
|
cy += pp.linecy[0];
|
|
if(rc.page.Height() < 30000) {
|
|
int nbefore = 0;
|
|
int nline = 0;
|
|
if(pp.keepnext && parti + 1 < part.GetCount() && part[parti + 1].Is<Para>()) {
|
|
Sync(parti + 1, rc);
|
|
const Para& p = part[parti + 1].Get<Para>();
|
|
nbefore = p.before + p.ruler;
|
|
nline = p.linecy[0];
|
|
}
|
|
if(pp.newhdrftr && rc.text == this)
|
|
rc.HeaderFooter(~pp.header, ~pp.footer);
|
|
if(pp.firstonpage && rc.py.y > rc.page.top ||
|
|
pp.newpage || rc.py.y + cy + nbefore + nline > rc.page.bottom && cy < rc.page.Height())
|
|
rc.Page();
|
|
begin = rc;
|
|
rc.py.y += pp.before + pp.ruler;
|
|
if(rc.py.y + pp.cy < rc.page.bottom)
|
|
rc.py.y += pp.cy;
|
|
else
|
|
for(int lni = 0; lni < pp.linecy.GetCount(); lni++) {
|
|
if(BreaksPage(rc.py, pp, lni, rc.page))
|
|
rc.Page();
|
|
rc.py.y += pp.linecy[lni];
|
|
}
|
|
rc.py.y += pp.after;
|
|
if(rc.py.y > rc.page.bottom)
|
|
rc.Page();
|
|
}
|
|
else {
|
|
begin = rc;
|
|
rc.py.y += pp.before + pp.cy + pp.after + pp.ruler;
|
|
}
|
|
}
|
|
}
|
|
|
|
RichContext RichTxt::GetAdvanced(int parti, const RichContext& rc, RichContext& begin) const
|
|
{
|
|
RichContext r = rc;
|
|
Advance(parti, r, begin);
|
|
return r;
|
|
}
|
|
|
|
RichContext RichTxt::GetPartContext(int parti, const RichContext& rc0) const
|
|
{
|
|
RichContext begin;
|
|
RichContext rc = rc0;
|
|
for(int i = 0; i < parti; i++)
|
|
Advance(i, rc, begin);
|
|
return rc;
|
|
}
|
|
|
|
bool IsPainting(PageDraw& pw, Zoom z, const Rect& page, PageY top, PageY bottom)
|
|
{
|
|
for(int pi = top.page; pi <= bottom.page; pi++)
|
|
if(pw.Page(pi).IsPainting(Rect(z * page.left, z * (pi == top.page ? top.y : page.top),
|
|
z * page.right, z * (pi == bottom.page ? bottom.y : page.bottom))))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
PageY RichTxt::GetHeight(RichContext rc) const
|
|
{
|
|
RichContext begin;
|
|
for(int i = 0; i < GetPartCount(); i++)
|
|
Advance(i, rc, begin);
|
|
return rc.py;
|
|
}
|
|
|
|
void RichTxt::Paint(PageDraw& pw, RichContext& rc, const PaintInfo& _pi) const
|
|
{
|
|
PaintInfo pi = _pi;
|
|
int parti = 0;
|
|
int pos = 0;
|
|
RichPara::Number n;
|
|
while(rc.py < pi.bottom && parti < part.GetCount()) {
|
|
if(part[parti].Is<RichTable>()) {
|
|
pi.tablesel--;
|
|
const RichTable& tab = GetTable(parti);
|
|
RichContext begin;
|
|
Advance(parti, rc, begin);
|
|
tab.Paint(pw, begin, pi, rc.text == this);
|
|
pi.tablesel -= tab.GetTableCount();
|
|
}
|
|
else {
|
|
const Para& pp = part[parti].Get<Para>();
|
|
if(pp.number) {
|
|
n.TestReset(*pp.number);
|
|
n.Next(*pp.number);
|
|
}
|
|
RichContext begin;
|
|
RichContext next = GetAdvanced(parti, rc, begin);
|
|
if(next.py >= pi.top) {
|
|
RichPara p = Get(parti, *rc.styles, true);
|
|
if(pi.spellingchecker) {
|
|
if(!pp.checked) {
|
|
pp.spellerrors = (*pi.spellingchecker)(p);
|
|
pp.checked = true;
|
|
}
|
|
}
|
|
else {
|
|
pp.checked = false;
|
|
pp.spellerrors.Clear();
|
|
}
|
|
if(IsPainting(pw, pi.zoom, rc.page, begin.py, next.py))
|
|
p.Paint(pw, begin, pi, n, pp.spellerrors, rc.text == this);
|
|
}
|
|
rc = next;
|
|
}
|
|
int l = GetPartLength(parti) + 1;
|
|
pi.highlightpara -= l;
|
|
pi.sell -= l;
|
|
pi.selh -= l;
|
|
pos += l;
|
|
++parti;
|
|
}
|
|
}
|
|
|
|
RichCaret RichTxt::GetCaret(int pos, RichContext rc) const
|
|
{
|
|
int parti = 0;
|
|
if(pos > GetLength())
|
|
pos = GetLength();
|
|
while(parti < part.GetCount()) {
|
|
int l = GetPartLength(parti) + 1;
|
|
RichContext begin;
|
|
Advance(parti, rc, begin);
|
|
if(pos < l) {
|
|
if(IsTable(parti))
|
|
return GetTable(parti).GetCaret(pos, begin);
|
|
else {
|
|
RichCaret tp = Get(parti, *rc.styles, true).GetCaret(pos, begin);
|
|
tp.textpage = begin.page;
|
|
return tp;
|
|
}
|
|
}
|
|
parti++;
|
|
pos -= l;
|
|
}
|
|
return RichCaret();
|
|
}
|
|
|
|
int RichTxt::GetPos(int x, PageY y, RichContext rc) const
|
|
{
|
|
int parti = 0;
|
|
int pos = 0;
|
|
|
|
if(part.GetCount()) {
|
|
while(parti < part.GetCount()) {
|
|
RichContext begin;
|
|
Advance(parti, rc, begin);
|
|
if(y < rc.py || y.page < rc.py.page) {
|
|
if(IsTable(parti))
|
|
return GetTable(parti).GetPos(x, y, begin) + pos;
|
|
else
|
|
return Get(parti, *rc.styles, true).GetPos(x, y, begin) + pos;
|
|
}
|
|
pos += GetPartLength(parti) + 1;
|
|
parti++;
|
|
}
|
|
}
|
|
|
|
return clamp(pos - 1, 0, GetLength());
|
|
}
|
|
|
|
RichHotPos RichTxt::GetHotPos(int x, PageY y, int tolerance, RichContext rc) const
|
|
{
|
|
int parti = 0;
|
|
int ti = 0;
|
|
if(part.GetCount()) {
|
|
while(parti < part.GetCount()) {
|
|
RichContext begin;
|
|
RichContext next = GetAdvanced(parti, rc, begin);
|
|
if(y < next.py || y.page < next.py.page) {
|
|
if(IsTable(parti)) {
|
|
RichHotPos pos = GetTable(parti).GetHotPos(x, y, tolerance, begin);
|
|
pos.table += ti + 1;
|
|
return pos;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
if(IsTable(parti))
|
|
ti += 1 + GetTable(parti).GetTableCount();
|
|
parti++;
|
|
rc = next;
|
|
}
|
|
}
|
|
|
|
return RichHotPos();
|
|
}
|
|
|
|
int RichTxt::GetVertMove(int pos, int gx, RichContext rc, int dir) const
|
|
{
|
|
ASSERT(dir == -1 || dir == 1);
|
|
if(GetPartCount() == 0)
|
|
return -1;
|
|
int pi;
|
|
int p = pos;
|
|
if(pos >= 0) {
|
|
pi = FindPart(p);
|
|
pos -= p;
|
|
}
|
|
else {
|
|
pi = dir > 0 ? 0 : GetPartCount() - 1;
|
|
p = -1;
|
|
pos = GetPartPos(pi);
|
|
}
|
|
while(pi < GetPartCount()) {
|
|
int q = IsTable(pi) ? GetTable(pi).GetVertMove(p, gx, rc, dir)
|
|
: Get(pi, *rc.styles, true).GetVertMove(p, gx, rc.page, dir);
|
|
if(q >= 0)
|
|
return q + pos;
|
|
if(dir > 0)
|
|
pos += GetPartLength(pi) + 1;
|
|
p = -1;
|
|
pi += dir;
|
|
if(pi < 0)
|
|
break;
|
|
if(dir < 0)
|
|
pos -= GetPartLength(pi) + 1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void RichTxt::GatherValPos(Vector<RichValPos>& f, RichContext rc, int pos, int type) const
|
|
{
|
|
int parti = 0;
|
|
while(parti < part.GetCount()) {
|
|
RichContext begin;
|
|
Advance(parti, rc, begin);
|
|
if(part[parti].Is<RichTable>())
|
|
GetTable(parti).GatherValPos(f, begin, pos, type);
|
|
else {
|
|
const Para& p = part[parti].Get<Para>();
|
|
if(p.haspos) {
|
|
if(type == LABELS)
|
|
Get(parti, *begin.styles, true).GatherLabels(f, begin, pos);
|
|
else
|
|
Get(parti, *begin.styles, true).GatherIndexes(f, begin, pos);
|
|
}
|
|
}
|
|
pos += GetPartLength(parti) + 1;
|
|
parti++;
|
|
}
|
|
}
|
|
|
|
PageY RichTxt::GetTop(RichContext rc) const
|
|
{
|
|
if(part.GetCount() == 0)
|
|
return rc.py;
|
|
if(part[0].Is<RichTable>())
|
|
return GetTable(0).GetTop(rc);
|
|
else {
|
|
Sync(0, rc);
|
|
const Para& pp = part[0].Get<Para>();
|
|
rc.py.y += pp.before + pp.ruler;
|
|
if(BreaksPage(rc.py, pp, 0, rc.page))
|
|
rc.Page();
|
|
return rc.py;
|
|
}
|
|
}
|
|
|
|
void RichTxt::ApplyZoom(Zoom z, const RichStyles& ostyle, const RichStyles& zstyle)
|
|
{
|
|
for(int i = 0; i < GetPartCount(); i++)
|
|
if(IsTable(i))
|
|
part[i].Get<RichTable>().ApplyZoom(z, ostyle, zstyle);
|
|
else {
|
|
RichPara p = Get(i, ostyle);
|
|
p.ApplyZoom(z);
|
|
Set(i, p, zstyle);
|
|
}
|
|
}
|
|
|
|
}
|