#include "CtrlLib.h" NAMESPACE_UPP void DocEdit::MouseWheel(Point p, int zdelta, dword keyflags) { sb.Wheel(zdelta); } void DocEdit::ClearLines() { para.Clear(); ASSERT(this->line.GetCount() == para.GetCount()); } void DocEdit::InsertLines(int line, int count) { para.Insert(line, Para(), count); ASSERT(this->line.GetCount() == para.GetCount()); } void DocEdit::RemoveLines(int line, int count) { para.Remove(line, count); ASSERT(this->line.GetCount() == para.GetCount()); } DocEdit::Fmt DocEdit::Format(const WString& text) const { FontInfo fi = font.Info(); Fmt fmt; int tcx = fi['x'] * 4; fmt.len = text.GetLength(); fmt.text.Alloc(text.GetLength()); memcpy(fmt.text, text, text.GetLength() * sizeof(wchar)); fmt.width.Alloc(text.GetLength()); fmt.line.Add(0); int *w = fmt.width; int x = 0; const wchar *space = NULL; int spacex = 0; for(wchar *s = fmt.text; s < fmt.text + fmt.len; s++) { int cw; if(*s == '\t') cw = (x + tcx) / tcx * tcx - x; else cw = fi[*s]; *w++ = cw; if(*s == ' ' || *s == '\t') { space = s; spacex = x + cw; *s = ' '; } x += cw; if(x > cx) if(space && space <= s) { space++; fmt.line.Add(int(space - fmt.text)); space = NULL; x -= spacex; } else { fmt.line.Add(int(s - fmt.text)); x = cw; } } fmt.fi = fi; return fmt; } int DocEdit::GetHeight(int i) { Para& p = para[i]; if(p.cx == cx) return p.cy; Fmt fmt = Format(line[i]); p.cx = cx; p.cy = fmt.line.GetCount() * (fmt.fi.GetHeight()) + after; return p.cy; } int DocEdit::GetY(int parai) { int y = 1; for(int i = 0; i < parai; i++) y += GetHeight(i); return y; } void DocEdit::InvalidateLine(int i) { para[i].cx = -1; } void DocEdit::RefreshLine(int i) { int q = para[i].cx >= 0 ? para[i].cy : -1; Refresh(1, GetY(i) - sb, cx, GetHeight(i)); if(q < 0 || q != para[i].cy) Refresh(); } int sSum(const int *w, int n) { int m = 0; while(n--) m += *w++; return m; } void DocEdit::Paint(Draw& w) { Size sz = GetSize(); Color bg = color[IsShowEnabled() && !IsReadOnly() ? PAPER_NORMAL : PAPER_READONLY]; if(nobg) bg = Null; int y = -sb + 1; int pos = 0; int sell, selh; GetSelection(sell, selh); for(int i = 0; i < para.GetCount() && y < sz.cy; i++) { int h = GetHeight(i); if(y + h >= 0) { WString text = line[i]; Fmt fmt = Format(text); int p = pos; for(int i = 0; i < fmt.line.GetCount(); i++) { int n = fmt.LineEnd(i) - fmt.line[i]; int a = minmax(sell - p, 0, n); int b = minmax(selh - p, 0, n) - a; int c = n - a - b; int *wa = fmt.width + fmt.line[i]; int *wb = fmt.width + fmt.line[i] + a; int *wc = fmt.width + fmt.line[i] + a + b; int acx = sSum(wa, a); int bcx = sSum(wb, b); int ccx = sSum(wc, c); w.DrawRect(1, y, acx, fmt.fi.GetHeight(), bg); w.DrawText(1, y, ~fmt.text + fmt.line[i], font, IsShowEnabled() ? color[INK_NORMAL] : color[INK_DISABLED], a, wa); w.DrawRect(1 + acx, y, bcx, fmt.fi.GetHeight(), color[PAPER_SELECTED]); w.DrawText(1 + acx, y, ~fmt.text + fmt.line[i] + a, font, color[INK_SELECTED], b, wb); w.DrawRect(1 + acx + bcx, y, ccx, fmt.fi.GetHeight(), bg); w.DrawText(1 + acx + bcx, y, ~fmt.text + fmt.line[i] + a + b, font, color[INK_NORMAL], c, wc); p += n; w.DrawRect(1 + acx + bcx + ccx, y, cx - (acx + bcx + ccx), fmt.fi.GetHeight(), p >= sell && p < selh ? color[PAPER_SELECTED] : bg); y += fmt.fi.GetHeight(); } w.DrawRect(1, y, cx, after, color[PAPER_NORMAL]); y += after; } else y += h; pos += line[i].GetLength() + 1; } w.DrawRect(0, -sb, sz.cx, 1, bg); w.DrawRect(0, 0, 1, sz.cy, bg); w.DrawRect(sz.cx - 1, 0, 1, sz.cy, bg); w.DrawRect(1, y++, cx, 1, SColorShadow); if(y < sz.cy) w.DrawRect(1, y, cx, sz.cy - y, bg); DrawTiles(w, DropCaret(), CtrlImg::checkers()); } void DocEdit::SetSb() { Size sz = GetSize(); cx = max(Draw::GetStdFontCy(), sz.cx - 2); sb.SetPage(GetSize().cy); sb.SetTotal(GetY(para.GetCount()) + 2); } void DocEdit::Layout() { SetSb(); Invalidate(); } Point DocEdit::GetCaret(int pos) { int i = GetLinePos(pos); Fmt fmt = Format(line[i]); int l; for(l = 0; l < fmt.line.GetCount(); l++) if(pos < fmt.line[l]) break; l--; const int *w = fmt.width + fmt.line[l]; pos -= fmt.line[l]; int x = 0; while(pos-- > 0) x += *w++; return Point(x, GetY(i) + l * fmt.fi.GetHeight()); } int DocEdit::GetCursorPos(Point p) { int pos = 0; for(int i = 0; i < para.GetCount(); i++) { int h = GetHeight(i); if(p.y < h) { WString text = line[i]; Fmt fmt = Format(text); int x = 0; int l = p.y / fmt.fi.GetHeight(); if(l < 0) return pos; if(l >= fmt.line.GetCount()) return pos + text.GetLength(); const int *w = fmt.width + fmt.line[l]; const int *e = fmt.width + fmt.LineEnd(l); while(w < e) { if(p.x < x + *w / 2) return int(w - fmt.width) + pos; x += *w++; } int p = int(e - fmt.width); if(p > 0 && text[p - 1] == ' ' && l < fmt.line.GetCount() - 1) p--; return p + pos; } p.y -= h; pos += line[i].GetLength() + 1; } return GetLength(); } void DocEdit::PlaceCaret(bool scroll) { Point cr = GetCaret(cursor); int fy = font.Info().GetLineHeight(); if(scroll) if(cursor == total) sb.End(); else sb.ScrollInto(cr.y, fy + 2); SetCaret(cr.x + 1, cr.y - sb, 1, fy); } void DocEdit::PlaceCaret(int newpos, bool select) { if(newpos > GetLength()) newpos = GetLength(); int z = GetLine(newpos); if(select) { if(anchor < 0) { anchor = cursor; } RefreshLines(z, GetLine(cursor)); } else if(anchor >= 0) { RefreshLines(GetLine(cursor), GetLine(anchor)); anchor = -1; } cursor = newpos; PlaceCaret(true); if(IsSelection()) SetSelectionSource(ClipFmtsText()); SelectionChanged(); } int DocEdit::GetMousePos(Point p) { return GetCursorPos(Point(p.x - 1, p.y + sb - 1)); } void DocEdit::LeftDown(Point p, dword flags) { SetWantFocus(); int c = GetMousePos(p); int l, h; if(GetSelection(l, h) && c >= l && c < h) { selclick = true; return; } PlaceCaret(c, flags & K_SHIFT); SetCapture(); } void DocEdit::LeftUp(Point p, dword flags) { if(!HasCapture() && selclick) { int c = GetMousePos(p); PlaceCaret(c, flags & K_SHIFT); SetWantFocus(); } selclick = false; } void DocEdit::MouseMove(Point p, dword flags) { if(!HasCapture()) return; PlaceCaret(GetMousePos(p), true); } void DocEdit::LeftDouble(Point, dword) { int l, h; if(GetWordSelection(cursor, l, h)) SetSelection(l, h); } void DocEdit::LeftTriple(Point, dword) { int q = cursor; int i = GetLinePos(q); q = cursor - q; SetSelection(q, q + GetLineLength(i) + 1); } Image DocEdit::CursorImage(Point, dword) { return Image::IBeam(); } void DocEdit::GotFocus() { Refresh(); } void DocEdit::LostFocus() { Refresh(); } void DocEdit::VertMove(int delta, bool select, bool scs) { int hy = GetY(para.GetCount()); Point p = GetCaret(cursor); int yy = p.y; for(;;) { p.y += delta; if(p.y > hy) p.y = hy - 1; if(p.y < 0) p.y = 0; int q = GetCursorPos(p); if(q >= 0 && q != cursor && delta < 0 == q < cursor && GetCaret(q).y != yy) { PlaceCaret(q, select); break; } if(p.y == 0 || p.y >= hy - 1) { PlaceCaret(delta > 0 ? total : 0, select); break; } delta = sgn(delta) * 4; } if(scs) sb = GetCaret(cursor).y - (yy - sb); PlaceCaret(true); } void DocEdit::HomeEnd(int x, bool select) { Point p = GetCaret(cursor); p.x = x; PlaceCaret(GetCursorPos(p), select); } bool DocEdit::Key(dword key, int cnt) { NextUndo(); bool h; int q; bool select = key & K_SHIFT; int pgsk = max(8, 6 * GetSize().cy / 8); switch(key & ~K_SHIFT) { case K_CTRL_LEFT: PlaceCaret(GetPrevWord(cursor), select); break; case K_CTRL_RIGHT: PlaceCaret(GetNextWord(cursor), select); break; case K_HOME: HomeEnd(0, select); break; case K_END: HomeEnd(cx, select); break; case K_CTRL_HOME: case K_CTRL_PAGEUP: PlaceCaret(0, select); break; case K_CTRL_END: case K_CTRL_PAGEDOWN: PlaceCaret(total, select); break; case K_UP: if(GetCursor() == 0) return !updownleave; VertMove(-8, select, false); return true; case K_DOWN: if(GetCursor() == GetLength()) return !updownleave; VertMove(8, select, false); return true; case K_PAGEUP: VertMove(-pgsk, select, true); return true; case K_PAGEDOWN: VertMove(pgsk, select, true); return true; case K_LEFT: if(cursor) PlaceCaret(cursor - 1, select); break; case K_RIGHT: if(cursor < total) PlaceCaret(cursor + 1, select); break; default: if(IsReadOnly()) return MenuBar::Scan(WhenBar, key); switch(key) { case K_BACKSPACE: case K_SHIFT|K_BACKSPACE: if(RemoveSelection()) break; if(cursor == 0) return true; cursor--; Remove(cursor, 1); break; case K_CTRL_BACKSPACE: if(RemoveSelection()) break; if(cursor <= 0) return true; q = cursor - 1; h = IsLetter(GetChar(q)); while(q > 0 && IsLetter(GetChar(q - 1)) == h) q--; Remove(q, cursor - q); SetCursor(q); break; case K_DELETE: if(RemoveSelection()) break; if(cursor >= total) return true; if(cursor < total) Remove(cursor, 1); break; case K_CTRL_DELETE: if(RemoveSelection()) break; if(cursor >= total) return true; q = cursor; h = IsLetter(GetChar(q)); while(IsLetter(GetChar(q)) == h && q < total) q++; Remove(cursor, q - cursor); break; case K_ENTER: key = '\n'; default: if(filter && key >= 32 && key < 65535) key = (*filter)(key); if(key >= ' ' && key < 65536 || key == '\n' || key == '\t' || key == K_SHIFT_SPACE) { if(key == K_TAB && !processtab) return false; if(key >= 128 && key < 65536 && charset != CHARSET_UNICODE && FromUnicode((wchar)key, charset) == DEFAULTCHAR) return true; RemoveSelection(); Insert(cursor, WString(key == K_SHIFT_SPACE ? ' ' : key, cnt), true); cursor += cnt; break; } return MenuBar::Scan(WhenBar, key); } UpdateAction(); } PlaceCaret(true); return true; } void DocEdit::Scroll() { PlaceCaret(false); Refresh(); } void DocEdit::Invalidate() { for(int i = 0; i < para.GetCount(); i++) para[i].cx = -1; PlaceCaret(false); } void DocEdit::RefreshStyle() { cursor = 0; sb = 0; ClearSelection(); Invalidate(); Layout(); Refresh(); } void DocEdit::RightDown(Point p, dword w) { SetFocus(); int c = GetMousePos(p); int l, h; if(!GetSelection(l, h) || c < l || c >= h) PlaceCaret(c, false); MenuBar::Execute(WhenBar); } DocEdit::DocEdit() { updownleave = false; cx = 0; filter = NULL; after = 0; font = StdFont(); AutoHideSb(); SetFrame(ViewFrame()); AddFrame(sb); sb.SetLine(8); sb.WhenScroll = THISBACK(Scroll); InsertLines(0, 1); } DocEdit::~DocEdit() {} void DocEdit::DragAndDrop(Point p, PasteClip& d) { if(IsReadOnly()) return; int c = GetMousePos(p); if(AcceptText(d)) { NextUndo(); int a = sb; int sell, selh; if(GetSelection(sell, selh)) { if(c >= sell && c < selh) { RemoveSelection(); if(IsDragAndDropSource()) d.SetAction(DND_COPY); c = sell; } else if(d.GetAction() == DND_MOVE && IsDragAndDropSource()) { if(c > sell) c -= selh - sell; RemoveSelection(); d.SetAction(DND_COPY); } } int count = Insert(c, GetWString(d)); sb = a; SetFocus(); SetSelection(c, c + count); Action(); return; } if(!d.IsAccepted()) return; Point dc = Null; if(c >= 0) { Point cr = GetCaret(c); dc = Point(cr.x + 1, cr.y); } if(dc != dropcaret) { RefreshDropCaret(); dropcaret = dc; RefreshDropCaret(); } } Rect DocEdit::DropCaret() { if(IsNull(dropcaret)) return Rect(0, 0, 0, 0); return RectC(dropcaret.x, dropcaret.y - sb, 1, font.Info().GetLineHeight()); } void DocEdit::RefreshDropCaret() { Refresh(DropCaret()); } void DocEdit::DragRepeat(Point p) { sb = (int)sb + GetDragScroll(this, p, 16).y; } void DocEdit::DragLeave() { RefreshDropCaret(); dropcaret = Null; isdrag = false; Layout(); } void DocEdit::LeftDrag(Point p, dword flags) { int c = GetMousePos(p); int l, h; if(!HasCapture() && GetSelection(l, h) && c >= l && c < h) { WString sample = GetW(l, min(h - l, 3000)); Size ssz = StdSampleSize(); ImageDraw iw(ssz); iw.DrawRect(ssz, Black()); iw.Alpha().DrawRect(ssz, Black()); DrawTLText(iw.Alpha(), 0, 0, ssz.cx, sample, StdFont(), White()); NextUndo(); if(DoDragAndDrop(ClipFmtsText(), iw) == DND_MOVE) { RemoveSelection(); Action(); } } } END_UPP_NAMESPACE