ultimatepp/uppsrc/CtrlLib/RichTextView.cpp
2023-01-13 16:33:29 +01:00

553 lines
11 KiB
C++

#include "CtrlLib.h"
namespace Upp {
#define LLOG(x) // LOG(x)
Zoom RichTextView::GetZoom() const
{
int szcx = GetSize().cx;
if(!sb.IsShown() && sb.IsAutoHide())
szcx -= ScrollBarSize();
return IsNull(zoom) ? Zoom(szcx - margin.left - margin.right, cx) : zoom;
}
int RichTextView::GetPageCx(bool reduced) const
{
int szcx = GetSize().cx;
if(reduced && !sb.IsShown() && sb.IsAutoHide())
szcx -= ScrollBarSize();
return IsNull(zoom) ? cx : (szcx - margin.left - margin.right) / zoom;
}
Rect RichTextView::GetPage() const
{
return Rect(0, 0, GetPageCx(), INT_MAX);
}
int RichTextView::GetCy() const
{
return GetZoom() * text.GetHeight(GetPage()).y;
}
int RichTextView::TopY() const
{
if(vcenter && sb.GetTotal() < sb.GetPage())
return (sb.GetPage() - sb.GetTotal()) / 2;
return 0;
}
void RichTextView::Paint(Draw& w)
{
Size sz = GetSize();
w.DrawRect(sz, background);
sz.cx -= margin.left + margin.right;
sz.cy -= margin.top + margin.bottom;
w.Clipoff(margin.left, margin.top, sz.cx, sz.cy);
PaintInfo pi;
if(!hldec)
pi.hyperlink = Null;
if(sell < selh) {
pi.sell = sell;
pi.selh = selh;
}
pi.indexentry = Null;
pi.highlightpara = highlight;
pi.WhenHighlight = WhenHighlight;
pi.highlight = highlight_color;
pi.zoom = GetZoom();
pi.textcolor = textcolor;
int q = sb * pi.zoom;
scroller.Set(q);
w.Offset(0, -q);
SimplePageDraw pw(w);
pi.top = PageY(0, sb);
pi.bottom = PageY(0, sb + sz.cy / pi.zoom);
pi.usecache = true;
pi.sizetracking = sizetracking;
pi.shrink_oversized_objects = shrink_oversized_objects;
pi.darktheme = IsDarkTheme();
Rect pg = GetPage();
pg.top = TopY();
text.Paint(pw, pg, pi);
w.End();
w.End();
}
void RichTextView::SetSb()
{
sb.SetTotal(text.GetHeight(GetPage()).y);
sb.SetPage((GetSize().cy - margin.top - margin.bottom) / GetZoom());
}
bool RichTextView::Key(dword key, int count)
{
if(key == K_CTRL_C || key == K_SHIFT_INSERT) {
Copy();
return true;
}
return sb.VertKey(key);
}
void RichTextView::MouseWheel(Point p, int zdelta, dword keyflags)
{
sb.Wheel(zdelta);
}
Image RichTextView::CursorImage(Point p, dword keyflags)
{
int pos = GetPointPos(p);
if(WhenLink && pos >= 0 && !IsNull(GetLink(pos, p)))
return Image::Hand();
if(HasCapture() && icursor)
return Image::IBeam();
return Image::Arrow();
}
WString RichTextView::GetSelText() const
{
if(anchor == cursor)
return text.GetPlainText();
else {
WString h = text.GetPlainText(false).Mid(sell, selh - sell);
WString r;
for(const wchar *s = ~h; s < h.End(); s++) {
if(*s == '\n')
r.Cat('\r');
r.Cat(*s);
}
return r;
}
}
void RichTextView::Copy()
{
RefreshSel();
WriteClipboardUnicodeText(GetSelText());
}
String RichTextView::GetSelectionData(const String& fmt) const
{
return GetTextClip(GetSelText(), fmt);
}
void RichTextView::RightDown(Point p, dword keyflags)
{
MenuBar b;
b.Add(cursor != anchor, t_("Copy"), CtrlImg::copy(), THISBACK(Copy)).Key(K_CTRL_C);
b.Execute();
}
Point RichTextView::GetTextPoint(Point p) const
{
p -= margin.TopLeft();
Zoom zoom = GetZoom();
p.y += sb * zoom;
return Point(p.x / zoom, p.y / zoom - TopY());
}
int RichTextView::GetPointPos(Point p) const
{
Size sz = GetSize();
sz.cx -= margin.left + margin.right;
sz.cy -= margin.top + margin.bottom;
p = GetTextPoint(p);
return text.GetPos(p.x, PageY(0, p.y), GetPage());
}
String RichTextView::GetLink(int pos, Point p) const
{
String link;
RichObject object = text.GetRichPos(pos).object;
if(object) {
Rect rc = text.GetCaret(pos, GetPage());
//TODO: Perhaps use GetTextPoint here?
link = object.GetLink(p - rc.TopLeft(), rc.Size());
}
if(IsNull(link)) {
RichPos richpos = text.GetRichPos(pos);
Rect rc = text.GetCaret(pos, GetPage());
if(richpos.chr != '\n' && rc.Contains(GetTextPoint(p)))
link = Nvl(richpos.fieldformat.link, richpos.format.link);
}
return link;
}
void RichTextView::RefreshRange(int a, int b)
{
int l = max(min(a, b) - 1, 0); // Extend the range to cover 'weird' cases (line break)
int h = min(max(a, b) + 1, GetLength());
if(l == h)
return;
Rect r1 = text.GetCaret(l, GetPage()) + margin.TopLeft();
Rect r2 = text.GetCaret(h, GetPage()) + margin.TopLeft();
Zoom zoom = GetZoom();
Refresh(Rect(0, zoom * (r1.top - sb + TopY()), GetSize().cx, zoom * (r2.bottom - sb + zoom.d - 1) + TopY()));
}
void RichTextView::RefreshSel()
{
int l = minmax(min(cursor, anchor), 0, text.GetLength());
int h = minmax(max(cursor, anchor), 0, text.GetLength());
if(sell == l && selh == h || sell == selh && l == h)
return;
RichPos pl = text.GetRichPos(l);
RichPos ph = text.GetRichPos(h);
RichPos psell = text.GetRichPos(sell);
RichPos pselh = text.GetRichPos(selh);
if(psell.parai != pl.parai || pselh.parai != ph.parai ||
psell.table != pl.table || pselh.table != ph.table ||
psell.cell != pl.cell || pselh.cell != ph.cell)
Refresh();
else {
RefreshRange(l, sell);
RefreshRange(h, selh);
}
sell = l;
selh = h;
if(IsSelection())
SetSelectionSource(ClipFmtsText());
}
void RichTextView::ClearSelection()
{
if(IsSelection()) {
anchor = cursor;
RefreshSel();
}
}
void RichTextView::LeftDown(Point p, dword keyflags)
{
int pos = GetPointPos(p);
if(pos < 0) {
cursor = anchor = 0;
return;
}
String link = GetLink(pos, p);
if(!IsNull(link))
WhenLink(link);
else {
cursor = pos;
if(!(keyflags & K_SHIFT))
anchor = pos;
RefreshSel();
SetFocus();
SetCapture();
WhenLeftClick();
}
}
void RichTextView::LeftDouble(Point p, dword keyflags)
{
int pos = GetPointPos(p);
if(IsLeNum(text[pos])) {
anchor = pos;
while(anchor > 0 && IsLeNum(text[anchor - 1]))
anchor--;
cursor = pos;
while(cursor < text.GetLength() && IsLeNum(text[cursor]))
cursor++;
while(cursor < text.GetLength() && text[cursor] == ' ')
cursor++;
RefreshSel();
SetFocus();
}
}
void RichTextView::LeftTriple(Point p, dword keyflags)
{
int pos = GetPointPos(p);
RichPos rp = text.GetRichPos(pos);
anchor = pos - rp.posinpara;
cursor = anchor + rp.paralen + 1;
RefreshSel();
SetFocus();
}
void RichTextView::MouseMove(Point p, dword keyflags)
{
int pos = GetPointPos(p);
WhenMouseMove(pos);
if(HasCapture()) {
if(pos < 0)
return;
cursor = pos;
Rect r1 = text.GetCaret(cursor, GetPage());
sb.ScrollInto(r1.top, r1.Height());
RefreshSel();
}
}
void RichTextView::LeftRepeat(Point p, dword keyflags)
{
MouseMove(p, keyflags);
}
void RichTextView::EndSizeTracking()
{
sizetracking = false;
Refresh();
}
void RichTextView::Layout()
{
sizetracking = false;
if(IsOpen() && lazy) {
sizetracking = true;
KillTimeCallback(TIMEID_ENDSIZETRACKING);
SetTimeCallback(250, THISBACK(EndSizeTracking), TIMEID_ENDSIZETRACKING);
}
SetSb();
Refresh();
}
Value RichTextView::GetData() const
{
if(text.IsEmpty()) return Value();
return GetQTF();
}
void RichTextView::SetData(const Value& v)
{
SetQTF(String(v));
}
void RichTextView::Scroll()
{
scroller.Scroll(*this, Rect(GetSize()).Deflated(margin), sb * GetZoom());
}
bool RichTextView::GotoLabel(Gate<const WString&> match, bool dohighlight, bool find_last)
{
Vector<RichValPos> f = text.GetValPos(GetPage(), RichText::LABELS);
highlight = Null;
bool ret = false;
for(int i = 0; i < f.GetCount(); i++) {
if(match(f[i].data)) {
if(dohighlight)
highlight = f[i].pos;
else
sb = f[i].py.y;
Refresh();
if(!find_last)
return true;
ret = true;
}
}
return ret;
}
bool RichTextView::GotoLabel(const String& lbl, bool dohighlight, bool find_last)
{
WString lw = lbl.ToWString();
return GotoLabel([&](const WString& data) { return data == lw; }, dohighlight, find_last);
}
void RichTextView::Clear()
{
sb = 0;
text.Clear();
SetSb();
Refresh();
anchor = cursor = sell = selh = 0;
}
void RichTextView::Pick(RichText&& rt)
{
sb = 0;
anchor = cursor = sell = selh = 0;
text = pick(rt);
SetSb();
UpdateRefresh();
highlight = -1;
}
void RichTextView::Pick(RichText&& txt, Zoom z) {
if(z.m != z.d)
const_cast<RichText&>(txt).ApplyZoom(z);
Pick(pick(txt));
sb.SetLine(z * 100);
}
void RichTextView::SetQTF(const char *qtf, Zoom z)
{
Pick(ParseQTF(qtf), z);
}
RichTextView& RichTextView::PageWidth(int _cx)
{
cx = _cx;
sb = 0;
SetSb();
Refresh();
return *this;
}
RichTextView& RichTextView::SetZoom(Zoom z)
{
zoom = z;
sb = 0;
SetSb();
Refresh();
return *this;
}
RichTextView& RichTextView::Background(Color c)
{
background = c;
Transparent(IsNull(c));
Refresh();
return *this;
}
RichTextView& RichTextView::TextColor(Color _color)
{
textcolor = _color;
Refresh();
return *this;
}
RichTextView& RichTextView::Highlight(Color _color)
{
highlight_color = _color;
Refresh();
return *this;
}
RichTextView& RichTextView::VCenter(bool b)
{
vcenter = b;
return *this;
}
RichTextView& RichTextView::Margins(const Rect& m)
{
margin = m;
Refresh();
return *this;
}
RichTextView& RichTextView::HMargins(int a)
{
margin.left = margin.right = a;
Refresh();
return *this;
}
RichTextView& RichTextView::VMargins(int a)
{
margin.top = margin.bottom = a;
Refresh();
return *this;
}
RichTextView& RichTextView::Margins(int a)
{
margin.Set(a, a, a, a);
Refresh();
return *this;
}
void LinkInRichTextClipboard__();
RichTextView::RichTextView()
{
cx = 3968;
sizetracking = false;
sb.SetLine(100);
sb.WhenScroll = THISBACK(Scroll);
zoom = Null;
background = SColorPaper;
textcolor = Null;
vcenter = false;
margin = Rect(0, 0, 0, 0);
highlight = -1;
hldec = true;
WhenLink = callback(LaunchWebBrowser);
anchor = cursor = sell = selh = 0;
SetFrame(ViewFrame());
AddFrame(sb);
NoWantFocus();
lazy = true;
shrink_oversized_objects = true;
}
RichTextView::~RichTextView() {}
void RichTextCtrl::SetData(const Value& v)
{
SetQTF(String(v));
}
RichTextCtrl::RichTextCtrl()
{
SetZoom(Zoom(1, 1));
Transparent();
Background(Null);
SetFrame(NullFrame());
AutoHideSb();
}
#ifndef PLATFORM_PDA
void Print(Draw& w, const RichText& text, const Rect& page, const Vector<int>& pg)
{
LLOG("Print");
int lpage = text.GetHeight(page).page;
PrintPageDraw pw(w);
Size sz = w.GetPageMMs();
Size pgsz = page.Size();
int x = (6000 * sz.cx / 254 - pgsz.cx) / 2;
int y = (6000 * sz.cy / 254 - pgsz.cy) / 2;
for(int pi = 0; pi < pg.GetCount(); pi++) {
int i = pg[pi];
w.StartPage();
w.Offset(x, y);
pw.SetPage(i);
PaintInfo paintinfo;
paintinfo.top = PageY(i, 0);
paintinfo.bottom = PageY(i + 1, 0);
paintinfo.indexentry = Null;
if(text.IsPrintNoLinks())
paintinfo.hyperlink = Null;
text.Paint(pw, page, paintinfo);
w.End();
String footer = text.GetFooter();
if(!IsNull(footer) && lpage) {
String n = Format(footer, i + 1, lpage + 1);
Size nsz = GetTextSize(n, Arial(90).Italic());
pw.Page(i).DrawText(
x + pgsz.cx - nsz.cx, y + pgsz.cy + 100,
n, Arial(90).Italic());
}
w.EndPage();
}
}
void Print(Draw& w, const RichText& text, const Rect& page)
{
int n = text.GetHeight(page).page;
Vector<int> pg;
for(int i = 0; i <= n; i++)
pg.Add(i);
Print(w, text, page, pg);
}
bool Print(const RichText& text, const Rect& page, int currentpage, const char *name)
{
PrinterJob pj(name);
pj.CurrentPage(currentpage);
pj.PageCount(text.GetHeight(page).page + 1);
pj.Landscape(page.GetWidth() > page.GetHeight());
if(pj.Execute()) {
Print(pj, text, page, pj.GetPages());
return true;
}
return false;
}
#endif
}