#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); if(pl.GetCount()) cx = max(cx, pl[0].xpos + pl[0].cx); } else cx = max(cx, 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()); const Para& pp = part[parti].Get(); 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()) { 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(); 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()) { Sync(parti + 1, rc); const Para& p = part[parti + 1].Get(); 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; RichPara::Number n; while(rc.py < pi.bottom && parti < part.GetCount()) { if(part[parti].Is()) { 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(); 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; ++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& 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()) GetTable(parti).GatherValPos(f, begin, pos, type); else { const Para& p = part[parti].Get(); 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()) return GetTable(0).GetTop(rc); else { Sync(0, rc); const Para& pp = part[0].Get(); 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().ApplyZoom(z, ostyle, zstyle); else { RichPara p = Get(i, ostyle); p.ApplyZoom(z); Set(i, p, zstyle); } } }