#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; 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) { if(!WhenMouseWheel(zdelta, 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() { Refresh(); } bool RichTextView::GotoLabel(Gate match, bool dohighlight, bool find_last) { Vector 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(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& 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 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 }