#include "CtrlLib.h" NAMESPACE_UPP TreeCtrl::Node::Node() { Init(); } TreeCtrl::Node::Node(const Image& img, const Value& v) { Init(); image = img; key = value = v; } TreeCtrl::Node::Node(const Image& img, const Value& v, const Value& t) { Init(); image = img; key = v; value = t; } TreeCtrl::Node::Node(const Value& v) { Init(); key = value = v; } TreeCtrl::Node::Node(const Value& v, const Value& t) { Init(); key = v; value = t; } TreeCtrl::Node::Node(Ctrl& ctrl) { Init(); SetCtrl(ctrl); } TreeCtrl::Node::Node(const Image& img, Ctrl& ctrl, int cx, int cy) { Init(); SetCtrl(ctrl); image = img; size = Null; if(cx > 0) size.cx = cx; if(cy > 0) size.cy = cy; } TreeCtrl::TreeCtrl() { display = &StdDisplay(); levelcx = 16; nocursor = false; noroot = false; dirty = true; multiselect = false; nobg = false; popupex = true; mousemove = false; accel = false; treesize = Size(0, 0); Clear(); SetFrame(ViewFrame()); AddFrame(sb); sb.WhenScroll = THISBACK(Scroll); WhenLeftDouble = THISBACK(StdLeftDouble); chldlck = false; highlight_ctrl = false; } void TreeCtrl::StdLeftDouble() { if(IsCursor()) Open(GetCursor(), !IsOpen(GetCursor())); } void TreeCtrl::CancelMode() { selclick = false; dropinsert = 0; dropitem = -1; repoint = Null; } TreeCtrl::~TreeCtrl() { Shutdown(); } void TreeCtrl::Layout() { Size full = sb.GetViewSize(); Size red = sb.GetReducedViewSize(); Size total = sb.GetTotal(); if(total.cx > full.cx) full.cy = red.cy; if(total.cy > full.cy) full.cx = red.cx; if(total.cx > full.cx) full.cy = red.cy; sb.SetPage(full); sb.SetLine(item[0].GetSize(display).cy); } // 2008-04-08 mrjt Size TreeCtrl::Item::GetValueSize(const Display *treedisplay) const { return display ? display->GetStdSize(value) : treedisplay->GetStdSize(value); } Size TreeCtrl::Item::GetCtrlSize() const { if(!ctrl) return Size(0, 0); Size csz = ctrl->GetStdSize(); return Size(Nvl(size.cx, csz.cx), Nvl(size.cy, csz.cy)); } Size TreeCtrl::Item::GetSize(const Display *treedisplay) const { Size sz = GetValueSize(treedisplay); Size csz = GetCtrlSize(); sz += Size(2 * margin, 2 * margin); Size isz = image.GetSize(); sz.cx += isz.cx; sz.cy = max(sz.cy, isz.cy); sz.cx += csz.cx; sz.cy = max(sz.cy, csz.cy); return sz; } void TreeCtrl::SetRoot(const TreeCtrl::Node& n) { (TreeCtrl::Node &)item[0] = n; RefreshItem(0); } void TreeCtrl::SetRoot(const Image& img, Value v) { SetRoot(TreeCtrl::Node(img, v).CanOpen()); } void TreeCtrl::SetRoot(const Image& img, Value v, Value t) { SetRoot(TreeCtrl::Node(img, v, t).CanOpen()); } void TreeCtrl::SetRoot(const Image& img, Ctrl& ctrl, int cx, int cy) { SetRoot(TreeCtrl::Node(img, ctrl, cx, cy).CanOpen()); } int TreeCtrl::Insert(int parentid, int i, const TreeCtrl::Node& n) { int id; if(freelist >= 0) { id = freelist; freelist = item[id].freelink; } else { id = item.GetCount(); item.Add(); } Item& m = item[id]; m.free = false; m.parent = parentid; (TreeCtrl::Node&)m = n; if(parentid >= 0) { Item& parent = item[parentid]; parent.child.Insert(i, id); } dirty = true; Dirty(parentid); return id; } TreeCtrl& TreeCtrl::SetDisplay(const Display& d) { display = &d; Dirty(); return *this; } int TreeCtrl::Add(int parentid, const TreeCtrl::Node& n) { return Insert(parentid, item[parentid].child.GetCount(), n); } int TreeCtrl::Insert(int parentid, int i) { return Insert(parentid, i, TreeCtrl::Node()); } int TreeCtrl::Add(int parentid) { return Add(parentid, TreeCtrl::Node()); } int TreeCtrl::Insert(int parentid, int i, const Image& img, Value v, bool withopen) { return Insert(parentid, i, TreeCtrl::Node(img, v).CanOpen(withopen)); } int TreeCtrl::Insert(int parentid, int i, const Image& img, Value v, Value t, bool withopen) { return Insert(parentid, i, TreeCtrl::Node(img, v, t).CanOpen(withopen)); } int TreeCtrl::Add(int parentid, const Image& img, Value v, bool withopen) { return Add(parentid, TreeCtrl::Node(img, v).CanOpen(withopen)); } int TreeCtrl::Add(int parentid, const Image& img, Value v, Value t, bool withopen) { return Add(parentid, TreeCtrl::Node(img, v, t).CanOpen(withopen)); } int TreeCtrl::Insert(int parentid, int i, const Image& img, Ctrl& ctrl, int cx, int cy, bool withopen) { return Insert(parentid, i, TreeCtrl::Node(img, ctrl, cx, cy).CanOpen(withopen)); } int TreeCtrl::Add(int parentid, const Image& img, Ctrl& ctrl, int cx, int cy, bool withopen) { return Add(parentid, TreeCtrl::Node(img, ctrl, cx, cy).CanOpen(withopen)); } Value TreeCtrl::Get(int id) const { const Item& m = item[id]; return m.ctrl ? m.ctrl->GetData() : m.key; } Value TreeCtrl::GetValue(int id) const { const Item& m = item[id]; return m.ctrl ? m.ctrl->GetData() : m.value; } void TreeCtrl::Set(int id, Value v) { Item& m = item[id]; if(m.ctrl) m.ctrl->SetData(v); else { m.value = m.key = v; RefreshItem(id); } SetOption(id); } void TreeCtrl::Set(int id, Value k, Value v) { Item& m = item[id]; if(m.ctrl) m.ctrl->SetData(v); else { m.key = k; m.value = v; RefreshItem(id); } SetOption(id); } void TreeCtrl::SetValue(const Value& v) { int id = GetCursor(); Set(id, Get(id), v); } void TreeCtrl::SetDisplay(int id, const Display& display) { item[id].SetDisplay(display); Dirty(id); } const Display& TreeCtrl::GetDisplay(int id) const { if(id >= 0 && id < item.GetCount() && item[id].display) return *item[id].display; return *display; } void TreeCtrl::SetNode(int id, const TreeCtrl::Node& n) { (TreeCtrl::Node&)item[id] = n; Dirty(id); SetOption(id); } void TreeCtrl::RemoveChildren(int id) { Item& m = item[id]; for(int i = 0; i < m.child.GetCount(); i++) RemoveSubtree(m.child[i]); m.child.Clear(); Dirty(id); } int TreeCtrl::GetChildIndex(int id, int childid) const { for(int i = 0; i < GetChildCount(id); i++) if(GetChild(id, i) == childid) return i; return -1; } void TreeCtrl::RemoveSubtree(int id) { Item& m = item[id]; if(m.sel) selectcount--; if(m.linei == cursor) cursor = item[m.parent].linei; if(m.ctrl) m.ctrl->Remove(); m.key = Null; m.value = Null; m.image = Null; RemoveChildren(id); m.freelink = freelist; freelist = id; m.free = true; } void TreeCtrl::Remove(int id) { ASSERT(id > 0); int pi = item[id].parent; Item& parent = item[item[id].parent]; parent.child.Remove(FindIndex(parent.child, id)); RemoveSubtree(id); Dirty(pi); } void TreeCtrl::Remove(const Vector& id) { for(int i = 0; i < id.GetCount(); i++) if(!item[id[i]].free) Remove(id[i]); } void TreeCtrl::Clear() { for(int i = 0; i < item.GetCount(); i++) if(item[i].ctrl) item[i].ctrl->Remove(); item.Clear(); item.Add(); item[0].linei = -1; item[0].parent = -1; item[0].canopen = true; freelist = -1; selectcount = 0; Dirty(); cursor = anchor = -1; } void TreeCtrl::RemoveCtrls(int itemi) { Item& m = item[itemi]; if(m.ctrl) m.ctrl->Remove(); for(int i = 0; i < m.child.GetCount(); i++) RemoveCtrls(m.child[i]); } void TreeCtrl::ReLine(int itemi, int level, Size& sz) { int ii = line.GetCount(); Line& l = line.Add(); l.level = level; l.itemi = itemi; l.y = sz.cy; l.ll = -1; Item& m = item[itemi]; if(m.ctrl) { hasctrls = true; // chldlck = true; // m.ctrl->Remove(); // chldlck = false; } m.linei = ii; Size msz = m.GetSize(display); sz.cy += msz.cy; sz.cx = max(sz.cx, level * levelcx + msz.cx); level++; for(int i = 0; i < m.child.GetCount(); i++) if(m.isopen) { line[ii].ll = line.GetCount(); ReLine(m.child[i], level, sz); } else { chldlck = true; RemoveCtrls(m.child[i]); chldlck = false; } } void TreeCtrl::SyncAfterSync(Ptr restorefocus) { if(treesize != sb.GetTotal()) { sb.SetTotal(treesize); Refresh(); } SyncCtrls(true, restorefocus); SyncInfo(); } void TreeCtrl::SyncTree() { if(!dirty) return; if(noroot) Open(0); Ptr restorefocus = GetFocusChildDeep(); hasctrls = false; int cursorid = GetCursor(); for(int i = 0; i < item.GetCount(); i++) item[i].linei = -1; line.Clear(); treesize = Size(0, 0); if(noroot) { if(GetChildCount(0)) treesize.cy = -item[0].GetSize(display).cy; ReLine(0, -1, treesize); } else ReLine(0, 0, treesize); treesize.cy = max(0, treesize.cy); treesize.cx += levelcx; cursor = -1; dirty = false; if(cursorid >= 0) SetCursor(cursorid, false, false, false); PostCallback(PTEBACK1(SyncAfterSync, restorefocus)); } void TreeCtrl::SyncCtrls(bool add, Ctrl *restorefocus) { if(!hasctrls) return; Point org = sb; chldlck = true; for(int i = noroot; i < line.GetCount(); i++) { const Line& l = line[i]; Item& m = item[l.itemi]; if(m.ctrl) { if(add) AddChildBefore(m.ctrl, GetLastChild()); if(m.ctrl == restorefocus || m.ctrl->HasChildDeep(restorefocus)) restorefocus->SetFocus(); Size msz = m.GetSize(display); Size isz = m.image.GetSize(); Size csz = m.GetCtrlSize(); m.ctrl->SetRect(levelcx + l.level * levelcx + isz.cx + m.margin - org.x, l.y + (msz.cy - csz.cy) / 2 - org.y, csz.cx, csz.cy); } } chldlck = false; } bool TreeCtrl::IsOpen(int id) const { return item[id].isopen; } void TreeCtrl::Dirty(int id) { // if(selectcount) SelClear(0); Size sz = GetSize(); dirty = true; while(id >= 0) { int q = item[id].linei; if(q >= 0) { int y = line[q].y - sb.GetY(); Refresh(0, y, sz.cx, sz.cy - y); return; } id = item[id].parent; } Refresh(); } void TreeCtrl::Open(int id, bool open) { Item& m = item[id]; if(m.isopen != open && (m.canopen || !open || m.child.GetCount())) { m.isopen = open; int q = GetCursor(); while(q >= 0) { q = GetParent(q); if(q == id) { SetCursor(id, true, true, true); break; } } if(selectcount) SelClear(0); Dirty(id); if(open) WhenOpen(id); else WhenClose(id); } } void TreeCtrl::OpenDeep(int id, bool open) { Open(id, open); Item& m = item[id]; for(int i = 0; i < m.child.GetCount(); i++) OpenDeep(m.child[i], open); } void TreeCtrl::MakeVisible(int id) { id = GetParent(id); while(id >= 0) { Open(id); id = GetParent(id); } } int TreeCtrl::FindLine(int y) const { int l = FindUpperBound(line, y, LineLess()); return l > 0 ? l - 1 : 0; } void TreeCtrl::RefreshLine(int i, int ex) { SyncTree(); if(i >= 0) { Size sz = GetSize(); int y = line[i].y - sb.GetY(); Refresh(0, y - ex, sz.cx, item[line[i].itemi].GetSize(display).cy + 2 * ex); SyncInfo(); } } void TreeCtrl::RefreshItem(int id, int ex) { SyncTree(); if(id >= 0 && id < item.GetCount()) RefreshLine(item[id].linei, ex); } int TreeCtrl::GetItemAtLine(int i) { SyncTree(); return line[i].itemi; } int TreeCtrl::GetLineAtItem(int id) { SyncTree(); return item[id].linei; } int TreeCtrl::GetLineCount() { SyncTree(); return line.GetCount(); } void TreeCtrl::ScrollIntoLine(int i) { sb.ScrollIntoY(line[i].y, item[line[i].itemi].GetSize(display).cy); } void TreeCtrl::CenterLine(int i) { int top = line[i].y; int bottom = top + item[line[i].itemi].GetSize(display).cy; sb.SetY(top + ((bottom - GetSize().cy) >> 1)); } void TreeCtrl::ScrollIntoCursor() { int c = GetCursorLine(); if(c >= 0) ScrollIntoLine(c); } void TreeCtrl::CenterCursor() { int c = GetCursorLine(); if(c >= 0) CenterLine(c); } void TreeCtrl::MoveCursorLine(int c, int incr) { int cnt = line.GetCount(); if (!incr) return; else if (c < 0) c = cnt-1; else if (c >= cnt) c = 0; while (!item[line[c].itemi].canselect) { c += incr; if (c == cursor) return; else if (c < 0) c = cnt-1; else if (c >= cnt) c = 0; } SetCursorLineSync(c); } void TreeCtrl::SetCursorLine(int i, bool sc, bool sel, bool cb) { if(nocursor) return; if(sel && multiselect) { if(selectcount) SelClear(0); SelectOne(line[i].itemi, true); } if(i != cursor) { i = minmax(i, 0, line.GetCount() - 1); if(i < 0) return; Item& m = item[line[i].itemi]; if (!multiselect && !m.canselect) return; if(sc) sb.ScrollIntoY(line[i].y, m.GetSize(display).cy); RefreshLine(cursor); cursor = i; RefreshLine(cursor); if(m.ctrl && m.ctrl->SetWantFocus()) return; if(cb) { WhenCursor(); if(!multiselect) WhenSel(); } } } void TreeCtrl::SetCursorLine(int i) { SetCursorLine(i, true, true, true); } void TreeCtrl::SetCursorLineSync(int i) { if(nocursor) return; if(i != cursor) { RefreshLine(cursor); if(i < 0) cursor = -1; else cursor = minmax(i, (int)noroot, line.GetCount() - 1); RefreshLine(cursor); Item& m = item[line[cursor].itemi]; if(cursor >= 0) { Sync(); sb.ScrollIntoY(line[cursor].y, m.GetSize(display).cy); } if(!(m.ctrl && m.ctrl->SetWantFocus())) SetWantFocus(); WhenCursor(); if (!multiselect) WhenSel(); } } void TreeCtrl::KillCursor() { RefreshLine(cursor); cursor = -1; Refresh(); WhenCursor(); WhenSel(); SyncInfo(); } void TreeCtrl::SetCursor(int id, bool sc, bool sel, bool cb) { while(id > 0) { ASSERT(id >= 0 && id < item.GetCount()); MakeVisible(id); SyncTree(); const Item& m = item[id]; if(m.linei >= 0) { SetCursorLine(m.linei, sc, sel, cb); return; } id = m.parent; } SetCursorLine(0, sc, sel, cb); } void TreeCtrl::SetCursor(int id) { SetCursor(id, true, true, true); } int TreeCtrl::GetCursor() const { return cursor >= 0 ? line[cursor].itemi : -1; } Value TreeCtrl::Get() const { return IsCursor() ? Get(GetCursor()) : Value(); } Value TreeCtrl::GetValue() const { return IsCursor() ? GetValue(GetCursor()) : Value(); } int TreeCtrl::Find(Value key) { for(int i = 0; i < item.GetCount(); i++) if(!item[i].free && Get(i) == key) return i; return -1; } bool TreeCtrl::FindSetCursor(Value key) { int q = Find(key); if(q < 0) return false; SetCursor(q); return true; } void TreeCtrl::UpdateSelect() { WhenSelection(); WhenSel(); WhenAction(); } bool TreeCtrl::IsSel(int id) const { return multiselect ? IsSelected(id) : GetCursor() == id; } bool TreeCtrl::IsSelDeep(int id) const { return IsSel(id) || id && IsSelDeep(GetParent(id)); } void TreeCtrl::SelectOne0(int id, bool sel, bool cb) { if(!multiselect) return; if(item[id].canselect && item[id].sel != sel) { item[id].sel = sel; selectcount += 2 * sel - 1; RefreshItem(id); if (cb) UpdateSelect(); } } void TreeCtrl::SelectOne(int id, bool sel) { SelectOne0(id, sel, true); } void TreeCtrl::ShiftSelect(int l1, int l2) { if(!multiselect) return; if(l1 > l2) UPP::Swap(l1, l2); for(int i = 0; i < line.GetCount(); i++) SelectOne0(line[i].itemi, i >= l1 && i <= l2, true); } void TreeCtrl::LeftDrag(Point p, dword keyflags) { if(p.y + sb.y > sb.GetTotal().cy) return; WhenDrag(); } void TreeCtrl::DoClick(Point p, dword flags, bool down) { Point org = sb; itemclickpos = Null; if(p.y + org.y > sb.GetTotal().cy) return; int i = FindLine(p.y + org.y); const Line& l = line[i]; int itemx = levelcx + l.level * levelcx - org.x; int x = itemx - (levelcx >> 1); itemclickpos.x = p.x - itemx; itemclickpos.y = p.y + org.y - l.y; if(p.x > x - 6 && p.x < x + 6) { if(down) Open(l.itemi, !IsOpen(l.itemi)); } else { if(down && IsSel(l.itemi)) { selclick = true; if(down) WhenLeftClick(); return; } SetWantFocus(); int q = cursor; SetCursorLine(i, true, false, true); if(multiselect) { int id = GetCursor(); if(flags & K_CTRL) { SelectOne(id, !IsSelected(id)); anchor = cursor; } else if(flags & K_SHIFT) ShiftSelect(anchor < 0 ? cursor : anchor, cursor); else { if(selectcount) SelClear(0); SelectOne(id); anchor = cursor; } } if(cursor != q) { WhenAction(); } if(down) WhenLeftClick(); Select(); } } void TreeCtrl::SyncInfo() { if(IsShutdown()) return; if((HasMouse() || info.HasMouse()) && popupex) { Size sz = GetSize(); Point p = GetMouseViewPos(); Point org = sb; // if(p.y + org.y > sb.GetTotal().cy) // return; int i = FindLine(p.y + org.y); if(i < line.GetCount()) { const Line& l = line[i]; const Item& m = item[l.itemi]; int x = levelcx + l.level * levelcx - org.x + m.image.GetSize().cx; Rect r = RectC(x, l.y - org.y, sz.cx - x, m.GetSize(display).cy); if(r.Contains(p)) { Color fg, bg; dword st; const Display *d = GetStyle(i, fg, bg, st); info.Set(this, r, m.value, d, fg, bg, st, m.margin); return; } } } info.Cancel(); } void TreeCtrl::MouseMove(Point p, dword) { if(mousemove && !IsReadOnly()) { Point org = sb; if(p.y + org.y < treesize.cy) { int i = FindLine(p.y + org.y); if(!IsNull(i)) SetCursorLine(i); } } SyncInfo(); } void TreeCtrl::LeftDown(Point p, dword flags) { DoClick(p, flags, true); } void TreeCtrl::LeftUp(Point p, dword keyflags) { if(selclick) { DoClick(p, keyflags, false); selclick = false; } } void TreeCtrl::LeftDouble(Point p, dword flags) { LeftDown(p, flags); Point org = sb; if(p.y + org.y > sb.GetTotal().cy) return; int i = FindLine(p.y + org.y); const Line& l = line[i]; int x = levelcx + l.level * levelcx - org.x - (levelcx >> 1) - org.x; if(i == GetCursorLine() && (flags & (K_SHIFT|K_CTRL)) == 0 && p.x > x + 6) WhenLeftDouble(); } void TreeCtrl::RightDown(Point p, dword flags) { Point org = sb; if(p.y + org.y < sb.GetTotal().cy) { int i = FindLine(p.y + org.y); if(i >= 0) { SetWantFocus(); SetCursorLine(i, true, !IsSel(line[i].itemi), true); } } if(WhenBar) MenuBar::Execute(WhenBar); } const Display *TreeCtrl::GetStyle(int i, Color& fg, Color& bg, dword& st) { const Line& l = line[i]; const Item& m = item[l.itemi]; st = 0; fg = SColorText; bg = SColorPaper; if(nobg) bg = Null; bool hasfocus = HasFocus() && !IsDragAndDropTarget(); bool drop = l.itemi == dropitem && dropinsert == 0; if(IsReadOnly()) st |= Display::READONLY; if(m.sel && multiselect) st |= Display::SELECT; if(i == cursor && !nocursor) st |= Display::CURSOR; if(drop) { hasfocus = true; st |= Display::CURSOR; } if(hasfocus) st |= Display::FOCUS; if((st & Display::SELECT) || !multiselect && (st & Display::CURSOR) && !nocursor || drop) { fg = hasfocus ? SColorHighlightText : SColorText; bg = hasfocus ? SColorHighlight : Blend(SColorDisabled, SColorPaper); } if(m.display) return m.display; return display; } void TreeCtrl::Paint(Draw& w) { SyncTree(); Size sz = GetSize(); Point org = sb; scroller.Set(org); if(!nobg) w.DrawRect(sz, SColorPaper); int levelcx2 = levelcx >> 1; for(int i = 0; i < line.GetCount(); i++) { Line& l = line[i]; if(l.ll >= 0) { int yl = line[i].y + item[l.itemi].GetSize(display).cy - org.y; int yh = line[l.ll].y + item[line[l.ll].itemi].GetSize(display).cy / 2 - org.y; if(yh >= 0 && yl < sz.cy) { int x = levelcx + levelcx * l.level + levelcx2 - org.x; w.DrawRect(x, yl, 1, yh - yl, SColorShadow); } } } Rect dri; for(int i = FindLine(org.y); i < line.GetCount(); i++) { Line& l = line[i]; const Item& m = item[l.itemi]; Size msz = m.GetSize(display); Size isz = m.image.GetSize(); Size vsz = m.GetValueSize(display); int y = l.y - org.y; if(y > sz.cy) break; int x = 0; x = levelcx + l.level * levelcx - org.x; Point op = Point(x - levelcx2, y + msz.cy / 2); Rect fr = RectC(x, y, vsz.cx + 2 * m.margin + isz.cx, msz.cy); if(l.itemi == dropitem) { dri = fr; if(i == 0) dri.top++; } if(w.IsPainting(0, y, sz.cx, msz.cy)) { w.DrawRect(op.x, op.y, levelcx2, 1, SColorShadow); if(m.canopen || m.child.GetCount()) { Image im = m.isopen ? CtrlImg::treeminus() : CtrlImg::treeplus(); op -= im.GetSize() / 2; w.DrawImage(op.x, op.y, im); } w.DrawImage(x, y + (msz.cy - isz.cy) / 2, m.image); x += isz.cx; Color fg, bg; dword st; Size csz = m.GetCtrlSize(); if(m.ctrl && !highlight_ctrl) // 2008-04-08 mrjt x += csz.cx; if(x < sz.cx) { const Display *d = GetStyle(i, fg, bg, st); if(!IsNull(m.value) || m.ctrl && highlight_ctrl) { int ctx = highlight_ctrl * csz.cx; w.DrawRect(x, y, vsz.cx + 2 * m.margin + ctx, msz.cy, bg); Rect r = RectC(x + ctx + m.margin, y + (msz.cy - vsz.cy) / 2, vsz.cx, vsz.cy); w.Clip(r); d->Paint(w, r, m.value, fg, bg, st); w.End(); } if(i == cursor && !nocursor && multiselect && GetSelectCount() != 1 && HasFocus() && !IsDragAndDropTarget()) DrawFocus(w, fr, st & Display::SELECT ? SColorPaper() : SColorText()); } } } if(dropitem >= 0 && dropinsert) DrawHorzDrop(w, dri.left - 2, dropinsert < 0 ? dri.top : dri.bottom - 1, sz.cx - dri.left + 2); } Image TreeCtrl::GetDragSample() { SyncTree(); Size sz = StdSampleSize(); ImageDraw iw(StdSampleSize()); iw.DrawRect(sz, SColorPaper); int y = 0; for(int i = 0; i < line.GetCount(); i++) { Line& l = line[i]; const Item& m = item[l.itemi]; const Display *d = m.display; if(!d) d = display; Size msz = m.GetSize(display); Size isz = m.image.GetSize(); Size vsz = m.GetValueSize(display); Rect r = RectC(0, y, vsz.cx + 2 * m.margin, msz.cy); int x = 0; if(IsSel(l.itemi)) { iw.DrawImage(x, y + (msz.cy - isz.cy) / 2, m.image); x += isz.cx; if(!(m.ctrl && m.ctrl->IsWantFocus())) { d->Paint(iw, RectC(x + m.margin, y + (msz.cy - vsz.cy) / 2, vsz.cx, vsz.cy), m.value, SColorHighlightText, SColorHighlight, Display::SELECT|Display::FOCUS); } y += msz.cy; } } return ColorMask(iw, SColorPaper); } void TreeCtrl::Scroll() { SyncTree(); if(hasctrls) { Refresh(); SyncCtrls(false, NULL); } else scroller.Scroll(*this, sb); } void TreeCtrl::MouseWheel(Point, int zdelta, dword) { sb.WheelY(zdelta); } void TreeCtrl::ChildGotFocus() { if(chldlck) return; for(int i = 0; i < line.GetCount(); i++) { Item& m = item[line[i].itemi]; if(m.ctrl && m.ctrl->HasFocusDeep()) { SetCursorLine(i); return; } } } bool TreeCtrl::Tab(int d) { if(cursor < 0) return false; Item& m = item[line[cursor].itemi]; if(m.ctrl && m.ctrl->HasFocusDeep()) return false; for(int i = cursor + d; i >= 0 && i < line.GetCount(); i += d) { Item& m = item[line[i].itemi]; if(m.ctrl && m.ctrl->SetWantFocus()) return true; } return false; } bool TreeCtrl::Key(dword key, int) { SyncTree(); if(Bar::Scan(WhenBar, key)) return true; Size sz = GetSize(); int cid = GetCursor(); bool shift = key & K_SHIFT; key &= ~K_SHIFT; switch(key) { case K_ENTER: Select(); break; case K_TAB: return Tab(1); case K_SHIFT_TAB: return Tab(-1); case K_UP: MoveCursorLine(cursor - 1, -1); break; case K_DOWN: MoveCursorLine(cursor + 1, 1); break; case K_PAGEDOWN: MoveCursorLine(cursor >= 0 ? FindLine(line[cursor].y + sz.cy) : 0, 1); break; case K_PAGEUP: MoveCursorLine(cursor >= 0 ? FindLine(line[cursor].y - sz.cy) : line.GetCount() - 1, -1); break; case K_LEFT: if(cid >= 0) Close(cid); break; case K_RIGHT: if(cid >= 0) Open(cid); break; default: { if(accel && key >= ' ' && key < 65536) { int ascii_line = -1; int upper_line = -1; int exact_line = -1; int l = GetCursorLine(); for(;;) { if(++l >= GetLineCount()) l = 0; if(l == GetCursorLine()) break; Value v = GetValue(line[l].itemi); WString w; if(IsString(v)) w = v; else w = StdFormat(v).ToWString(); word first = w[0]; if(ascii_line < 0 && ToAscii(first) == ToAscii((word)key)) ascii_line = l; if(upper_line < 0 && ToUpper(first) == ToUpper((word)key)) upper_line = l; if(exact_line < 0 && first == key) exact_line = l; } int ln = (exact_line >= 0 ? exact_line : upper_line >= 0 ? upper_line : ascii_line); if(ln < 0) BeepExclamation(); else { int id = GetItemAtLine(ln); Open(id); SetCursor(id); } return true; } return false; } } if(GetCursor() != cid) { if(multiselect && cursor >= 0) { if(shift) { if(anchor < 0) anchor = cursor; ShiftSelect(anchor, cursor); } else { ClearSelection(); SelectOne(GetCursor()); anchor = cursor; } } WhenAction(); } return true; } void TreeCtrl::GotFocus() { if(dirty) return; if(hasctrls && cursor >= 0 && item[line[cursor].itemi].ctrl) for(int i = 0; i < line.GetCount(); i++) { Item& m = item[line[i].itemi]; if(m.ctrl && m.ctrl->SetWantFocus()) break; } RefreshLine(cursor); if(GetSelectCount() > 1) Refresh(); SyncInfo(); } void TreeCtrl::LostFocus() { if(dirty) return; RefreshLine(cursor); if(GetSelectCount() > 1) Refresh(); SyncInfo(); } void TreeCtrl::ChildRemoved(Ctrl *) { if(!chldlck) Dirty(); } struct TreeCtrl::SortOrder { TreeCtrl *tree; const ValueOrder *order; const ValuePairOrder *pairorder; bool byvalue; bool operator()(int a, int b) const { return pairorder ? (*pairorder)(tree->Get(a), tree->GetValue(a), tree->Get(b), tree->GetValue(b)) : byvalue ? (*order)(tree->GetValue(a), tree->GetValue(b)) : (*order)(tree->Get(a), tree->Get(b)); } SortOrder() { pairorder = NULL; } }; void TreeCtrl::Sort0(int id, const ValueOrder& order, bool byvalue) { SortOrder so; so.tree = this; so.order = ℴ so.byvalue = byvalue; UPP::Sort(item[id].child, so); } void TreeCtrl::Sort(int id, const ValueOrder& order, bool byvalue) { SyncTree(); Sort0(id, order, byvalue); Dirty(id); } void TreeCtrl::SortDeep0(int id, const ValueOrder& order, bool byvalue) { Sort0(id, order, byvalue); Item& m = item[id]; for(int i = 0; i < m.child.GetCount(); i++) SortDeep0(m.child[i], order, byvalue); } void TreeCtrl::SortDeep(int id, const ValueOrder& order, bool byvalue) { SyncTree(); SortDeep0(id, order, byvalue); Dirty(id); } void TreeCtrl::Sort(int id, int (*compare)(const Value& v1, const Value& v2), bool byvalue) { Sort(id, FnValueOrder(compare), byvalue); } void TreeCtrl::SortDeep(int id, int (*compare)(const Value& v1, const Value& v2), bool byvalue) { SortDeep(id, FnValueOrder(compare), byvalue); } void TreeCtrl::SortByValue(int id, const ValueOrder& order) { Sort(id, order, true); } void TreeCtrl::SortDeepByValue(int id, const ValueOrder& order) { SortDeep(id, order, true); } void TreeCtrl::SortByValue(int id, int (*compare)(const Value& v1, const Value& v2)) { Sort(id, compare, true); } void TreeCtrl::SortDeepByValue(int id, int (*compare)(const Value& v1, const Value& v2)) { SortDeep(id, compare, true); } void TreeCtrl::Sort0(int id, const ValuePairOrder& order) { SortOrder so; so.tree = this; so.pairorder = ℴ UPP::Sort(item[id].child, so); } void TreeCtrl::Sort(int id, const ValuePairOrder& order) { SyncTree(); Sort0(id, order); Dirty(id); } void TreeCtrl::SortDeep0(int id, const ValuePairOrder& order) { Sort0(id, order); Item& m = item[id]; for(int i = 0; i < m.child.GetCount(); i++) SortDeep0(m.child[i], order); } void TreeCtrl::SortDeep(int id, const ValuePairOrder& order) { SyncTree(); SortDeep0(id, order); Dirty(id); } void TreeCtrl::Sort(int id, int (*compare)(const Value& k1, const Value& v1, const Value& k2, const Value& v2)) { SortDeep(id, FnValuePairOrder(compare)); } void TreeCtrl::SortDeep(int id, int (*compare)(const Value& k1, const Value& v1, const Value& k2, const Value& v2)) { SortDeep(id, FnValuePairOrder(compare)); } void TreeCtrl::SetData(const Value& data) { FindSetCursor(data); } Value TreeCtrl::GetData() const { return Get(); } Point TreeCtrl::GetScroll() const { return sb; } void TreeCtrl::ScrollTo(Point sc) { sb = sc; } void TreeCtrl::SelClear(int id) { Item& m = item[id]; if(m.sel) { m.sel = false; RefreshItem(id); selectcount--; } for(int i = 0; i < m.child.GetCount(); i++) SelClear(m.child[i]); } void TreeCtrl::GatherSel(int id, Vector& sel) const { if(IsSel(id)) sel.Add(id); const Item& m = item[id]; for(int i = 0; i < m.child.GetCount(); i++) GatherSel(m.child[i], sel); } Vector TreeCtrl::GetSel() const { Vector v; GatherSel(0, v); return v; } void TreeCtrl::RefreshSel() { Size sz = GetSize(); for(int i = FindLine(sb.GetY()); i < line.GetCount(); i++) { Line& l = line[i]; int y = l.y - sb.GetY(); if(y > sz.cy) break; if(IsSel(l.itemi)) RefreshItem(l.itemi); } } void TreeCtrl::ClearSelection() { if(selectcount) { SelClear(0); WhenSelection(); WhenSel(); WhenAction(); selectcount = 0; } } void TreeCtrl::SetOption(int id) { } void TreeCtrl::Select() { } void TreeCtrl::DnD(int itemid, int insert) { if(itemid != dropitem || insert != dropinsert) { RefreshItem(dropitem, 4); dropitem = itemid; dropinsert = insert; RefreshItem(dropitem, 4); } } bool TreeCtrl::DnDInserti(int ii, PasteClip& d, bool bottom) { if(ii >= 0 && ii < line.GetCount()) { int itemi = line[ii].itemi; int parent = GetParent(itemi); int childi = GetChildIndex(parent, itemi); int ins = -1; if(bottom) if(childi != GetChildCount(parent) - 1 && ii + 1 < line.GetCount()) return DnDInserti(ii + 1, d, false); else { childi++; ins = 1; } WhenDropInsert(parent, childi, d); if(d.IsAccepted()) { DnD(itemi, ins); return true; } } return false; } void TreeCtrl::DragEnter() { RefreshSel(); } void TreeCtrl::DragAndDrop(Point p, PasteClip& d) { int py = p.y + sb.GetY(); if(py < sb.GetTotal().cy) { int ii = FindLine(py); const Line& l = line[ii]; if(l.itemi && WhenDropInsert) { int y = l.y; int cy = item[l.itemi].GetSize(display).cy; if(py < y + cy / 4 && DnDInserti(ii, d, false)) return; if(py >= y + 3 * cy / 4 && DnDInserti(ii, d, true)) return; } WhenDropItem(l.itemi, d); if(d.IsAccepted()) { DnD(l.itemi, 0); return; } WhenDropInsert(l.itemi, GetChildCount(l.itemi), d); if(d.IsAccepted()) { DnD(l.itemi, 0); return; } } WhenDrop(d); DnD(-1, 0); } void TreeCtrl::DragRepeat(Point p) { if(IsReadOnly()) return; sb = sb + GetDragScroll(this, p, 16); p.y += sb.y; if(p == repoint) { if(GetTimeClick() - retime > 1000 && p.y < sb.GetTotal().cy) { int ii = FindLine(p.y); const Line& l = line[ii]; int y = l.y; int cy = item[l.itemi].GetSize(display).cy; if(p.y >= y + cy / 4 && p.y < y + 3 * cy / 4 && !IsOpen(l.itemi) && GetChildCount(l.itemi)) { Open(l.itemi, true); retime = GetTimeClick(); } } } else { retime = GetTimeClick(); repoint = p; } } void TreeCtrl::DragLeave() { DnD(-1, 0); RefreshSel(); repoint = Null; } void TreeCtrl::AdjustAction(int parent, PasteClip& d) { if(IsDragAndDropSource() && IsSelDeep(parent) && d.GetAction() == DND_MOVE) d.SetAction(DND_COPY); } Vector TreeCtrl::InsertDrop(int parent, int ii, const TreeCtrl& src, PasteClip& d) { TreeCtrl copy; Vector sel = src.GetSel(); for(int i = 0; i < sel.GetCount(); i++) Copy(copy, 0, i, src, sel[i]); Vector did; for(int i = 0; i < copy.GetChildCount(0); i++) { did.Add(Copy(*this, parent, ii + i, copy, copy.GetChild(0, i))); SetCursor(did.Top()); } if(&src == this && d.GetAction() == DND_MOVE) { Remove(sel); d.SetAction(DND_COPY); } for(int i = 0; i < did.GetCount(); i++) SelectOne(did[i], true); return did; } Vector TreeCtrl::InsertDrop(int parent, int ii, PasteClip& d) { return InsertDrop(parent, ii, GetInternal(d), d); } void TreeCtrl::Swap(int id1, int id2) { SyncTree(); Item& i1 = item[id1]; Item& i2 = item[id2]; for(int i = 0; i < i1.child.GetCount(); i++) item[i1.child[i]].parent = id2; for(int i = 0; i < i2.child.GetCount(); i++) item[i2.child[i]].parent = id1; item.Swap(id1, id2); Dirty(id1); Dirty(id2); } void TreeCtrl::SwapChildren(int parentid, int i1, int i2) { SyncTree(); Item& parent = item[parentid]; UPP::Swap(parent.child[i1], parent.child[i2]); Dirty(parentid); } void OptionTree::SetRoot(const Image& img, Option& opt, const char *text) { if(text) opt.SetLabel(text); TreeCtrl::SetRoot(img, opt); opt.NoNotNull().BlackEdge(); option.At(0) = &opt; opt <<= THISBACK1(SetOption, 0); } void OptionTree::Clear() { TreeCtrl::Clear(); aux.Clear(); option.Clear(); aux.Add(); } void OptionTree::SetRoot(Option& option, const char *text) { SetRoot(Null, option, text); } void OptionTree::SetRoot(const Image& img, const char *text) { SetRoot(img, aux[0], text); } void OptionTree::SetRoot(const char *text) { SetRoot(Null, aux[0], text); } int OptionTree::Add(int parentid, const Image& img, Option& opt, const char *text) { return Insert(parentid, GetChildCount(parentid), img, opt, text); } int OptionTree::Insert(int parentid, int i, const Image& img, Option& opt, const char *text) { if(text) opt.SetLabel(text); int id = TreeCtrl::Insert(parentid, i, img, opt); option.At(id, NULL) = &opt; opt.NoNotNull().BlackEdge(); opt <<= THISBACK1(SetOption, id); SetOption(id); return id; } int OptionTree::Insert(int parentid, int i, const Image& img, const char *text) { return Insert(parentid, i, img, aux.Add().NoNotNull().Set(Get(parentid)), text); } int OptionTree::Insert(int parentid, int i, const char *text) { return Insert(parentid, i, aux.Add().NoNotNull().Set(Get(parentid)), text); } int OptionTree::Add(int parentid, const Image& img, const char *text) { return Insert(parentid, GetChildCount(parentid), img, text); } int OptionTree::Add(int parentid, Option& opt, const char *text) { return Insert(parentid, GetChildCount(parentid), opt, text); } int OptionTree::Insert(int parentid, int i, Option& opt, const char *text) { return Insert(parentid, i, Null, opt.NoNotNull(), text); } int OptionTree::Add(int parentid, const char *text) { return Insert(parentid, GetChildCount(parentid), text); } void OptionTree::SetLabel(int id, const char *text) { Node n = GetNode(id); Option *o = dynamic_cast