#include "CtrlLib.h" NAMESPACE_UPP void ColumnList::MouseWheel(Point p, int zdelta, dword keyflags) { sb.Wheel(zdelta); } int ColumnList::GetColumnCx(int i) const { int szcx = GetSize().cx; int cx = max(szcx / max(ncl, 1), Draw::GetStdFontCy()); return i == ncl - 1 ? szcx - i * cx : cx; } int ColumnList::GetColumnItems() const { if(cy == 0) return 0; return GetSize().cy / cy; } int ColumnList::GetDragColumn(int x) const { int cx = GetColumnCx(); int i = 0; if(cx == 0) return -1; for(;;) { x -= GetColumnCx(i); if(x >= -3 && x < 4) return i; if(x < 0) return -1; i++; } } void ColumnList::ShiftSelect() { if(anchor < 0) anchor = cursor; int l = min(anchor, cursor); int h = max(anchor, cursor); for(int i = 0; i < GetCount(); i++) { bool sel = i >= l && i <= h; if(item[i].canselect && item[i].sel != sel) { item[i].sel = sel; RefreshItem(i); } } UpdateSelect(); } Image ColumnList::CursorImage(Point p, dword) { return HasCapture() || GetDragColumn(p.x) >= 0 ? Image::SizeHorz() : Image::Arrow(); } int ColumnList::GetItem(Point p) { int i = p.y / cy + GetColumnItems() * (p.x / GetColumnCx(0)); if(i < 0 || i >= GetPageItems()) return -1; i += sb; return i >= 0 && i < GetCount() ? i : -1; } void ColumnList::PointDown(Point p) { int i = GetItem(p); if(i >= 0 && i < GetCount()) SetCursor0(i, false); else if(clickkill) KillCursor(); } void ColumnList::DoClick(Point p, dword flags) { SetWantFocus(); PointDown(p); p.y %= cy; p.x %= GetColumnCx(0); if(multi && cursor >= 0) { if(flags & K_SHIFT) ShiftSelect(); else if(flags & K_CTRL) SelectOne(cursor, !IsSelected(cursor)); else { ClearSelection(); SelectOne(cursor, true); anchor = cursor; } } WhenLeftClick(); WhenLeftClickPos(p); } void ColumnList::DoLeftDown(Point p, dword flags) { selclick = false; int i = GetDragColumn(p.x); if(i >= 0) { ci = i; dx = p.x - GetColumnCx(0) * (i + 1); mpos = p.x; SetCapture(); Refresh(mpos - dx, 0, 1, GetSize().cy); SyncInfo(); } else { int i = GetItem(p); if(i >= 0 && i < GetCount() && multi && IsSel(i)) selclick = true; else DoClick(p, flags); } } void ColumnList::LeftDown(Point p, dword flags) { DoLeftDown(p, flags); } void ColumnList::LeftUp(Point p, dword flags) { if(selclick) DoClick(p, flags); selclick = false; if(HasCapture()) { Refresh(); SyncInfo(); } } void ColumnList::LeftDrag(Point p, dword keyflags) { if(!HasCapture()) WhenDrag(); } void ColumnList::RightDown(Point p, dword flags) { int i = GetItem(p); if(!(i >= 0 && i < GetCount() && multi && IsSel(i))) PointDown(p); MenuBar::Execute(WhenBar); } void ColumnList::LeftDouble(Point p, dword flags) { DoLeftDown(p, flags); WhenLeftDouble(); } void ColumnList::SyncInfo() { if((HasMouse() || info.HasMouse()) && !HasCapture() && popupex) { Point p = GetMouseViewPos(); int i = GetItem(p); if(i >= 0) { Rect r = GetItemRect(i); if(p.x > r.left + 3 && p.x < r.right - 2) { Color ink, paper; dword style; GetItemStyle(i, ink, paper, style); Item& m = item[i]; info.Set(this, r, m.value, m.display ? m.display : display, ink, paper, style); return; } } } info.Cancel(); } void ColumnList::MouseMove(Point p, dword) { if(HasCapture()) { int cx = sb.GetViewSize().cx; Refresh(mpos - dx, 0, 1, GetSize().cy); mpos = p.x; Refresh(mpos - dx, 0, 1, GetSize().cy); int ocl = ncl; ncl = max(1, cx * (ci + 1) / max(p.x, Draw::GetStdFontCy())); if(ncl != ocl) Refresh(); SetSb(); } SyncInfo(); } void ColumnList::CancelMode() { info.Cancel(); dropitem = -1; anchor = -1; selclick = false; } void ColumnList::MouseLeave() { if(!info.IsOpen()) info.Cancel(); } Rect ColumnList::GetItemRect(int i) const { i -= sb; int n = GetColumnItems(); if(i < 0 || n == 0) return Rect(0, 0, 0, 0); int cl = i / n; int x = 0; for(int q = 0; q < cl; q++) x += GetColumnCx(q); return RectC(x, i % n * cy, GetColumnCx(cl), cy); } void ColumnList::Page(bool down) { int q = sb; if(down) sb.NextPage(); else sb.PrevPage(); if(q == sb) SetCursor(down ? GetCount() - 1 : 0); else SetCursor(cursor - q + sb); } bool ColumnList::Key(dword _key, int count) { int c = cursor; bool sel = _key & K_SHIFT; int key = _key & ~K_SHIFT; switch(key) { case K_UP: if(c < 0) c = GetCount() - 1; else if(c > 0) c--; break; case K_DOWN: if(c < 0) c = 0; else if(c < GetCount() - 1) c++; break; case K_LEFT: c = max(c - GetColumnItems(), 0); break; case K_RIGHT: c = min(c + GetColumnItems(), GetCount() - 1); break; case K_PAGEUP: Page(false); return true; case K_PAGEDOWN: Page(true); return true; case K_HOME: c = 0; break; case K_END: c = GetCount() - 1; break; default: return MenuBar::Scan(WhenBar, _key); } if(sel) { SetCursor0(c, false); ShiftSelect(); } else { SetCursor(c); anchor = c; } return true; } void ColumnList::RefreshSel() { for(int i = 0; i < item.GetCount(); i++) if(item[i].sel) RefreshItem(i); } void ColumnList::ClearSelection() { bool upd = false; for(int i = 0; i < item.GetCount(); i++) if(item[i].sel) { item[i].sel = false; upd = true; RefreshItem(i); } if(upd) UpdateSelect(); } void ColumnList::SelectOne(int i, bool sel) { if(!multi) return; if(item[i].canselect) item[i].sel = sel; UpdateSelect(); RefreshItem(i); } bool ColumnList::IsSelected(int i) const { return item[i].sel; } bool ColumnList::IsSel(int i) const { return IsMultiSelect() ? IsSelected(i) : GetCursor() == i; } void ColumnList::UpdateSelect() { selcount = 0; for(int i = 0; i < item.GetCount(); i++) if(item[i].sel) selcount++; SyncInfo(); Action(); WhenSelection(); WhenSel(); } void ColumnList::GetItemStyle(int i, Color& fg, Color& bg, dword& st) { st = 0; bool hasfocus = HasFocusDeep() && !IsDragAndDropTarget(); bool drop = i == dropitem && !insert; if(IsReadOnly()) st |= Display::READONLY; if(IsSelected(i)) st |= Display::SELECT; if(i == cursor) st |= Display::CURSOR; if(drop) { hasfocus = true; st |= Display::CURSOR; } if(hasfocus) st |= Display::FOCUS; fg = SColorText; bg = SColorPaper; if(nobg) bg = Null; if((st & Display::SELECT) || (!multi || !item[i].canselect && selcount == 0) && (st & Display::CURSOR) || drop) { fg = hasfocus ? SColorHighlightText : SColorText; bg = hasfocus ? SColorHighlight : Blend(SColorDisabled, SColorPaper); } } dword ColumnList::PaintItem(Draw& w, int i, const Rect& r) { Color ink, paper; dword style; GetItemStyle(i, ink, paper, style); const Item& m = item[i]; w.Clip(r); (m.display ? m.display : display)->Paint(w, r, m.value, ink, paper, style); w.End(); return style; } void ColumnList::Paint(Draw& w) { Size sz = GetSize(); if(GetColumnCx(0) == 0) return; int x = 0; int i = sb; int coli = 0; while(x < sz.cx) { int cx = GetColumnCx(coli++); int y = 0; while(y + cy <= sz.cy) { Rect rect = RectC(x, y, cx, cy); if(i < GetCount()) { if(w.IsPainting(rect)) { Rect r = rect; r.right--; dword style = PaintItem(w, i, r); w.DrawRect(rect.right - 1, rect.top, 1, rect.Height(), x + cx < sz.cx ? SColorDisabled : SColorPaper); if(i == cursor && selcount != 1 && multi && item[i].canselect) DrawFocus(w, r, style & Display::SELECT ? SColorPaper() : SColorText()); } } else w.DrawRect(rect, SColorPaper); if(i == dropitem && insert) DrawHorzDrop(w, x, y, cx); if(i + 1 == dropitem && insert && y + 2 * cy > sz.cy) DrawHorzDrop(w, x, y + cy - 2, cx); y += cy; i++; } w.DrawRect(x, y, cx, sz.cy - y, SColorPaper); x += cx; } if(HasCapture()) w.DrawRect(mpos - dx, 0, 1, sz.cy, Blend(SColorHighlight, SColorFace)); scroller.Set(sb); } Image ColumnList::GetDragSample() { Size sz = StdSampleSize(); ImageDraw iw(sz); int y = 0; for(int i = 0; i < GetCount() && y < sz.cy; i++) if(IsSel(i)) { PaintItem(iw, i, RectC(0, y, sz.cx, cy)); y += cy; } return Crop(iw, 0, 0, sz.cx, y);; } int ColumnList::GetPageItems() const { return ncl * GetColumnItems(); } void ColumnList::SetSb() { sb.SetTotal(GetCount()); sb.SetPage(GetPageItems()); } void ColumnList::Layout() { SetSb(); } void ColumnList::Scroll() { Size sz = GetSize(); sz.cy = sz.cy / cy * cy; scroller.Scroll(*this, sz, sb, cy); info.Cancel(); } void ColumnList::RefreshItem(int i, int ex) { if(i >= 0) { Refresh(GetItemRect(i).InflatedVert(ex)); SyncInfo(); } } void ColumnList::SetCursor0(int c, bool sel) { int c0 = cursor; c = minmax(c, 0, GetCount() - 1); if(c < 0 || cursor < 0) Refresh(); else RefreshCursor(); cursor = c; int q = sb; sb.ScrollInto(cursor); if(q != sb) Refresh(); else RefreshCursor(); if(sel && multi) { ClearSelection(); if(cursor >= 0) { SelectOne(cursor, true); anchor = cursor; } } if(c0 != cursor) { if(cursor >= 0) WhenEnterItem(); else WhenKillCursor(); WhenSel(); } SyncInfo(); Action(); } void ColumnList::SetCursor(int c) { SetCursor0(c, true); } void ColumnList::SetSbPos(int y) { SetSb(); sb = minmax(y, 0, GetCount() - GetPageItems()); } void ColumnList::KillCursor() { if(cursor < 0) return; Refresh(); SyncInfo(); cursor = -1; sb.Begin(); WhenKillCursor(); WhenSel(); Action(); } void ColumnList::GotFocus() { if(cursor < 0 && GetCount()) SetCursor(sb); Refresh(); SyncInfo(); } void ColumnList::LostFocus() { Refresh(); SyncInfo(); } int ColumnList::RoundedCy() { Rect r = GetRect(); Rect rr = r; frame->FrameLayout(r); return r.Height() / cy * cy + rr.Height() - r.Height(); } void ColumnList::FrameLayout(Rect& r) { r.bottom = r.top + RoundedCy(); frame->FrameLayout(r); } void ColumnList::FrameAddSize(Size& sz) { frame->FrameAddSize(sz); } void ColumnList::FramePaint(Draw& draw, const Rect& r) { int ry = RoundedCy(); Rect rr = r; rr.bottom = ry; frame->FramePaint(draw, rr); rr = r; rr.top = ry; draw.DrawRect(rr, SColorFace); } ColumnList& ColumnList::RoundSize(bool b) { if(b) Ctrl::SetFrame(0, *this); else Ctrl::SetFrame(0, *frame); RefreshLayout(); Refresh(); SyncInfo(); return *this; } void ColumnList::SetFrame(CtrlFrame& _frame) { frame = &_frame; RefreshLayout(); Refresh(); SyncInfo(); } struct ColumnList::ItemOrder { const ValueOrder *order; bool operator()(const Item& a, const Item& b) const { return (*order)(a.value, b.value); } }; void ColumnList::Sort(const ValueOrder& order) { ItemOrder itemorder; itemorder.order = ℴ KillCursor(); UPP::Sort(item, itemorder); sb.Begin(); SyncInfo(); } void ColumnList::Clear() { CancelMode(); KillCursor(); item.Clear(); selcount = 0; Update(); Refresh(); SyncInfo(); SetSb(); } void ColumnList::Insert(int ii, const Value& val, bool canselect) { int c = -1; if(cursor >= ii) { c = cursor + 1; KillCursor(); } Item& m = item.Insert(ii); m.value = val; m.sel = false; m.canselect = canselect; m.display = NULL; Refresh(); SyncInfo(); SetSb(); if(c >= 0) SetCursor(c); } void ColumnList::Insert(int ii, const Value& val, const Display& display, bool canselect) { Insert(ii, val, canselect); item[ii].display = &display; SyncInfo(); } void ColumnList::Set(int ii, const Value& val, bool canselect) { Item& m = item[ii]; m.value = val; m.sel = false; m.canselect = canselect; m.display = NULL; RefreshItem(ii); SyncInfo(); SetSb(); } void ColumnList::Set(int ii, const Value& val, const Display& display, bool canselect) { Set(ii, val, canselect); item[ii].display = &display; SyncInfo(); } void ColumnList::Remove(int ii) { int c = -1; if(cursor >= ii) { c = max(ii, cursor - 1); KillCursor(); } item.Remove(ii); Refresh(); SyncInfo(); SetSb(); if(c >= 0) SetCursor(c); } void ColumnList::Add(const Value& val, bool canselect) { Insert(item.GetCount(), val, canselect); } void ColumnList::Add(const Value& val, const Display& display, bool canselect) { Add(val, canselect); item.Top().display = &display; } void ColumnList::DnD(int _drop, bool _insert) { if(dropitem != _drop || insert != _insert) { RefreshItem(dropitem - 1); RefreshItem(dropitem); dropitem = _drop; insert = _insert; RefreshItem(dropitem - 1); RefreshItem(dropitem); } } bool ColumnList::DnDInsert(int i, int py, PasteClip& d, int q) { Rect r = GetItemRect(i); int cy = r.GetHeight(); if(py < r.top + cy / q) { WhenDropInsert(i, d); if(d.IsAccepted()) { DnD(i, true); return true; } } if(py >= r.top + (q - 1) * cy / q && i < GetCount()) { WhenDropInsert(i + 1, d); if(d.IsAccepted()) { DnD(i + 1, true); return true; } } return false; } void ColumnList::DragAndDrop(Point p, PasteClip& d) { int i = GetItem(p); if(i >= 0) { if(DnDInsert(i, p.y, d, 4)) return; WhenDropItem(i, d); if(d.IsAccepted()) { DnD(i, false); return; } if(DnDInsert(i, p.y, d, 2)) return; } if(GetCount() == 0 && p.y < 4 || !WhenDrop) { WhenDropInsert(GetCount(), d); if(d.IsAccepted()) { DnD(GetCount(), true); return; } } WhenDrop(d); DnD(-1, false); } void ColumnList::DragRepeat(Point p) { sb = sb + GetDragScroll(this, p, 1).y; } void ColumnList::DragEnter() { RefreshSel(); } void ColumnList::DragLeave() { DnD(-1, false); RefreshSel(); } void ColumnList::RemoveSelection() { for(int i = GetCount() - 1; i >= 0; i--) if(IsSel(i)) Remove(i); // Optimize! } void ColumnList::InsertDrop(int ii, const Vector& data, PasteClip& d, bool self) { if(data.GetCount() == 0) return; if(d.GetAction() == DND_MOVE && self) { for(int i = GetCount() - 1; i >= 0; i--) { if(IsSel(i)) { Remove(i); // Optimize! if(i < ii) ii--; } } KillCursor(); d.SetAction(DND_COPY); } item.InsertN(ii, data.GetCount()); for(int i = 0; i < data.GetCount(); i++) { Item& m = item[ii + i]; m.value = data[i]; m.sel = false; m.canselect = true; m.display = NULL; } Refresh(); SyncInfo(); SetSb(); ClearSelection(); SetCursor(ii + data.GetCount() - 1); if(IsMultiSelect()) for(int i = 0; i < data.GetCount(); i++) SelectOne(ii + i, true); } void ColumnList::InsertDrop(int ii, const ColumnList& src, PasteClip& d) { Vector data; for(int i = 0; i < src.GetCount(); i++) if(src.IsSel(i)) data.Add(src[i]); InsertDrop(ii, data, d, &src == this); } void ColumnList::InsertDrop(int ii, PasteClip& d) { InsertDrop(ii, GetInternal(d), d); } void ColumnList::Serialize(Stream& s) { int version = 0; s / version; s / ncl; Refresh(); SyncInfo(); } ColumnList::ColumnList() { clickkill = false; ncl = 1; cy = Draw::GetStdFontCy(); cursor = -1; AddFrame(sb); sb.WhenScroll = THISBACK(Scroll); sb.AutoHide(); Ctrl::SetFrame(ViewFrame()); frame = &ViewFrame(); RoundSize(); display = &StdDisplay(); multi = false; selcount = 0; nobg = false; popupex = true; } ColumnList::~ColumnList() {} END_UPP_NAMESPACE