mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 14:16:07 -06:00
553 lines
11 KiB
C++
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
|
|
|
|
}
|